Use import inside a nodejs usually using require - javascript

I have a NodeJS app with a lot of routes, functions, etc. I have a lot of files with const xxx = require('yyy').
I now want to use this: https://www.npmjs.com/package/simple-ldap-search
I thought my journey would be as peaceful as it has been for now, but I can't use it, let me explain: instead of the usual require, I have to use import, as described in the documentation:
import SimpleLDAP from 'simple-ldap-search';
I searched on StackOverflow, and I saw I could put "type": "module" in the top package.json file, but then if I do I can't use require... It seems unsolvable.
Does it mean I have to choose between using ES6 or CommonJS? I read this question: Using Node.js require vs. ES6 import/export
I have the impression the import is more efficient, am I right?
Is it possible to use simple-ldap-search in my current node app without making big changes?
Is it possible to "convert" (I know it's not the precise term but I think you'll understand) the use of require to import and vice-versa?
Thanks for your answers.

As this project issue states, it isn't possible to use require in a project that switched to type: "module" in its package.json. For these use cases one would need to use a loader like Babel, etc.
It is worth considering switching over to ES modules, i.e to add type: "module" to your top-level package.json. It is still possible to import a CommonJS module with import but it does not work the other way around.
Alternatively, you can switch back to a 2.x version of that package, from what I see they made the switch to ES modules from 3.0.0 onwards.

While you can't use import in a commonjs file, you can use import()
This will make it more annoying when having to deal with the async loading, but is probably easier than converting your entire project to ESModules.
const simpleLDAPLoader = import('simple-ldap-search');
// later
async function () {
const {default: SimpleLDAP} = await simpleLDAPLoader;
const simpleLDAP = new SimpleLDAP();
}
Adn this will use the cached version every time you await the exisiting promise
Source: https://nodejs.org/api/esm.html#esm_interoperability_with_commonjs
Using require to load an ES module is not supported because ES modules have asynchronous execution. Instead, use import() to load an ES module from a CommonJS module.

I am not sure if it is going to help you in your case but I have found this solution for myself and I can use ES6 import and "require" at the same time.
In package.json:
"type": "module"
And in your file:
import { createRequire } from "module";
const require = createRequire(import.meta.url);
Works for my projects!

Related

What is the difference between javascript require("file") vs require("file.js")?

I have two files.
import.js
export.js
In import I do:
const export = require('export')
This will successful import the module from export.js. When I do require('export.js') (with file ending) it is also working.
So I was wondering what is the difference there? Is without slower?
Node.js automatically resolves some extensions. For example: .js, .json, etc. No, it works the same with or without the extension.
require in Node.js takes one argument which can be either module name or path. You can read for more info.
The difference between import/export and require is that require uses commonjs under the hood. While the import was introduced in ES6.
Even if you use ES6 import it's still transpiled into thecommonjs. But using ES6 is recommended because with the time, it will be widely supported.
Performance-wise the difference will be negligible because the module files are evaluated only once but you will have to benchmark it, if you are very keen on the performance.

How do you convert "top-level await of dynamic import" to "synchronous require" in a Typescript-based project targeting both ESM and CJS?

