Isomorphic import of CommonJS modules - node.js

I've written a small library that's meant to run both in the browser and on the server. The library in turn uses npm libraries published as CommonJS modules (e.g. lodash.isequal).
I'd like to compile the library two different ways:
Using tsc, generating commonjs modules for use on the server
Through webpack, using the awesome-typescript-loader, generating es6 modules for webpack to bundle
Per the docs, the correct way of importing CommonJS libraries which override module.exports is as follows:
import isEqual = require('lodash.isequal');
However for the second compilation case, typescript complains that
error TS1202: Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
I've managed to get code generation to work properly by importing it as:
import * as isEqual from 'lodash.isequal';
but that requires disabling typechecks, since otherwise typescript complains with errors like the following:
error TS2497: Module '"/home/user/ts-mod-test/node_modules/#types/lodash.isequal/index"' resolves to a non-module entity and cannot be imported using this construct.

Since TypeScript 2.7, this issue can be resolved by using synthetic default imports. Using the esModuleInterop flag, isEqual (and similar modules) can be imported like in babel and webpack:
import isEqual from 'lodash.isequal';
For webpack with the module option set to es6, the import will be left as-is, and webpack itself will handle the synthetic default. For node, with module set to CommonJS, the corresponding emit will be const lodash_isequal_1 = __importDefault(require("lodash.isequal"));, where the __importDefault handles the non-es module case like Babel

Related

Convert old CJS module into ESM and import into TS files

I am wanting to convert an old module, https://github.com/capaj/object-resolve-path, into ESM so I can use it via an import statement, in order to move all my NodeJS Lambda functions to ESM.
I have forked the repo, and changed the 2 main .js files to .mjs, updated the exports, as well as update the main property in package.json to point to the object-resolve-path.mjs file.
In my NodeJS Lambda function, I have then installed the fork via NPM from my private repo, which pulls the new code in.
However, when I try to import the package in my code now, using import * as resolvePath from 'object-resolve-path'; I get an error:
Could not find a declaration file for module 'object-resolve-path'.
What am I missing? The module isn't written in TS, so why is it asking for a declaration file?

How to require a node package that wants to be a module, without type: module

I need to import a module to my project.
When I do const gifsicle = require('gifsicle'); it tells me:
ReferenceError: require is not defined in ES module scope, you can use import instead
When I try to do import, it says
SyntaxError: Cannot use import statement outside a module
Other threads say to fix this I have to add "type": "module" to my package.json
But doing so gives me this error:
ReferenceError: require is not defined in ES module scope, you can use import instead
How do I load in an ES module package into my current non-module package which uses require?
First of all add "type":"commonjs" to package.json.
Downgrade your package version. If you are using let suppose version 8.0.0 then install older version like npm i package-name#7.1.0.
That's all now enjoy coding...

How can I dynamically import both commonJS and ES6 modules from the same node CLI?

I have looked through many similar questions on SO, and haven't found a clear answer.
I'm writing an npm CLI tool which is supposed to dynamically import a specified module and run some operations on that module's exports.
In my npm package, I have specified "type": "module" and my binary is bin/generator.mjs.
The way I'm trying to use it is: ❯ ./bin/generator.mjs path/to/test-module.js [other args] - and then in the generator I do:
const modulePath = "file://" + path.resolve(inputFilename);
const objs = await import(modulePath);
With this configuration, I can specify an ES6 module on the command line, and the generator works fine, however, if I specify a CommonJS module, I get:
const Library = require('some-library');
^
ReferenceError: require is not defined
Here are my two test files:
ES6 module
import Library from 'some-library';
export const person = { firstName: "Bob", lastName: "Mortimer };
CommonJS module
const Library = require('some-library');
const person = { firstName: "Bob", lastName: "Mortimer };
module.exports.person = person;
So then I tried having two different binaries - generator for ES6 modules, and generator-cjs for CommonJS ones - I modified the original to use requires in place of import, however, the bin/generator.mjs continues to work fine, but the bin/generator.cjs throws this error:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: [...]/test-module.js
require() of ES modules is not supported.
require() of [...]/test-module.js from [...]/bin/generator.cjs is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename test-module.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from [...]/package.json.
If I remove "type": "module" from my package.json, both generator and generator-cjs work on CommonJS files, but generator throws this error on ES6 modules:
throw new Error('Unexpected import statement in CJS module.');
^
Error: Unexpected import statement in CJS module.
Ideally: I want to have one binary that can import both CommonJS and ES6 modules dynamically.
If you have read through this far - thank you.
Do you have any ideas on how I can resolve this? Let me know if you want me to try things to provide more information.
Thanks,
Edit: I should mention that converting the CommonJS modules to ES6 is not an option as there are nearly a hundred CJS ones, and they have complicated dependency chains that will take time to unravel.

Babel ES6: Import node modules that need to be transpiled in ES6 as well

I am importing a node module correctly but run into an issue where the node module I am importing is written in es6 and babel is unable to transpile it.
In base class header:
import foo from 'bar/lib/foo';
in foo.js in node modules:
import Debug from 'debug';
export default class foo from bar {
...
...
...
}
Error Message:
import Debug from 'debug';
^^^^^^
SyntaxError: Unexpected token import
As you can see it is able to find the file foo.js but it is not running the node module in es6. How can I have Babel transpile both the base code as well as the node module that it is trying to import?
Do you have a file .babelrc with this content?
{
"presets": ["es2015"],
"plugins": []
}
You can check an example here: https://github.com/Talento90/ima-up
The node module should be compiled separately from your source code. If you are using an external lib, they should have a directory with the transpiled code. If you are writing your own, you need to use npm link and compile it separately.
If the node module already has a transpiled directory (like dist), you could try importing the transpiled code into the node module:
import foo from 'bar/dist/foo';

Typescript import ts file from node module

Maybe it's a duplicate but I've searched for an hour and haven't found the answer.
I have a node module named a-module which contains some .ts files (for example a.ts)
I have another node module b-module which has a-module among its dependencies.
I want to import some .ts file from a-module to b-module.
In some file within b-module I write:
import a = require('a-module/a');
console.log(a);
When then I'm trying to compile b-module with tsc, is says
Cannot find external module 'a-module/a'.
What am I doing wrong?
P.S. I have ArcticTypescript plugin for SublimeText, and seems that it is enough intelligent to find a-module/a. Why then tsc doesn't manage to locate my file?
P.P.S My file structure looks like that
b-module/
node_modules/
a-module/
a.ts
b.ts
I'm trying to import a.ts to b.ts.
import a = require('a-module/a');
You need to either use relative paths i.e. ../a-module/a or declare it for TypeScript explicitly i.e. declare module "a-module/a".

Resources