diff --git a/doc/api/esm.md b/doc/api/esm.md index bc25c88b9a82e9..03aa6c90a8fa99 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -19,11 +19,13 @@ and implementation is ready. Error messages are still being polished. The `--experimental-modules` flag can be used to enable features for loading ESM modules. -Once this has been set, files ending with `.mjs` will be able to be loaded +Once this has been set, files ending with `.mjs` or `.m.js` will be able to be loaded as ES Modules. ```sh node --experimental-modules my-app.mjs + +node --experimental-modules other-app.m.js ``` ## Features @@ -41,6 +43,7 @@ points into ESM graphs at run time. | Feature | Reason | | --- | --- | | `require('./foo.mjs')` | ES Modules have differing resolution and timing, use language standard `import()` | +| `require('./foo.m.js')` | same as above, use language standard `import()` | | `import()` | pending newer V8 release used in Node.js | | `import.meta` | pending V8 implementation | | Loader Hooks | pending Node.js EP creation/consensus | diff --git a/lib/internal/loader/ModuleRequest.js b/lib/internal/loader/ModuleRequest.js index 88e48ae9d3eaa1..9640cb1875336c 100644 --- a/lib/internal/loader/ModuleRequest.js +++ b/lib/internal/loader/ModuleRequest.js @@ -114,7 +114,8 @@ exports.resolve = (specifier, parentURL) => { case '.node': return { url: `${url}`, format: 'addon' }; case '.js': - return { url: `${url}`, format: 'cjs' }; + const format = url.pathname.slice(-5) === '.m.js' ? 'esm' : 'cjs'; + return { url: `${url}`, format }; default: throw new errors.Error('ERR_UNKNOWN_FILE_EXTENSION', internalURLModule.getPathFromURL(url)); diff --git a/lib/module.js b/lib/module.js index 73f3cc8dd8cde5..f2e58365b534f3 100644 --- a/lib/module.js +++ b/lib/module.js @@ -611,6 +611,9 @@ Module.prototype._compile = function(content, filename) { // Native extension for .js Module._extensions['.js'] = function(module, filename) { + if (experimentalModules && filename.slice(-5) === '.m.js') { + return Module._extensions['.mjs'](module, filename); + } var content = fs.readFileSync(filename, 'utf8'); module._compile(internalModule.stripBOM(content), filename); }; diff --git a/test/es-module/test-es-m-basic-imports.mjs b/test/es-module/test-es-m-basic-imports.mjs new file mode 100644 index 00000000000000..98ef35a313eccd --- /dev/null +++ b/test/es-module/test-es-m-basic-imports.mjs @@ -0,0 +1,5 @@ +// Flags: --experimental-modules +import assert from 'assert'; +import ok from './test-esm-ok.m.js'; + +assert(ok === true); diff --git a/test/es-module/test-es-m-failing-require.js b/test/es-module/test-es-m-failing-require.js new file mode 100644 index 00000000000000..b61c8a1a549b86 --- /dev/null +++ b/test/es-module/test-es-m-failing-require.js @@ -0,0 +1,9 @@ +// Flags: --experimental-modules +const assert = require('assert'); + +try { + require('./test-esm-ok.m.js'); + assert(false); +} catch(error) { + assert(/es\s*m(?:odule)?|\.m\.?js/i.test(error.message)); +} diff --git a/test/es-module/test-esm-ok.m.js b/test/es-module/test-esm-ok.m.js new file mode 100644 index 00000000000000..6712e1ab7dfca1 --- /dev/null +++ b/test/es-module/test-esm-ok.m.js @@ -0,0 +1,5 @@ +// Flags: --experimental-modules +/* eslint-disable required-modules */ + +const isJs = true; +export default isJs;