The correct way is to continue using the old import syntax. The new import syntax is for ES modules only, the old import syntax is for pre-ES6 modules. The two are distinct, intentionally so. import * as foo from 'foo' imports all the properties of the module 'foo', it does not import the default value as foo.
From the designer of the feature:
- An export default declaration always declares an exported member named default and is always emitted as an assignment to exports.default. In other words,
export defaultconsistently has ES module semantics. For compatibility with Babel we could optionally emit an__esModulemarker when a module has a default export, but we wouldn't actually use that marker for anything. - An
export =declaration, which substitutes a different entity to be exported in place of the module itself, is always emitted as an assignment tomodule.exports. It is an error to have other exports in a module that usesexport =. This is the existing TypeScript behavior. - A module that uses
export =to export another module (be that an internal or external module) can be imported using the new ES6 constructs. In particular, the convenient destructuring imports can be used with such modules. The pattern of usingexport =to export another module is common in .d.ts files that provide a CommonJS/AMD view of an internal module (e.g. angular.d.ts). - A module that uses
export =to export a non-module entity in place of the module itself must be imported using the existingimport x = require("foo")syntax as is the case today.
2016 update: The TypeScript compiler at some point started allowing import * as foo from 'legacy-module-foo' to get the default import of a legacy module in certain circumstances. This is a violation of the ES6 specification (§15.2.1.16, “The value "*" indicates that the import request is for the target module’s namespace object.”).
When legacy modules you import in this manner are updated to ES6 modules, the “default” imports for those modules will stop working (because * as foo imports are supposed to be importing namespace objects), which may be extremely confusing if you don’t know that doing this is a TypeScript/SystemJS hack. It is also possible that a future TypeScript realignment to the ES specification will cause them to break.
As such, you should probably prefer to continue to use the legacy import syntax described above to load legacy modules to avoid confusing yourself and other developers working on your code about how ES6 namespace imports work, and to avoid confusing breaking changes.
Answer from C Snover on Stack OverflowThe correct way is to continue using the old import syntax. The new import syntax is for ES modules only, the old import syntax is for pre-ES6 modules. The two are distinct, intentionally so. import * as foo from 'foo' imports all the properties of the module 'foo', it does not import the default value as foo.
From the designer of the feature:
- An export default declaration always declares an exported member named default and is always emitted as an assignment to exports.default. In other words,
export defaultconsistently has ES module semantics. For compatibility with Babel we could optionally emit an__esModulemarker when a module has a default export, but we wouldn't actually use that marker for anything. - An
export =declaration, which substitutes a different entity to be exported in place of the module itself, is always emitted as an assignment tomodule.exports. It is an error to have other exports in a module that usesexport =. This is the existing TypeScript behavior. - A module that uses
export =to export another module (be that an internal or external module) can be imported using the new ES6 constructs. In particular, the convenient destructuring imports can be used with such modules. The pattern of usingexport =to export another module is common in .d.ts files that provide a CommonJS/AMD view of an internal module (e.g. angular.d.ts). - A module that uses
export =to export a non-module entity in place of the module itself must be imported using the existingimport x = require("foo")syntax as is the case today.
2016 update: The TypeScript compiler at some point started allowing import * as foo from 'legacy-module-foo' to get the default import of a legacy module in certain circumstances. This is a violation of the ES6 specification (§15.2.1.16, “The value "*" indicates that the import request is for the target module’s namespace object.”).
When legacy modules you import in this manner are updated to ES6 modules, the “default” imports for those modules will stop working (because * as foo imports are supposed to be importing namespace objects), which may be extremely confusing if you don’t know that doing this is a TypeScript/SystemJS hack. It is also possible that a future TypeScript realignment to the ES specification will cause them to break.
As such, you should probably prefer to continue to use the legacy import syntax described above to load legacy modules to avoid confusing yourself and other developers working on your code about how ES6 namespace imports work, and to avoid confusing breaking changes.
The corresponding syntax for ES6 module syntax is:
import * as foo from 'foo';
Basically import everything from the foo module into a local variable by the name of foo.
I am working in NodeJS. I have a great deal of legacy code including several packages that are used in many places. This code is all CommonJS, Node require() module structures.
Node now supports ES6. Since it is a Javascript language feature, I would like to migrate to it.
Today, I started a small project. My small project boilerplate requires() a couple of my favorite utilities and then says 'Hello World'. I edited it to import said utilities. Node told me I needed to add "type":"module" to my package.json and I did.
When I ran it, I was told that "require is not defined", this in reference to one of the utility modules I imported.
I infer that this means that a project is either CommonJS or ES6 and it appears that never the twain shall meet. I am surprised by this because it means that I will never use ES6 in NodeJS because I will never be able to change all of the modules I require(). Some are not even mine, others are used in projects (npm!) that I do not even know about.
Honestly, I have a hard time believing that this is the case. I don't understand how ES6 can ever become a widely used standard because of if ES^ and CommonJS cannot be used together in an application. I realize that Webpack, etc, will preprocess code and revise all the require() statements but not everyone uses that sort of utility.
My questions are:
Is this analysis correct?
Is there some workaround that will let me use both module systems (without a preprocessor)?
Is my impending decision to never, ever use ES6 the right one?
UPDATE: It's a couple of years later and I am a little bit mad at all of you. It turns out there is a completely simple workaround that nobody ever mentioned**. IE...**
const thing=await import('thing'); //remember to add "async" to the containing function definition
Is it possible to es6 import a commonjs module?
Importing commonJS modules with es6 imports
How to include commonjs module in ES6 module node app?
CommonJS to ES6 rewrite
Videos
In Node.js, if you want to import an ES module in a CommonJS module you can use dynamic import and the .mjs file extension on the ES modules. For example:
index.js CommonJS
const crypto = require('crypto'); // to show this is a commonJS module
import('./path/to/mod.mjs').then(mod =>
console.log(mod.msg); // "Hello world!"
);
mod.mjs ES module
export const msg = "Hello world!";
Two examples of how import could be used within a CommonJS module to import all or some of the lodash-es package:
import('lodash-es').then(_ => {
console.log(_.pad(_.toUpper('hello world'), 17, '*'));
});
Promise.all([
import('lodash-es/pad.js'),
import('lodash-es/toUpper.js'),
])
.then(([{ default: pad }, { default: toUpper }]) => {
console.log(pad(toUpper('hello world'), 17, '#'));
});
Or you can just import what you need into a different CommonJS module then export a Promise which you can then import or require.
utils.js
module.exports = Promise.all([
import('lodash-es/pad.js'),
import('lodash-es/toUpper.js'),
]);
index.js
require('./utils.js').then(([{ default: pad }, { default: toUpper }]) => {
console.log(pad(toUpper('hello world'), 17, '*'));
});
ESModules and CommonJS are mutually exclusive, so you can't "use ES6 modules inside CommonJS".
However, in a way, you can "use CommonJS inside ESModules", if by "CommonJS" you only mean the require() function. You can create an instance of require() function with module.createRequire():
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
// sibling-module.js is a CommonJS module.
const siblingModule = require('./sibling-module');
There's a chapter in NodeJS's documentation on interoperability between the two module systems. It's very helpful, you might want to check it out.
Short Answer: No
When using es6, you need to export using export and not exports. exports is a commonjs feature primarily used within node and not a web browser.
If you would like to use commonjs you need to use a third party library like requirejs, but this uses require() and not import, and exports and not export. You will then be able to write your code using import/export with typescript but it will be compiled using require and requirejs will handle the rest.
So, to use it in the browser properly, you would do it like so:
test.js
export function Test() {
console.log('hello')
}
index.js
import { Test } from './test.js'
Test()
Then when you load the file in your html, the function test will execute.
<script src="index.js" type="module"></script>
Yes by using esm like this:
- Install esm like
npm i -save esm
- Run the app like
node -r esm filename.js
You can use import in cjs
Working with CommonJS modules is pretty straight forward.
You can only do default exports from CommonJS modules.
import packageMain from 'commonjs-package'; // Works
import { method } from 'commonjs-package'; // Errors
This means that all commonjs exports will live on the packageMain object, and you need to dot in to the packageMain object to pickup what you need.
packageMain.method1()
More info in the official nodejs docs
Since Node 13.10, there is another option, the most forward-looking one:
File an issue in the repo of the CommonJS library you'd like to use, persuading the maintainers to publish dual packages (ESM + CommonJS), using conditional exports.
For libraries written in TypeScript, generating dual packages is easy, and doesn't require Babel or rollup or any additional tools. Here's how I did it in local-iso-dt:
Relevant parts of package.json:
{
"name": "local-iso-dt",
"version": "3.1.0",
"description": "...",
"type": "commonjs",
"exports": {
"node": {
"import": "./index.mjs",
"require": "./index.js"
},
"default": "./index.mjs"
},
"main": "index.js",
"files": [
"index.ts",
"index.mjs",
"index.js"
],
"scripts": {
"clean": "rm index*.js index.mjs",
"prepublishOnly:cjs": "tsc index.ts --esModuleInterop --removeComments",
"prepublishOnly:esm": "tsc index.ts -t ES2015 --types node && mv index.js index.mjs",
"prepublishOnly": "npm run prepublishOnly:esm; npm run prepublishOnly:cjs"
},
"devDependencies": {
"typescript": "^4.0.2"
},
}
prepublishOnly:esm renames the output manually because TypeScript can't yet generate .mjs output directly and --outFile doesn't work with ES Modules.
The exports block has the "conditional exports that enable TypeScript code transpiled with ES Modules, to use named imports. TypeScript doesn't directly support .mjs input files.
No tsconfig.json was necessary for this simple module.
I want to rewrite a application that uses CommonJS modules to ES6 modules in nodejs. It's a simple web app with simple stack (Express, Knex, Mocha, etc) but I will evolve it to a more complex app with more features and modules.
The thing that makes me worry is the current state of the ecosystem and support for ES6 modules in nodejs. Things like drivers for databases, testing frameworks, support libraries (ex: lodash), etc.
Have we passed the point where we can use ES6 modules in nodejs without any problems? Or should I stick with CommonJS for now?