I'm working on creating a small Node.js library, with source written in TS, and (ideally) the generated JS distributed in both ESM and CJS formats.
It has a peer dependency (currently as CJS) where I need to detect if it's installed, and if not provide a fallback before continuing. Because of this, I can't statically import it, and seem to need to use dynamic import in the ESM distribution and require in the CJS distribution to detect that instead.
To avoid the dynamic import alone forcing me to export a Promise onto consumers, I was thinking of using top-level await in the ESM distribution (since it's been supported unflagged since Node 14.8.0 and the use case here is similar to this one from the tc39 proposal). So the generated JS would look like
ESM:
try {
peerExport = await import("the-cjs-peer-dependency") //Notice this isn't inside of an async block
}
CJS:
try {
peerExport = require("the-cjs-peer-dependency")
}
Right now I have 2 tsconfig.json's, one for ESM and one for CJS, and my source TS looks exactly like the ESM version of the generated JS.
What I'm struggling with is how to replace the await import's with require's in the CJS build process, as TS immediately gives a hard-stop that CJS doesn't support top-level await and won't continue. I haven't found anything so far in Typescript's settings either that would enable this substitution, only this related Github issue for a reversed problem of wanting to keep dynamic imports of ESM modules from CJS ones as-is.
My current plan is to add pre-/post-build steps to scan my TS source and do a find-and-replace, then run the tsc, then revert the source, though this doesn't seem great. Any ideas on this specific problem or the larger one of trying to support ESM/CJS out of a single TS source codebase would be appreciated.
Thanks

How to prevent typescript from transpiling dynamic imports into require()?

I'm building a discord.js Discord bot. Now for some reason, discord.js doesn't work with ESM modules (a totally separate issue), so my bot app uses CommonJS modules. Now I have another project on my system called Lib, which has a lot of utility functions that I plan to use in several different projects so I don't have to rewrite them. This Lib project uses ESM modules. Since I have to import Lib from DiscordBot, I use the dynamic import syntax in typescript. Now, whenever I transpile my DiscordBot project, the dynamic imports get converted into some ugly javascript module code, and that ugly module code ultimately ends up using require(). Since require() can't import ESM modules, my bot ends up crashing.
I tried however to stop my ts compiler, copy the code from my ts file that imports Lib then pasting that code into the corresponding JS file manually (and removing TS-exclusive features like type annotations and interfaces). Then I ran my bot app, and it worked perfectly fine. But I don't want to have to do this every time. So it's tsc's compiling that's the problem. How do I fix this?
So I understand the purpose is:
Develop the code in TypeScript
Run the compiled code in CommonJS package
Import and use an ES Module
Option 1:
If "module" in tsconfig.json is set to "commonjs", currently there's no way to prevent TypeScript from transpiling dynamic import() into require() - except that you hide the code in a string and use eval to execute it. Like this:
async function body (pMap:any){
// do something with module pMap here
}
eval ("import('p-map').then(body)");
No way TypeScript transpiles a string!
Option 2
Set "module" in tsconfig.json to "es2020". By doing this, dynamic import would not be transpiled into require(), and you can use dynamic import to import a CommonJS or ES Module. Or, you can use the const someModule = require("someModule") syntax to import a CommonJS module (would not be transpiled to ES6 import syntax). You cannot use the ES6 import syntax such as import * as someModule from "someModule" or import someModule from "someModule". These syntaxes will emit ES Module syntax imports ("module" is set to "es2020") and cannot be run in CommonJS package.
Below is a bit information:
If "module" is set to "es2020": dynamic import import() is not transpiled.
If "module" is set to `"es2015": there's an error:
TS1323: Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'esnext', 'commonjs', 'amd', 'system', or 'umd'.
If "module" is set to "commonjs": dynamic imports are transpiled.
Quote tsconfig.json reference for "module" field:
If you are wondering about the difference between ES2015 and ES2020,
ES2020 adds support for dynamic imports, and import.meta.
This is currently not possible. There is a very new issue at GitHub (https://github.com/microsoft/TypeScript/issues/43329), but that is not implemented yet. So everything you can do now is to switch from ESM to CommonJS with your Lib project.
Update 2022
The issue has been closed and there is now a new option for "module" called node12. That should fix the problem
The node12 setting others are talking about did not work for me, but these compilerOptions did, using Typescript 4.7.2:
"module": "CommonJS",
"moduleResolution": "Node16",
This saved my backside, I did not have to migrate all import requires to imports to be able to use an ESM npm lib.
Typescript input source:
import Redis = require('redis');
import * as _ from 'lodash';
export async function main() {
const fileType = await import('file-type');
console.log(fileType, _.get, Redis);
}
CommonJS output:
...
const Redis = require("redis");
const _ = __importStar(require("lodash"));
async function main() {
const fileType = await import('file-type');
console.log(fileType, _.get, Redis);
}
exports.main = main;
What compiler/bundler are you using? I am assuming tsc based on context.
I recommend using esbuild to compile and bundle your TS. You can also use it simply to transform it after using tsc. It has an option called "format" that can remove any module-style imports. See https://esbuild.github.io/api/#format.
Here is a simple example of using.
build.js
const esbuild = require("esbuild");
esbuild.build({
allowOverwrite: true,
write: true,
entryPoints: ["my-main-file.ts"],
outfile: "some-file.bundle.js",
format: "cjs", //format option set to cjs makes all imports common-js style
bundle: true,
}).then(() => {
console.log("Done!");
});
You can then add something like this to your package.json
"scripts": {
"build": "node build.js",
...rest of scripts
Here is an additional link about some caveats using esbuild with typescript. None of these should really be a problem for you. https://esbuild.github.io/content-types/#typescript-caveats
This has been fixed with the addition of the node12 option for the module setting. From the docs:
Available in nightly builds, the experimental node12 and nodenext modes integrate with Node’s native ECMAScript Module support. The emitted JavaScript uses either CommonJS or ES2020 output depending on the file extension and the value of the type setting in the nearest package.json. Module resolution also works differently. You can learn more in the handbook.
If you use this setting without a nightly build, however, it currently produces the following error:
error TS4124: Compiler option 'module' of value 'node12' is unstable. Use nightly TypeScript to silence this error. Try updating with 'npm install -D typescript#next'.
I'm using a variant of the already mentioned eval-based hack to overcome this issue.
So for example, parse-domain is distributed as an ESM module, so importing it like this breaks in a CJS-based node app:
import { fromUrl, parseDomain } from 'parse-domain';
const parseDomainFromUrl = (url: string) => {
return parseDomain(fromUrl(url));
}
And this is how I have managed to get it working:
const dynamicImport = new Function('specifier', 'return import(specifier)');
const parseDomainFromUrl = (url: string) => {
return dynamicImport('parse-domain').then((module: any) => {
const { fromUrl, parseDomain } = module;
return parseDomain(fromUrl(url));
})
};
(Note that parseDomainFromUrl became asynchronous in the process, so it would need to be awaited by the caller.)

Import JSON file in NodeJs using es6

Hi i'm currently learning nodejs and I try to import a json file like this :
'use strict'
import data from 'users.json'
console.log(data)
Each time I get this error "Cannot find package 'users.json'"
But if I read the file with fs it worked so can you explain me how to do please
try :
import data from './users'
which is the es6 method of doing things or if you want an older syntax method you can try
const data = require('./users')
Okay so the explanation just is this, those fullstops and forward slash are used to indicate relative paths to the file the import is being called from, which means somethings you'd see something like for example ../file/help. The ./ means go up one directory ../ means go up two directories and so on. The name after the slash just tells your program the folder to go in to look for the file to import from so ./folder means go up one directory and enter into the folder directory and so on.
Almost forgot to mention, you should not import from a json file or a text file or any other file that does not have a .js extention(of which you do not actually have to specify) unless absolutely neccessary. To deal with files in node you have to use the fs library.
const fs =require('fs')
....
fs.readFile('./users.json','utf-8',(err,jsonString)=>{
const data = JSON.parse(jsonString);
//whatever other code you may fanacy
}
take a look at the fs module for a list of awesome features you can use to play with files
While the selected answer is correct, here's an explanation that I hope might add some clarity:
import MyModule from 'some-module'
is ES6 import syntax, as opposed to the older CommonJS syntax, which uses require.
CommonJS's require can be used to import files generally; ES6's import statement is more specific, and is generally used for importing js files which meet the criteria for being a module (having an export statement), or specific assets such as CSS sheets. Unlike require, it won't generally work for reading in files that are not modules.
You can't mix CommonJS require and ES6 import in the same file (at least not easily), so if you're using ES6 import and wish to read a file, do so with fs.readFileSync or a similar method. If the file is a json string, you'll need to parse it with JSON.parse().
If you want to use ES6 (NOT CommonJS) module system but want to use the Node.js version which is less than V18 (supports direct json import) then use the below approach.
import { createRequire } from "module";
const require = createRequire(import.meta.url); // construct the require method
const data = require("users.json"); // Now you can use require method in ES6
console.log(data)

Use "import" with Node library, rather than "require"?

Newbie Node question here.
I'm trying to use the Google Cloud Node client with an existing application (not written by me) that bundles its code with rollup.
I've tried importing the library with require, as per its documentation, as follows:
import REGL from "regl/dist/regl";
import Camera from "./lib/camera";
...
var gcloud = require('google-cloud');
But my application complains (CLARIFICATION: it only starts producing this error when I add the require statement, otherwise the imports work fine):
'import' and 'export' may only appear at the top level
So maybe I need to use import gcloud instead of require, but how? I tried looking at the code in node_modules and doing this instead:
import gcloud from "google-cloud/src/index";
But now I get a bunch of other errors
🚨 Unexpected token
node_modules/google-cloud/node_modules/ent/reversed.json (2:7)
1: {
2: "9": "Tab;",
^
How can I use import instead of require, or alternatively, how can I make require play nicely with import?
import is ES6 syntax. You either must use an experimental flag with nodejs or use babel to compile your js to be ES6 compatible.
EDIT:
Since the problem is with require and not import, i'm updating my answer.
I'm not sure what you're setup is but it's because, i'm guessing, google-cloud isn't written in es6. So you'll have to see if there's an es6 version in the src. If there is you could try (I doubt this will work)
Try:
import * as gcloud from 'google-cloud'
if that doesn't work - try a shimming module like riveted. You'll need webpack to compile this. Since you're using rollup.js, which i'm unfamiliar with you'll need a es5 to es6 compiler for this.

Categories

Resources