esm: phase two of new esm implementation
This PR updates the current `--experimental-modules` implementation based on the work of the modules team and reflects Phase 2 of our new modules plan. The largest differences from the current implementation include * `packge.type` which can be either `module` or `commonjs` - `type: "commonjs"`: - `.js` is parsed as commonjs - default for entry point without an extension is commonjs - `type: "module"`: - `.js` is parsed as esm - does not support loading JSON or Native Module by default - default for entry point without an extension is esm * `--entry-type=[mode]` - allows you set the type on entry point. * A new file extension `.cjs`. - this is specifically to support importing commonjs in the `module` mode. - this is only in the esm loader, the commonjs loader remains untouched, but the extension will work in the old loader if you use the full file path. * `--es-module-specifier-resolution=[type]` - options are `explicit` (default) and `node` - by default our loader will not allow for optional extensions in the import, the path for a module must include the extension if there is one - by default our loader will not allow for importing directories that have an index file - developers can use `--es-module-specifier-resolution=node` to enable the commonjs specifier resolution algorithm - This is not a “feature” but rather an implementation for experimentation. It is expected to change before the flag is removed * `--experimental-json-loader` - the only way to import json when `"type": "module"` - when enable all `import 'thing.json'` will go through the experimental loader independent of mode - based on https://github.com/whatwg/html/issues/4315 * You can use `package.main` to set an entry point for a module - the file extensions used in main will be resolved based on the `type` of the module Refs: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md Refs: https://github.com/GeoffreyBooth/node-import-file-specifier-resolution-proposal Refs: https://github.com/nodejs/modules/pull/180 Refs: https://github.com/nodejs/ecmascript-modules/pull/6 Refs: https://github.com/nodejs/ecmascript-modules/pull/12 Refs: https://github.com/nodejs/ecmascript-modules/pull/28 Refs: https://github.com/nodejs/modules/issues/255 Refs: https://github.com/whatwg/html/issues/4315 Refs: https://github.com/w3c/webcomponents/issues/770 Co-authored-by: Myles Borins <MylesBorins@google.com> Co-authored-by: John-David Dalton <john.david.dalton@gmail.com> Co-authored-by: Evan Plaice <evanplaice@gmail.com> Co-authored-by: Geoffrey Booth <webmaster@geoffreybooth.com> Co-authored-by: Michaël Zasso <targos@protonmail.com> PR-URL: https://github.com/nodejs/node/pull/26745 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Ben Coe <bencoe@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
This commit is contained in:
parent
39141426d4
commit
b1094dbe19
@ -38,6 +38,8 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
'doc/api/esm.md',
|
'doc/api/esm.md',
|
||||||
|
'test/es-module/test-esm-type-flag.js',
|
||||||
|
'test/es-module/test-esm-type-flag-alias.js',
|
||||||
'*.mjs',
|
'*.mjs',
|
||||||
'test/es-module/test-esm-example-loader.js',
|
'test/es-module/test-esm-example-loader.js',
|
||||||
],
|
],
|
||||||
|
@ -131,9 +131,43 @@ conjunction with native stack and other runtime environment data.
|
|||||||
added: v6.0.0
|
added: v6.0.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
### `--entry-type=type`
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
Used with `--experimental-modules`, this configures Node.js to interpret the
|
||||||
|
initial entry point as CommonJS or as an ES module.
|
||||||
|
|
||||||
|
Valid values are `"commonjs"` and `"module"`. The default is to infer from
|
||||||
|
the file extension and the `"type"` field in the nearest parent `package.json`.
|
||||||
|
|
||||||
|
Works for executing a file as well as `--eval`, `--print`, `STDIN`.
|
||||||
|
|
||||||
Enable FIPS-compliant crypto at startup. (Requires Node.js to be built with
|
Enable FIPS-compliant crypto at startup. (Requires Node.js to be built with
|
||||||
`./configure --openssl-fips`.)
|
`./configure --openssl-fips`.)
|
||||||
|
|
||||||
|
### `--es-module-specifier-resolution=mode`
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
To be used in conjunction with `--experimental-modules`. Sets the resolution
|
||||||
|
algorithm for resolving specifiers. Valid options are `explicit` and `node`.
|
||||||
|
|
||||||
|
The default is `explicit`, which requires providing the full path to a
|
||||||
|
module. The `node` mode will enable support for optional file extensions and
|
||||||
|
the ability to import a directory that has an index file.
|
||||||
|
|
||||||
|
Please see [customizing esm specifier resolution][] for example usage.
|
||||||
|
|
||||||
|
### `--experimental-json-modules`
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
Enable experimental JSON support for the ES Module loader.
|
||||||
|
|
||||||
### `--experimental-modules`
|
### `--experimental-modules`
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.5.0
|
added: v8.5.0
|
||||||
@ -927,9 +961,10 @@ greater than `4` (its current default value). For more information, see the
|
|||||||
[REPL]: repl.html
|
[REPL]: repl.html
|
||||||
[ScriptCoverage]: https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ScriptCoverage
|
[ScriptCoverage]: https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ScriptCoverage
|
||||||
[V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html
|
[V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html
|
||||||
|
[customizing esm specifier resolution]: esm.html#esm_customizing_esm_specifier_resolution_algorithm
|
||||||
[debugger]: debugger.html
|
[debugger]: debugger.html
|
||||||
[debugging security implications]: https://nodejs.org/en/docs/guides/debugging-getting-started/#security-implications
|
[debugging security implications]: https://nodejs.org/en/docs/guides/debugging-getting-started/#security-implications
|
||||||
[emit_warning]: process.html#process_process_emitwarning_warning_type_code_ctor
|
[emit_warning]: process.html#process_process_emitwarning_warning_type_code_ctor
|
||||||
[experimental ECMAScript Module]: esm.html#esm_loader_hooks
|
[experimental ECMAScript Module]: esm.html#esm_resolve_hook
|
||||||
[libuv threadpool documentation]: http://docs.libuv.org/en/latest/threadpool.html
|
[libuv threadpool documentation]: http://docs.libuv.org/en/latest/threadpool.html
|
||||||
[remote code execution]: https://www.owasp.org/index.php/Code_Injection
|
[remote code execution]: https://www.owasp.org/index.php/Code_Injection
|
||||||
|
@ -844,6 +844,18 @@ provided.
|
|||||||
Encoding provided to `TextDecoder()` API was not one of the
|
Encoding provided to `TextDecoder()` API was not one of the
|
||||||
[WHATWG Supported Encodings][].
|
[WHATWG Supported Encodings][].
|
||||||
|
|
||||||
|
<a id="ERR_ENTRY_TYPE_MISMATCH"></a>
|
||||||
|
#### ERR_ENTRY_TYPE_MISMATCH
|
||||||
|
|
||||||
|
> Stability: 1 - Experimental
|
||||||
|
|
||||||
|
The `--entry-type=commonjs` flag was used to attempt to execute an `.mjs` file
|
||||||
|
or a `.js` file where the nearest parent `package.json` contains
|
||||||
|
`"type": "module"`; or
|
||||||
|
the `--entry-type=module` flag was used to attempt to execute a `.cjs` file or
|
||||||
|
a `.js` file where the nearest parent `package.json` either lacks a `"type"`
|
||||||
|
field or contains `"type": "commonjs"`.
|
||||||
|
|
||||||
<a id="ERR_FALSY_VALUE_REJECTION"></a>
|
<a id="ERR_FALSY_VALUE_REJECTION"></a>
|
||||||
### ERR_FALSY_VALUE_REJECTION
|
### ERR_FALSY_VALUE_REJECTION
|
||||||
|
|
||||||
@ -1267,6 +1279,11 @@ An invalid or unexpected value was passed in an options object.
|
|||||||
|
|
||||||
An invalid or unknown file encoding was passed.
|
An invalid or unknown file encoding was passed.
|
||||||
|
|
||||||
|
<a id="ERR_INVALID_PACKAGE_CONFIG"></a>
|
||||||
|
### ERR_INVALID_PACKAGE_CONFIG
|
||||||
|
|
||||||
|
An invalid `package.json` file was found which failed parsing.
|
||||||
|
|
||||||
<a id="ERR_INVALID_PERFORMANCE_MARK"></a>
|
<a id="ERR_INVALID_PERFORMANCE_MARK"></a>
|
||||||
### ERR_INVALID_PERFORMANCE_MARK
|
### ERR_INVALID_PERFORMANCE_MARK
|
||||||
|
|
||||||
@ -1440,7 +1457,7 @@ strict compliance with the API specification (which in some cases may accept
|
|||||||
|
|
||||||
> Stability: 1 - Experimental
|
> Stability: 1 - Experimental
|
||||||
|
|
||||||
An [ES6 module][] loader hook specified `format: 'dynamic'` but did not provide
|
An [ES Module][] loader hook specified `format: 'dynamic'` but did not provide
|
||||||
a `dynamicInstantiate` hook.
|
a `dynamicInstantiate` hook.
|
||||||
|
|
||||||
<a id="ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST"></a>
|
<a id="ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST"></a>
|
||||||
@ -1449,13 +1466,6 @@ a `dynamicInstantiate` hook.
|
|||||||
A `MessagePort` was found in the object passed to a `postMessage()` call,
|
A `MessagePort` was found in the object passed to a `postMessage()` call,
|
||||||
but not provided in the `transferList` for that call.
|
but not provided in the `transferList` for that call.
|
||||||
|
|
||||||
<a id="ERR_MISSING_MODULE"></a>
|
|
||||||
### ERR_MISSING_MODULE
|
|
||||||
|
|
||||||
> Stability: 1 - Experimental
|
|
||||||
|
|
||||||
An [ES6 module][] could not be resolved.
|
|
||||||
|
|
||||||
<a id="ERR_MISSING_PLATFORM_FOR_WORKER"></a>
|
<a id="ERR_MISSING_PLATFORM_FOR_WORKER"></a>
|
||||||
### ERR_MISSING_PLATFORM_FOR_WORKER
|
### ERR_MISSING_PLATFORM_FOR_WORKER
|
||||||
|
|
||||||
@ -1463,12 +1473,12 @@ The V8 platform used by this instance of Node.js does not support creating
|
|||||||
Workers. This is caused by lack of embedder support for Workers. In particular,
|
Workers. This is caused by lack of embedder support for Workers. In particular,
|
||||||
this error will not occur with standard builds of Node.js.
|
this error will not occur with standard builds of Node.js.
|
||||||
|
|
||||||
<a id="ERR_MODULE_RESOLUTION_LEGACY"></a>
|
<a id="ERR_MODULE_NOT_FOUND"></a>
|
||||||
### ERR_MODULE_RESOLUTION_LEGACY
|
### ERR_MODULE_NOT_FOUND
|
||||||
|
|
||||||
> Stability: 1 - Experimental
|
> Stability: 1 - Experimental
|
||||||
|
|
||||||
A failure occurred resolving imports in an [ES6 module][].
|
An [ES Module][] could not be resolved.
|
||||||
|
|
||||||
<a id="ERR_MULTIPLE_CALLBACK"></a>
|
<a id="ERR_MULTIPLE_CALLBACK"></a>
|
||||||
### ERR_MULTIPLE_CALLBACK
|
### ERR_MULTIPLE_CALLBACK
|
||||||
@ -1555,7 +1565,7 @@ A given value is out of the accepted range.
|
|||||||
|
|
||||||
> Stability: 1 - Experimental
|
> Stability: 1 - Experimental
|
||||||
|
|
||||||
An attempt was made to `require()` an [ES6 module][].
|
An attempt was made to `require()` an [ES Module][].
|
||||||
|
|
||||||
<a id="ERR_SCRIPT_EXECUTION_INTERRUPTED"></a>
|
<a id="ERR_SCRIPT_EXECUTION_INTERRUPTED"></a>
|
||||||
### ERR_SCRIPT_EXECUTION_INTERRUPTED
|
### ERR_SCRIPT_EXECUTION_INTERRUPTED
|
||||||
@ -2220,10 +2230,17 @@ A non-specific HTTP/2 error has occurred.
|
|||||||
Used in the `repl` in case the old history file is used and an error occurred
|
Used in the `repl` in case the old history file is used and an error occurred
|
||||||
while trying to read and parse it.
|
while trying to read and parse it.
|
||||||
|
|
||||||
|
<a id="ERR_INVALID_REPL_TYPE"></a>
|
||||||
|
#### ERR_INVALID_REPL_TYPE
|
||||||
|
|
||||||
|
> Stability: 1 - Experimental
|
||||||
|
|
||||||
|
The `--entry-type=...` flag is not compatible with the Node.js REPL.
|
||||||
|
|
||||||
<a id="ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK"></a>
|
<a id="ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK"></a>
|
||||||
#### ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK
|
#### ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK
|
||||||
|
|
||||||
Used when an [ES6 module][] loader hook specifies `format: 'dynamic'` but does
|
Used when an [ES Module][] loader hook specifies `format: 'dynamic'` but does
|
||||||
not provide a `dynamicInstantiate` hook.
|
not provide a `dynamicInstantiate` hook.
|
||||||
|
|
||||||
<a id="ERR_STREAM_HAS_STRINGDECODER"></a>
|
<a id="ERR_STREAM_HAS_STRINGDECODER"></a>
|
||||||
@ -2250,7 +2267,6 @@ size.
|
|||||||
This `Error` is thrown when a read is attempted on a TTY `WriteStream`,
|
This `Error` is thrown when a read is attempted on a TTY `WriteStream`,
|
||||||
such as `process.stdout.on('data')`.
|
such as `process.stdout.on('data')`.
|
||||||
|
|
||||||
|
|
||||||
[`'uncaughtException'`]: process.html#process_event_uncaughtexception
|
[`'uncaughtException'`]: process.html#process_event_uncaughtexception
|
||||||
[`--force-fips`]: cli.html#cli_force_fips
|
[`--force-fips`]: cli.html#cli_force_fips
|
||||||
[`Class: assert.AssertionError`]: assert.html#assert_class_assert_assertionerror
|
[`Class: assert.AssertionError`]: assert.html#assert_class_assert_assertionerror
|
||||||
@ -2293,7 +2309,7 @@ such as `process.stdout.on('data')`.
|
|||||||
[`subprocess.kill()`]: child_process.html#child_process_subprocess_kill_signal
|
[`subprocess.kill()`]: child_process.html#child_process_subprocess_kill_signal
|
||||||
[`subprocess.send()`]: child_process.html#child_process_subprocess_send_message_sendhandle_options_callback
|
[`subprocess.send()`]: child_process.html#child_process_subprocess_send_message_sendhandle_options_callback
|
||||||
[`zlib`]: zlib.html
|
[`zlib`]: zlib.html
|
||||||
[ES6 module]: esm.html
|
[ES Module]: esm.html
|
||||||
[ICU]: intl.html#intl_internationalization_support
|
[ICU]: intl.html#intl_internationalization_support
|
||||||
[Node.js Error Codes]: #nodejs-error-codes
|
[Node.js Error Codes]: #nodejs-error-codes
|
||||||
[V8's stack trace API]: https://github.com/v8/v8/wiki/Stack-Trace-API
|
[V8's stack trace API]: https://github.com/v8/v8/wiki/Stack-Trace-API
|
||||||
|
564
doc/api/esm.md
564
doc/api/esm.md
@ -5,39 +5,233 @@
|
|||||||
|
|
||||||
> Stability: 1 - Experimental
|
> Stability: 1 - Experimental
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
<!--name=esm-->
|
<!--name=esm-->
|
||||||
|
|
||||||
Node.js contains support for ES Modules based upon the
|
ECMAScript modules are [the official standard format][] to package JavaScript
|
||||||
[Node.js EP for ES Modules][].
|
code for reuse. Modules are defined using a variety of [`import`][] and
|
||||||
|
[`export`][] statements.
|
||||||
|
|
||||||
Not all features of the EP are complete and will be landing as both VM support
|
Node.js fully supports ECMAScript modules as they are currently specified and
|
||||||
and implementation is ready. Error messages are still being polished.
|
provides limited interoperability between them and the existing module format,
|
||||||
|
[CommonJS][].
|
||||||
|
|
||||||
|
Node.js contains support for ES Modules based upon the
|
||||||
|
[Node.js EP for ES Modules][] and the [ECMAScript-modules implementation][].
|
||||||
|
|
||||||
|
Expect major changes in the implementation including interoperability support,
|
||||||
|
specifier resolution, and default behavior.
|
||||||
|
|
||||||
## Enabling
|
## Enabling
|
||||||
|
|
||||||
<!-- type=misc -->
|
<!-- type=misc -->
|
||||||
|
|
||||||
The `--experimental-modules` flag can be used to enable features for loading
|
The `--experimental-modules` flag can be used to enable support for
|
||||||
ESM modules.
|
ECMAScript modules (ES modules).
|
||||||
|
|
||||||
Once this has been set, files ending with `.mjs` will be able to be loaded
|
## Running Node.js with an ECMAScript Module
|
||||||
as ES Modules.
|
|
||||||
|
There are a few ways to start Node.js with an ES module as its input.
|
||||||
|
|
||||||
|
### Initial entry point with an <code>.mjs</code> extension
|
||||||
|
|
||||||
|
A file ending with `.mjs` passed to Node.js as an initial entry point will be
|
||||||
|
loaded as an ES module.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
node --experimental-modules my-app.mjs
|
node --experimental-modules my-app.mjs
|
||||||
```
|
```
|
||||||
|
|
||||||
## Features
|
### <code>--entry-type=module</code> flag
|
||||||
|
|
||||||
<!-- type=misc -->
|
Files ending with `.js` or `.mjs`, or lacking any extension,
|
||||||
|
will be loaded as ES modules when the `--entry-type=module` flag is set.
|
||||||
|
|
||||||
### Supported
|
```sh
|
||||||
|
node --experimental-modules --entry-type=module my-app.js
|
||||||
|
```
|
||||||
|
|
||||||
Only the CLI argument for the main entry point to the program can be an entry
|
For completeness there is also `--entry-type=commonjs`, for explicitly running
|
||||||
point into an ESM graph. Dynamic import can also be used to create entry points
|
a `.js` file as CommonJS. This is the default behavior if `--entry-type` is
|
||||||
into ESM graphs at runtime.
|
unspecified.
|
||||||
|
|
||||||
#### import.meta
|
The `--entry-type=module` flag can also be used to configure Node.js to treat
|
||||||
|
as an ES module input sent in via `--eval` or `--print` (or `-e` or `-p`) or
|
||||||
|
piped to Node.js via `STDIN`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
node --experimental-modules --entry-type=module --eval \
|
||||||
|
"import { sep } from 'path'; console.log(sep);"
|
||||||
|
|
||||||
|
echo "import { sep } from 'path'; console.log(sep);" | \
|
||||||
|
node --experimental-modules --entry-type=module
|
||||||
|
```
|
||||||
|
|
||||||
|
### <code>package.json</code> <code>"type"</code> field
|
||||||
|
|
||||||
|
Files ending with `.js` or `.mjs`, or lacking any extension,
|
||||||
|
will be loaded as ES modules when the nearest parent `package.json` file
|
||||||
|
contains a top-level field `"type"` with a value of `"module"`.
|
||||||
|
|
||||||
|
The nearest parent `package.json` is defined as the first `package.json` found
|
||||||
|
when searching in the current folder, that folder’s parent, and so on up
|
||||||
|
until the root of the volume is reached.
|
||||||
|
|
||||||
|
<!-- eslint-skip -->
|
||||||
|
```js
|
||||||
|
// package.json
|
||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# In same folder as above package.json
|
||||||
|
node --experimental-modules my-app.js # Runs as ES module
|
||||||
|
```
|
||||||
|
|
||||||
|
If the nearest parent `package.json` lacks a `"type"` field, or contains
|
||||||
|
`"type": "commonjs"`, extensionless and `.js` files are treated as CommonJS.
|
||||||
|
If the volume root is reached and no `package.json` is found,
|
||||||
|
Node.js defers to the default, a `package.json` with no `"type"`
|
||||||
|
field.
|
||||||
|
|
||||||
|
## Package Scope and File Extensions
|
||||||
|
|
||||||
|
A folder containing a `package.json` file, and all subfolders below that
|
||||||
|
folder down until the next folder containing another `package.json`, is
|
||||||
|
considered a _package scope_. The `"type"` field defines how `.js` and
|
||||||
|
extensionless files should be treated within a particular `package.json` file’s
|
||||||
|
package scope. Every package in a project’s `node_modules` folder contains its
|
||||||
|
own `package.json` file, so each project’s dependencies have their own package
|
||||||
|
scopes. A `package.json` lacking a `"type"` field is treated as if it contained
|
||||||
|
`"type": "commonjs"`.
|
||||||
|
|
||||||
|
The package scope applies not only to initial entry points (`node
|
||||||
|
--experimental-modules my-app.js`) but also to files referenced by `import`
|
||||||
|
statements and `import()` expressions.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// my-app.js, in an ES module package scope because there is a package.json
|
||||||
|
// file in the same folder with "type": "module".
|
||||||
|
|
||||||
|
import './startup/init.js';
|
||||||
|
// Loaded as ES module since ./startup contains no package.json file,
|
||||||
|
// and therefore inherits the ES module package scope from one level up.
|
||||||
|
|
||||||
|
import 'commonjs-package';
|
||||||
|
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json
|
||||||
|
// lacks a "type" field or contains "type": "commonjs".
|
||||||
|
|
||||||
|
import './node_modules/commonjs-package/index.js';
|
||||||
|
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json
|
||||||
|
// lacks a "type" field or contains "type": "commonjs".
|
||||||
|
```
|
||||||
|
|
||||||
|
Files ending with `.mjs` are always loaded as ES modules regardless of package
|
||||||
|
scope.
|
||||||
|
|
||||||
|
Files ending with `.cjs` are always loaded as CommonJS regardless of package
|
||||||
|
scope.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import './legacy-file.cjs';
|
||||||
|
// Loaded as CommonJS since .cjs is always loaded as CommonJS.
|
||||||
|
|
||||||
|
import 'commonjs-package/src/index.mjs';
|
||||||
|
// Loaded as ES module since .mjs is always loaded as ES module.
|
||||||
|
```
|
||||||
|
|
||||||
|
The `.mjs` and `.cjs` extensions may be used to mix types within the same
|
||||||
|
package scope:
|
||||||
|
|
||||||
|
- Within a `"type": "module"` package scope, Node.js can be instructed to
|
||||||
|
interpret a particular file as CommonJS by naming it with a `.cjs` extension
|
||||||
|
(since both `.js` and `.mjs` files are treated as ES modules within a
|
||||||
|
`"module"` package scope).
|
||||||
|
|
||||||
|
- Within a `"type": "commonjs"` package scope, Node.js can be instructed to
|
||||||
|
interpret a particular file as an ES module by naming it with an `.mjs`
|
||||||
|
extension (since both `.js` and `.cjs` files are treated as CommonJS within a
|
||||||
|
`"commonjs"` package scope).
|
||||||
|
|
||||||
|
## Package Entry Points
|
||||||
|
|
||||||
|
The `package.json` `"main"` field defines the entry point for a package,
|
||||||
|
whether the package is included into CommonJS via `require` or into an ES
|
||||||
|
module via `import`.
|
||||||
|
|
||||||
|
<!-- eslint-skip -->
|
||||||
|
```js
|
||||||
|
// ./node_modules/es-module-package/package.json
|
||||||
|
{
|
||||||
|
"type": "module",
|
||||||
|
"main": "./src/index.js"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```js
|
||||||
|
// ./my-app.mjs
|
||||||
|
|
||||||
|
import { something } from 'es-module-package';
|
||||||
|
// Loads from ./node_modules/es-module-package/src/index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
An attempt to `require` the above `es-module-package` would attempt to load
|
||||||
|
`./node_modules/es-module-package/src/index.js` as CommonJS, which would throw
|
||||||
|
an error as Node.js would not be able to parse the `export` statement in
|
||||||
|
CommonJS.
|
||||||
|
|
||||||
|
As with `import` statements, for ES module usage the value of `"main"` must be
|
||||||
|
a full path including extension: `"./index.mjs"`, not `"./index"`.
|
||||||
|
|
||||||
|
If the `package.json` `"type"` field is omitted, a `.js` file in `"main"` will
|
||||||
|
be interpreted as CommonJS.
|
||||||
|
|
||||||
|
> Currently a package can define _either_ a CommonJS entry point **or** an ES
|
||||||
|
> module entry point; there is no way to specify separate entry points for
|
||||||
|
> CommonJS and ES module usage. This means that a package entry point can be
|
||||||
|
> included via `require` or via `import` but not both.
|
||||||
|
>
|
||||||
|
> Such a limitation makes it difficult for packages to support both new versions
|
||||||
|
> of Node.js that understand ES modules and older versions of Node.js that
|
||||||
|
> understand only CommonJS. There is work ongoing to remove this limitation, and
|
||||||
|
> it will very likely entail changes to the behavior of `"main"` as defined
|
||||||
|
> here.
|
||||||
|
|
||||||
|
## <code>import</code> Specifiers
|
||||||
|
|
||||||
|
### Terminology
|
||||||
|
|
||||||
|
The _specifier_ of an `import` statement is the string after the `from` keyword,
|
||||||
|
e.g. `'path'` in `import { sep } from 'path'`. Specifiers are also used in
|
||||||
|
`export from` statements, and as the argument to an `import()` expression.
|
||||||
|
|
||||||
|
There are four types of specifiers:
|
||||||
|
|
||||||
|
- _Bare specifiers_ like `'some-package'`. They refer to an entry point of a
|
||||||
|
package by the package name.
|
||||||
|
|
||||||
|
- _Deep import specifiers_ like `'some-package/lib/shuffle.mjs'`. They refer to
|
||||||
|
a path within a package prefixed by the package name.
|
||||||
|
|
||||||
|
- _Relative specifiers_ like `'./startup.js'` or `'../config.mjs'`. They refer
|
||||||
|
to a path relative to the location of the importing file.
|
||||||
|
|
||||||
|
- _Absolute specifiers_ like `'file:///opt/nodejs/config.js'`. They refer
|
||||||
|
directly and explicitly to a full path.
|
||||||
|
|
||||||
|
Bare specifiers, and the bare specifier portion of deep import specifiers, are
|
||||||
|
strings; but everything else in a specifier is a URL.
|
||||||
|
|
||||||
|
Only `file://` URLs are supported. A specifier like
|
||||||
|
`'https://example.com/app.js'` may be supported by browsers but it is not
|
||||||
|
supported in Node.js.
|
||||||
|
|
||||||
|
Specifiers may not begin with `/` or `//`. These are reserved for potential
|
||||||
|
future use. The root of the current volume may be referenced via `file:///`.
|
||||||
|
|
||||||
|
## import.meta
|
||||||
|
|
||||||
* {Object}
|
* {Object}
|
||||||
|
|
||||||
@ -46,63 +240,128 @@ property:
|
|||||||
|
|
||||||
* `url` {string} The absolute `file:` URL of the module.
|
* `url` {string} The absolute `file:` URL of the module.
|
||||||
|
|
||||||
### Unsupported
|
## Differences Between ES Modules and CommonJS
|
||||||
|
|
||||||
| Feature | Reason |
|
### Mandatory file extensions
|
||||||
| --- | --- |
|
|
||||||
| `require('./foo.mjs')` | ES Modules have differing resolution and timing, use dynamic import |
|
|
||||||
|
|
||||||
## Notable differences between `import` and `require`
|
A file extension must be provided when using the `import` keyword. Directory
|
||||||
|
indexes (e.g. `'./startup/index.js'`) must also be fully specified.
|
||||||
|
|
||||||
### No NODE_PATH
|
This behavior matches how `import` behaves in browser environments, assuming a
|
||||||
|
typically configured server.
|
||||||
|
|
||||||
|
### No <code>NODE_PATH</code>
|
||||||
|
|
||||||
`NODE_PATH` is not part of resolving `import` specifiers. Please use symlinks
|
`NODE_PATH` is not part of resolving `import` specifiers. Please use symlinks
|
||||||
if this behavior is desired.
|
if this behavior is desired.
|
||||||
|
|
||||||
### No `require.extensions`
|
### No <code>require</code>, <code>exports</code>, <code>module.exports</code>, <code>\_\_filename</code>, <code>\_\_dirname</code>
|
||||||
|
|
||||||
|
These CommonJS variables are not available in ES modules.
|
||||||
|
|
||||||
|
`require` can be imported into an ES module using
|
||||||
|
[`module.createRequireFromPath()`][].
|
||||||
|
|
||||||
|
An equivalent for `__filename` and `__dirname` is [`import.meta.url`][].
|
||||||
|
|
||||||
|
### No <code>require.extensions</code>
|
||||||
|
|
||||||
`require.extensions` is not used by `import`. The expectation is that loader
|
`require.extensions` is not used by `import`. The expectation is that loader
|
||||||
hooks can provide this workflow in the future.
|
hooks can provide this workflow in the future.
|
||||||
|
|
||||||
### No `require.cache`
|
### No <code>require.cache</code>
|
||||||
|
|
||||||
`require.cache` is not used by `import`. It has a separate cache.
|
`require.cache` is not used by `import`. It has a separate cache.
|
||||||
|
|
||||||
### URL based paths
|
### URL-based paths
|
||||||
|
|
||||||
ESM are resolved and cached based upon [URL](https://url.spec.whatwg.org/)
|
ES modules are resolved and cached based upon
|
||||||
semantics. This means that files containing special characters such as `#` and
|
[URL](https://url.spec.whatwg.org/) semantics. This means that files containing
|
||||||
`?` need to be escaped.
|
special characters such as `#` and `?` need to be escaped.
|
||||||
|
|
||||||
Modules will be loaded multiple times if the `import` specifier used to resolve
|
Modules will be loaded multiple times if the `import` specifier used to resolve
|
||||||
them have a different query or fragment.
|
them have a different query or fragment.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import './foo?query=1'; // loads ./foo with query of "?query=1"
|
import './foo.mjs?query=1'; // loads ./foo.mjs with query of "?query=1"
|
||||||
import './foo?query=2'; // loads ./foo with query of "?query=2"
|
import './foo.mjs?query=2'; // loads ./foo.mjs with query of "?query=2"
|
||||||
```
|
```
|
||||||
|
|
||||||
For now, only modules using the `file:` protocol can be loaded.
|
For now, only modules using the `file:` protocol can be loaded.
|
||||||
|
|
||||||
## Interop with existing modules
|
## Interoperability with CommonJS
|
||||||
|
|
||||||
All CommonJS, JSON, and C++ modules can be used with `import`.
|
### <code>require</code>
|
||||||
|
|
||||||
Modules loaded this way will only be loaded once, even if their query
|
`require` always treats the files it references as CommonJS. This applies
|
||||||
or fragment string differs between `import` statements.
|
whether `require` is used the traditional way within a CommonJS environment, or
|
||||||
|
in an ES module environment using [`module.createRequireFromPath()`][].
|
||||||
|
|
||||||
When loaded via `import` these modules will provide a single `default` export
|
To include an ES module into CommonJS, use [`import()`][].
|
||||||
representing the value of `module.exports` at the time they finished evaluating.
|
|
||||||
|
### <code>import</code> statements
|
||||||
|
|
||||||
|
An `import` statement can reference either ES module or CommonJS JavaScript.
|
||||||
|
Other file types such as JSON and Native modules are not supported. For those,
|
||||||
|
use [`module.createRequireFromPath()`][].
|
||||||
|
|
||||||
|
`import` statements are permitted only in ES modules. For similar functionality
|
||||||
|
in CommonJS, see [`import()`][].
|
||||||
|
|
||||||
|
The _specifier_ of an `import` statement (the string after the `from` keyword)
|
||||||
|
can either be an URL-style relative path like `'./file.mjs'` or a package name
|
||||||
|
like `'fs'`.
|
||||||
|
|
||||||
|
Like in CommonJS, files within packages can be accessed by appending a path to
|
||||||
|
the package name.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// foo.js
|
import { sin, cos } from 'geometry/trigonometry-functions.mjs';
|
||||||
module.exports = { one: 1 };
|
|
||||||
|
|
||||||
// bar.mjs
|
|
||||||
import foo from './foo.js';
|
|
||||||
foo.one === 1; // true
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> Currently only the “default export” is supported for CommonJS files or
|
||||||
|
> packages:
|
||||||
|
>
|
||||||
|
> <!-- eslint-disable no-duplicate-imports -->
|
||||||
|
> ```js
|
||||||
|
> import packageMain from 'commonjs-package'; // Works
|
||||||
|
>
|
||||||
|
> import { method } from 'commonjs-package'; // Errors
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> There are ongoing efforts to make the latter code possible.
|
||||||
|
|
||||||
|
### <code>import()</code> expressions
|
||||||
|
|
||||||
|
Dynamic `import()` is supported in both CommonJS and ES modules. It can be used
|
||||||
|
to include ES module files from CommonJS code.
|
||||||
|
|
||||||
|
```js
|
||||||
|
(async () => {
|
||||||
|
await import('./my-app.mjs');
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
## CommonJS, JSON, and Native Modules
|
||||||
|
|
||||||
|
CommonJS, JSON, and Native modules can be used with [`module.createRequireFromPath()`][].
|
||||||
|
|
||||||
|
```js
|
||||||
|
// cjs.js
|
||||||
|
module.exports = 'cjs';
|
||||||
|
|
||||||
|
// esm.mjs
|
||||||
|
import { createRequireFromPath as createRequire } from 'module';
|
||||||
|
import { fileURLToPath as fromURL } from 'url';
|
||||||
|
|
||||||
|
const require = createRequire(fromURL(import.meta.url));
|
||||||
|
|
||||||
|
const cjs = require('./cjs');
|
||||||
|
cjs === 'cjs'; // true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Builtin modules
|
||||||
|
|
||||||
Builtin modules will provide named exports of their public API, as well as a
|
Builtin modules will provide named exports of their public API, as well as a
|
||||||
default export which can be used for, among other things, modifying the named
|
default export which can be used for, among other things, modifying the named
|
||||||
exports. Named exports of builtin modules are updated when the corresponding
|
exports. Named exports of builtin modules are updated when the corresponding
|
||||||
@ -132,7 +391,41 @@ fs.readFileSync = () => Buffer.from('Hello, ESM');
|
|||||||
fs.readFileSync === readFileSync;
|
fs.readFileSync === readFileSync;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Loader hooks
|
## Experimental JSON Modules
|
||||||
|
|
||||||
|
**Note: This API is still being designed and is subject to change.**
|
||||||
|
|
||||||
|
Currently importing JSON modules are only supported in the `commonjs` mode
|
||||||
|
and are loaded using the CJS loader. [WHATWG JSON modules][] are currently
|
||||||
|
being standardized, and are experimentally supported by including the
|
||||||
|
additional flag `--experimental-json-modules` when running Node.js.
|
||||||
|
|
||||||
|
When the `--experimental-json-modules` flag is included both the
|
||||||
|
`commonjs` and `module` mode will use the new experimental JSON
|
||||||
|
loader. The imported JSON only exposes a `default`, there is no
|
||||||
|
support for named exports. A cache entry is created in the CommonJS
|
||||||
|
cache, to avoid duplication. The same object will be returned in
|
||||||
|
CommonJS if the JSON module has already been imported from the
|
||||||
|
same path.
|
||||||
|
|
||||||
|
Assuming an `index.js` with
|
||||||
|
|
||||||
|
<!-- eslint-skip -->
|
||||||
|
```js
|
||||||
|
import packageConfig from './package.json';
|
||||||
|
```
|
||||||
|
|
||||||
|
The `--experimental-json-modules` flag is needed for the module
|
||||||
|
to work.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node --experimental-modules --entry-type=module index.js # fails
|
||||||
|
node --experimental-modules --entry-type=module --experimental-json-modules index.js # works
|
||||||
|
```
|
||||||
|
|
||||||
|
## Experimental Loader hooks
|
||||||
|
|
||||||
|
**Note: This API is currently being redesigned and will still change.**
|
||||||
|
|
||||||
<!-- type=misc -->
|
<!-- type=misc -->
|
||||||
|
|
||||||
@ -173,11 +466,10 @@ module. This can be one of the following:
|
|||||||
|
|
||||||
| `format` | Description |
|
| `format` | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `'esm'` | Load a standard JavaScript module |
|
| `'module'` | Load a standard JavaScript module |
|
||||||
| `'cjs'` | Load a node-style CommonJS module |
|
| `'commonjs'` | Load a Node.js CommonJS module |
|
||||||
| `'builtin'` | Load a node builtin CommonJS module |
|
| `'builtin'` | Load a Node.js builtin module |
|
||||||
| `'json'` | Load a JSON file |
|
| `'json'` | Load a JSON file |
|
||||||
| `'addon'` | Load a [C++ Addon][addons] |
|
|
||||||
| `'dynamic'` | Use a [dynamic instantiate hook][] |
|
| `'dynamic'` | Use a [dynamic instantiate hook][] |
|
||||||
|
|
||||||
For example, a dummy loader to load JavaScript restricted to browser resolution
|
For example, a dummy loader to load JavaScript restricted to browser resolution
|
||||||
@ -253,6 +545,184 @@ With the list of module exports provided upfront, the `execute` function will
|
|||||||
then be called at the exact point of module evaluation order for that module
|
then be called at the exact point of module evaluation order for that module
|
||||||
in the import tree.
|
in the import tree.
|
||||||
|
|
||||||
|
## Resolution Algorithm
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
The resolver has the following properties:
|
||||||
|
|
||||||
|
* FileURL-based resolution as is used by ES modules
|
||||||
|
* Support for builtin module loading
|
||||||
|
* Relative and absolute URL resolution
|
||||||
|
* No default extensions
|
||||||
|
* No folder mains
|
||||||
|
* Bare specifier package resolution lookup through node_modules
|
||||||
|
|
||||||
|
### Resolver Algorithm
|
||||||
|
|
||||||
|
The algorithm to load an ES module specifier is given through the
|
||||||
|
**ESM_RESOLVE** method below. It returns the resolved URL for a
|
||||||
|
module specifier relative to a parentURL, in addition to the unique module
|
||||||
|
format for that resolved URL given by the **ESM_FORMAT** routine.
|
||||||
|
|
||||||
|
The _"module"_ format is returned for an ECMAScript Module, while the
|
||||||
|
_"commonjs"_ format is used to indicate loading through the legacy
|
||||||
|
CommonJS loader. Additional formats such as _"wasm"_ or _"addon"_ can be
|
||||||
|
extended in future updates.
|
||||||
|
|
||||||
|
In the following algorithms, all subroutine errors are propagated as errors
|
||||||
|
of these top-level routines.
|
||||||
|
|
||||||
|
_isMain_ is **true** when resolving the Node.js application entry point.
|
||||||
|
|
||||||
|
When using the `--entry-type` flag, it overrides the ESM_FORMAT result while
|
||||||
|
providing errors in the case of explicit conflicts.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Resolver algorithm specification</summary>
|
||||||
|
|
||||||
|
**ESM_RESOLVE(_specifier_, _parentURL_, _isMain_)**
|
||||||
|
> 1. Let _resolvedURL_ be **undefined**.
|
||||||
|
> 1. If _specifier_ is a valid URL, then
|
||||||
|
> 1. Set _resolvedURL_ to the result of parsing and reserializing
|
||||||
|
> _specifier_ as a URL.
|
||||||
|
> 1. Otherwise, if _specifier_ starts with _"/"_, then
|
||||||
|
> 1. Throw an _Invalid Specifier_ error.
|
||||||
|
> 1. Otherwise, if _specifier_ starts with _"./"_ or _"../"_, then
|
||||||
|
> 1. Set _resolvedURL_ to the URL resolution of _specifier_ relative to
|
||||||
|
> _parentURL_.
|
||||||
|
> 1. Otherwise,
|
||||||
|
> 1. Note: _specifier_ is now a bare specifier.
|
||||||
|
> 1. Set _resolvedURL_ the result of
|
||||||
|
> **PACKAGE_RESOLVE**(_specifier_, _parentURL_).
|
||||||
|
> 1. If the file at _resolvedURL_ does not exist, then
|
||||||
|
> 1. Throw a _Module Not Found_ error.
|
||||||
|
> 1. Set _resolvedURL_ to the real path of _resolvedURL_.
|
||||||
|
> 1. Let _format_ be the result of **ESM_FORMAT**(_resolvedURL_, _isMain_).
|
||||||
|
> 1. Load _resolvedURL_ as module format, _format_.
|
||||||
|
|
||||||
|
PACKAGE_RESOLVE(_packageSpecifier_, _parentURL_)
|
||||||
|
> 1. Let _packageName_ be *undefined*.
|
||||||
|
> 1. Let _packageSubpath_ be *undefined*.
|
||||||
|
> 1. If _packageSpecifier_ is an empty string, then
|
||||||
|
> 1. Throw an _Invalid Specifier_ error.
|
||||||
|
> 1. If _packageSpecifier_ does not start with _"@"_, then
|
||||||
|
> 1. Set _packageName_ to the substring of _packageSpecifier_ until the
|
||||||
|
> first _"/"_ separator or the end of the string.
|
||||||
|
> 1. Otherwise,
|
||||||
|
> 1. If _packageSpecifier_ does not contain a _"/"_ separator, then
|
||||||
|
> 1. Throw an _Invalid Specifier_ error.
|
||||||
|
> 1. Set _packageName_ to the substring of _packageSpecifier_
|
||||||
|
> until the second _"/"_ separator or the end of the string.
|
||||||
|
> 1. Let _packageSubpath_ be the substring of _packageSpecifier_ from the
|
||||||
|
> position at the length of _packageName_ plus one, if any.
|
||||||
|
> 1. Assert: _packageName_ is a valid package name or scoped package name.
|
||||||
|
> 1. Assert: _packageSubpath_ is either empty, or a path without a leading
|
||||||
|
> separator.
|
||||||
|
> 1. If _packageSubpath_ contains any _"."_ or _".."_ segments or percent
|
||||||
|
> encoded strings for _"/"_ or _"\\"_ then,
|
||||||
|
> 1. Throw an _Invalid Specifier_ error.
|
||||||
|
> 1. If _packageSubpath_ is empty and _packageName_ is a Node.js builtin
|
||||||
|
> module, then
|
||||||
|
> 1. Return the string _"node:"_ concatenated with _packageSpecifier_.
|
||||||
|
> 1. While _parentURL_ is not the file system root,
|
||||||
|
> 1. Let _packageURL_ be the URL resolution of "node_modules/"
|
||||||
|
> concatenated with _packageSpecifier_, relative to _parentURL_.
|
||||||
|
> 1. Set _parentURL_ to the parent folder URL of _parentURL_.
|
||||||
|
> 1. If the folder at _packageURL_ does not exist, then
|
||||||
|
> 1. Set _parentURL_ to the parent URL path of _parentURL_.
|
||||||
|
> 1. Continue the next loop iteration.
|
||||||
|
> 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_packageURL_).
|
||||||
|
> 1. If _packageSubpath_ is empty, then
|
||||||
|
> 1. Return the result of **PACKAGE_MAIN_RESOLVE**(_packageURL_,
|
||||||
|
> _pjson_).
|
||||||
|
> 1. Otherwise,
|
||||||
|
> 1. Return the URL resolution of _packageSubpath_ in _packageURL_.
|
||||||
|
> 1. Throw a _Module Not Found_ error.
|
||||||
|
|
||||||
|
PACKAGE_MAIN_RESOLVE(_packageURL_, _pjson_)
|
||||||
|
> 1. If _pjson_ is **null**, then
|
||||||
|
> 1. Throw a _Module Not Found_ error.
|
||||||
|
> 1. If _pjson.main_ is a String, then
|
||||||
|
> 1. Let _resolvedMain_ be the concatenation of _packageURL_, "/", and
|
||||||
|
> _pjson.main_.
|
||||||
|
> 1. If the file at _resolvedMain_ exists, then
|
||||||
|
> 1. Return _resolvedMain_.
|
||||||
|
> 1. If _pjson.type_ is equal to _"module"_, then
|
||||||
|
> 1. Throw a _Module Not Found_ error.
|
||||||
|
> 1. Let _legacyMainURL_ be the result applying the legacy
|
||||||
|
> **LOAD_AS_DIRECTORY** CommonJS resolver to _packageURL_, throwing a
|
||||||
|
> _Module Not Found_ error for no resolution.
|
||||||
|
> 1. If _legacyMainURL_ does not end in _".js"_ then,
|
||||||
|
> 1. Throw an _Unsupported File Extension_ error.
|
||||||
|
> 1. Return _legacyMainURL_.
|
||||||
|
|
||||||
|
**ESM_FORMAT(_url_, _isMain_)**
|
||||||
|
> 1. Assert: _url_ corresponds to an existing file.
|
||||||
|
> 1. Let _pjson_ be the result of **READ_PACKAGE_SCOPE**(_url_).
|
||||||
|
> 1. If _url_ ends in _".mjs"_, then
|
||||||
|
> 1. Return _"module"_.
|
||||||
|
> 1. If _url_ ends in _".cjs"_, then
|
||||||
|
> 1. Return _"commonjs"_.
|
||||||
|
> 1. If _pjson?.type_ exists and is _"module"_, then
|
||||||
|
> 1. If _isMain_ is **true** or _url_ ends in _".js"_, then
|
||||||
|
> 1. Return _"module"_.
|
||||||
|
> 1. Throw an _Unsupported File Extension_ error.
|
||||||
|
> 1. Otherwise,
|
||||||
|
> 1. If _isMain_ is **true** or _url_ ends in _".js"_, _".json"_ or
|
||||||
|
> _".node"_, then
|
||||||
|
> 1. Return _"commonjs"_.
|
||||||
|
> 1. Throw an _Unsupported File Extension_ error.
|
||||||
|
|
||||||
|
READ_PACKAGE_SCOPE(_url_)
|
||||||
|
> 1. Let _scopeURL_ be _url_.
|
||||||
|
> 1. While _scopeURL_ is not the file system root,
|
||||||
|
> 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_scopeURL_).
|
||||||
|
> 1. If _pjson_ is not **null**, then
|
||||||
|
> 1. Return _pjson_.
|
||||||
|
> 1. Set _scopeURL_ to the parent URL of _scopeURL_.
|
||||||
|
> 1. Return **null**.
|
||||||
|
|
||||||
|
READ_PACKAGE_JSON(_packageURL_)
|
||||||
|
> 1. Let _pjsonURL_ be the resolution of _"package.json"_ within _packageURL_.
|
||||||
|
> 1. If the file at _pjsonURL_ does not exist, then
|
||||||
|
> 1. Return **null**.
|
||||||
|
> 1. If the file at _packageURL_ does not parse as valid JSON, then
|
||||||
|
> 1. Throw an _Invalid Package Configuration_ error.
|
||||||
|
> 1. Return the parsed JSON source of the file at _pjsonURL_.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Customizing ESM specifier resolution algorithm
|
||||||
|
|
||||||
|
The current specifier resolution does not support all default behavior of
|
||||||
|
the CommonJS loader. One of the behavior differences is automatic resolution
|
||||||
|
of file extensions and the ability to import directories that have an index
|
||||||
|
file.
|
||||||
|
|
||||||
|
The `--es-module-specifier-resolution=[mode]` flag can be used to customize
|
||||||
|
the extension resolution algorithm. The default mode is `explicit`, which
|
||||||
|
requires the full path to a module be provided to the loader. To enable the
|
||||||
|
automatic extension resolution and importing from directories that include an
|
||||||
|
index file use the `node` mode.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ node --experimental-modules index.mjs
|
||||||
|
success!
|
||||||
|
$ node --experimental-modules index #Failure!
|
||||||
|
Error: Cannot find module
|
||||||
|
$ node --experimental-modules --es-module-specifier-resolution=node index
|
||||||
|
success!
|
||||||
|
```
|
||||||
|
|
||||||
|
[`export`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
|
||||||
|
[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
|
||||||
|
[`import()`]: #esm_import-expressions
|
||||||
|
[`import.meta.url`]: #esm_import_meta
|
||||||
|
[`module.createRequireFromPath()`]: modules.html#modules_module_createrequirefrompath_filename
|
||||||
|
[CommonJS]: modules.html
|
||||||
|
[ECMAScript-modules implementation]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md
|
||||||
[Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
|
[Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
|
||||||
[addons]: addons.html
|
[WHATWG JSON modules]: https://github.com/whatwg/html/issues/4315
|
||||||
[dynamic instantiate hook]: #esm_dynamic_instantiate_hook
|
[dynamic instantiate hook]: #esm_dynamic_instantiate_hook
|
||||||
|
[the official standard format]: https://tc39.github.io/ecma262/#sec-modules
|
||||||
|
@ -119,6 +119,15 @@ Enable FIPS-compliant crypto at startup.
|
|||||||
Requires Node.js to be built with
|
Requires Node.js to be built with
|
||||||
.Sy ./configure --openssl-fips .
|
.Sy ./configure --openssl-fips .
|
||||||
.
|
.
|
||||||
|
.It Fl -entry-type Ns = Ns Ar type
|
||||||
|
Set the top-level module resolution type.
|
||||||
|
.
|
||||||
|
.It Fl -es-module-specifier-resolution
|
||||||
|
Select extension resolution algorithm for ES Modules; either 'explicit' (default) or 'node'
|
||||||
|
.
|
||||||
|
.It Fl -experimental-json-modules
|
||||||
|
Enable experimental JSON interop support for the ES Module loader.
|
||||||
|
.
|
||||||
.It Fl -experimental-modules
|
.It Fl -experimental-modules
|
||||||
Enable experimental ES module support and caching modules.
|
Enable experimental ES module support and caching modules.
|
||||||
.
|
.
|
||||||
|
@ -323,6 +323,10 @@ function initializeESMLoader() {
|
|||||||
const userLoader = getOptionValue('--loader');
|
const userLoader = getOptionValue('--loader');
|
||||||
// If --loader is specified, create a loader with user hooks. Otherwise
|
// If --loader is specified, create a loader with user hooks. Otherwise
|
||||||
// create the default loader.
|
// create the default loader.
|
||||||
|
if (userLoader) {
|
||||||
|
const { emitExperimentalWarning } = require('internal/util');
|
||||||
|
emitExperimentalWarning('--loader');
|
||||||
|
}
|
||||||
esm.initializeLoader(process.cwd(), userLoader);
|
esm.initializeLoader(process.cwd(), userLoader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -678,6 +678,24 @@ E('ERR_ENCODING_INVALID_ENCODED_DATA', function(encoding, ret) {
|
|||||||
}, TypeError);
|
}, TypeError);
|
||||||
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported',
|
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported',
|
||||||
RangeError);
|
RangeError);
|
||||||
|
E('ERR_ENTRY_TYPE_MISMATCH', (filename, ext, typeFlag, conflict) => {
|
||||||
|
const typeString =
|
||||||
|
typeFlag === 'module' ? '--entry-type=module' : '--entry-type=commonjs';
|
||||||
|
// --entry-type mismatches file extension
|
||||||
|
if (conflict === 'extension') {
|
||||||
|
return `Extension ${ext} is not supported for ` +
|
||||||
|
`${typeString} loading ${filename}`;
|
||||||
|
}
|
||||||
|
assert(
|
||||||
|
conflict === 'scope',
|
||||||
|
'"conflict" value unknown. Set this argument to "extension" or "scope"'
|
||||||
|
);
|
||||||
|
// --entry-type mismatches package.json "type"
|
||||||
|
return `Cannot use ${typeString} because nearest parent package.json ` +
|
||||||
|
((typeFlag === 'module') ?
|
||||||
|
'includes "type": "commonjs"' : 'includes "type": "module",') +
|
||||||
|
` which controls the type to use for ${filename}`;
|
||||||
|
}, TypeError);
|
||||||
E('ERR_FALSY_VALUE_REJECTION', function(reason) {
|
E('ERR_FALSY_VALUE_REJECTION', function(reason) {
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
return 'Promise was rejected with falsy value';
|
return 'Promise was rejected with falsy value';
|
||||||
@ -864,6 +882,8 @@ E('ERR_INVALID_OPT_VALUE', (name, value) =>
|
|||||||
RangeError);
|
RangeError);
|
||||||
E('ERR_INVALID_OPT_VALUE_ENCODING',
|
E('ERR_INVALID_OPT_VALUE_ENCODING',
|
||||||
'The value "%s" is invalid for option "encoding"', TypeError);
|
'The value "%s" is invalid for option "encoding"', TypeError);
|
||||||
|
E('ERR_INVALID_PACKAGE_CONFIG',
|
||||||
|
'Invalid package config in \'%s\' imported from %s', Error);
|
||||||
E('ERR_INVALID_PERFORMANCE_MARK',
|
E('ERR_INVALID_PERFORMANCE_MARK',
|
||||||
'The "%s" performance mark has not been set', Error);
|
'The "%s" performance mark has not been set', Error);
|
||||||
E('ERR_INVALID_PROTOCOL',
|
E('ERR_INVALID_PROTOCOL',
|
||||||
@ -958,11 +978,6 @@ E('ERR_MISSING_ARGS',
|
|||||||
E('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK',
|
E('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK',
|
||||||
'The ES Module loader may not return a format of \'dynamic\' when no ' +
|
'The ES Module loader may not return a format of \'dynamic\' when no ' +
|
||||||
'dynamicInstantiate function was provided', Error);
|
'dynamicInstantiate function was provided', Error);
|
||||||
E('ERR_MISSING_MODULE', 'Cannot find module %s', Error);
|
|
||||||
E('ERR_MODULE_RESOLUTION_LEGACY',
|
|
||||||
'%s not found by import in %s.' +
|
|
||||||
' Legacy behavior in require() would have found it at %s',
|
|
||||||
Error);
|
|
||||||
E('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times', Error);
|
E('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times', Error);
|
||||||
E('ERR_NAPI_CONS_FUNCTION', 'Constructor must be a function', TypeError);
|
E('ERR_NAPI_CONS_FUNCTION', 'Constructor must be a function', TypeError);
|
||||||
E('ERR_NAPI_INVALID_DATAVIEW_ARGS',
|
E('ERR_NAPI_INVALID_DATAVIEW_ARGS',
|
||||||
@ -1067,9 +1082,7 @@ E('ERR_UNHANDLED_ERROR',
|
|||||||
E('ERR_UNKNOWN_BUILTIN_MODULE', 'No such built-in module: %s', Error);
|
E('ERR_UNKNOWN_BUILTIN_MODULE', 'No such built-in module: %s', Error);
|
||||||
E('ERR_UNKNOWN_CREDENTIAL', '%s identifier does not exist: %s', Error);
|
E('ERR_UNKNOWN_CREDENTIAL', '%s identifier does not exist: %s', Error);
|
||||||
E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError);
|
E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError);
|
||||||
|
E('ERR_UNKNOWN_FILE_EXTENSION', 'Unknown file extension: %s', TypeError);
|
||||||
// This should probably be a `TypeError`.
|
|
||||||
E('ERR_UNKNOWN_FILE_EXTENSION', 'Unknown file extension: %s', Error);
|
|
||||||
E('ERR_UNKNOWN_MODULE_FORMAT', 'Unknown module format: %s', RangeError);
|
E('ERR_UNKNOWN_MODULE_FORMAT', 'Unknown module format: %s', RangeError);
|
||||||
E('ERR_UNKNOWN_SIGNAL', 'Unknown signal: %s', TypeError);
|
E('ERR_UNKNOWN_SIGNAL', 'Unknown signal: %s', TypeError);
|
||||||
|
|
||||||
|
@ -11,12 +11,18 @@ const {
|
|||||||
readStdin
|
readStdin
|
||||||
} = require('internal/process/execution');
|
} = require('internal/process/execution');
|
||||||
|
|
||||||
const CJSModule = require('internal/modules/cjs/loader');
|
const { pathToFileURL } = require('url');
|
||||||
|
|
||||||
const vm = require('vm');
|
const vm = require('vm');
|
||||||
const {
|
const {
|
||||||
stripShebang, stripBOM
|
stripShebang, stripBOM
|
||||||
} = require('internal/modules/cjs/helpers');
|
} = require('internal/modules/cjs/helpers');
|
||||||
|
|
||||||
|
let CJSModule;
|
||||||
|
function CJSModuleInit() {
|
||||||
|
if (!CJSModule)
|
||||||
|
CJSModule = require('internal/modules/cjs/loader');
|
||||||
|
}
|
||||||
|
|
||||||
if (process.argv[1] && process.argv[1] !== '-') {
|
if (process.argv[1] && process.argv[1] !== '-') {
|
||||||
// Expand process.argv[1] into a full path.
|
// Expand process.argv[1] into a full path.
|
||||||
@ -25,7 +31,7 @@ if (process.argv[1] && process.argv[1] !== '-') {
|
|||||||
|
|
||||||
// TODO(joyeecheung): not every one of these are necessary
|
// TODO(joyeecheung): not every one of these are necessary
|
||||||
prepareMainThreadExecution();
|
prepareMainThreadExecution();
|
||||||
|
CJSModuleInit();
|
||||||
// Read the source.
|
// Read the source.
|
||||||
const filename = CJSModule._resolveFilename(process.argv[1]);
|
const filename = CJSModule._resolveFilename(process.argv[1]);
|
||||||
|
|
||||||
@ -34,20 +40,40 @@ if (process.argv[1] && process.argv[1] !== '-') {
|
|||||||
|
|
||||||
markBootstrapComplete();
|
markBootstrapComplete();
|
||||||
|
|
||||||
checkScriptSyntax(source, filename);
|
checkSyntax(source, filename);
|
||||||
} else {
|
} else {
|
||||||
// TODO(joyeecheung): not every one of these are necessary
|
// TODO(joyeecheung): not every one of these are necessary
|
||||||
prepareMainThreadExecution();
|
prepareMainThreadExecution();
|
||||||
|
CJSModuleInit();
|
||||||
markBootstrapComplete();
|
markBootstrapComplete();
|
||||||
|
|
||||||
readStdin((code) => {
|
readStdin((code) => {
|
||||||
checkScriptSyntax(code, '[stdin]');
|
checkSyntax(code, '[stdin]');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkScriptSyntax(source, filename) {
|
function checkSyntax(source, filename) {
|
||||||
// Remove Shebang.
|
// Remove Shebang.
|
||||||
source = stripShebang(source);
|
source = stripShebang(source);
|
||||||
|
|
||||||
|
const { getOptionValue } = require('internal/options');
|
||||||
|
const experimentalModules = getOptionValue('--experimental-modules');
|
||||||
|
if (experimentalModules) {
|
||||||
|
let isModule = false;
|
||||||
|
if (filename === '[stdin]' || filename === '[eval]') {
|
||||||
|
isModule = getOptionValue('--entry-type') === 'module';
|
||||||
|
} else {
|
||||||
|
const resolve = require('internal/modules/esm/default_resolve');
|
||||||
|
const { format } = resolve(pathToFileURL(filename).toString());
|
||||||
|
isModule = format === 'module';
|
||||||
|
}
|
||||||
|
if (isModule) {
|
||||||
|
const { ModuleWrap } = internalBinding('module_wrap');
|
||||||
|
new ModuleWrap(source, filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove BOM.
|
// Remove BOM.
|
||||||
source = stripBOM(source);
|
source = stripBOM(source);
|
||||||
// Wrap it.
|
// Wrap it.
|
||||||
|
@ -7,6 +7,7 @@ const {
|
|||||||
} = require('internal/bootstrap/pre_execution');
|
} = require('internal/bootstrap/pre_execution');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
evalModule,
|
||||||
evalScript,
|
evalScript,
|
||||||
readStdin
|
readStdin
|
||||||
} = require('internal/process/execution');
|
} = require('internal/process/execution');
|
||||||
@ -16,5 +17,8 @@ markBootstrapComplete();
|
|||||||
|
|
||||||
readStdin((code) => {
|
readStdin((code) => {
|
||||||
process._eval = code;
|
process._eval = code;
|
||||||
evalScript('[stdin]', process._eval, process._breakFirstLine);
|
if (require('internal/options').getOptionValue('--entry-type') === 'module')
|
||||||
|
evalModule(process._eval);
|
||||||
|
else
|
||||||
|
evalScript('[stdin]', process._eval, process._breakFirstLine);
|
||||||
});
|
});
|
||||||
|
@ -6,11 +6,15 @@
|
|||||||
const {
|
const {
|
||||||
prepareMainThreadExecution
|
prepareMainThreadExecution
|
||||||
} = require('internal/bootstrap/pre_execution');
|
} = require('internal/bootstrap/pre_execution');
|
||||||
const { evalScript } = require('internal/process/execution');
|
const { evalModule, evalScript } = require('internal/process/execution');
|
||||||
const { addBuiltinLibsToObject } = require('internal/modules/cjs/helpers');
|
const { addBuiltinLibsToObject } = require('internal/modules/cjs/helpers');
|
||||||
|
|
||||||
const source = require('internal/options').getOptionValue('--eval');
|
const { getOptionValue } = require('internal/options');
|
||||||
|
const source = getOptionValue('--eval');
|
||||||
prepareMainThreadExecution();
|
prepareMainThreadExecution();
|
||||||
addBuiltinLibsToObject(global);
|
addBuiltinLibsToObject(global);
|
||||||
markBootstrapComplete();
|
markBootstrapComplete();
|
||||||
evalScript('[eval]', source, process._breakFirstLine);
|
if (getOptionValue('--entry-type') === 'module')
|
||||||
|
evalModule(source);
|
||||||
|
else
|
||||||
|
evalScript('[eval]', source, process._breakFirstLine);
|
||||||
|
@ -13,6 +13,12 @@ const {
|
|||||||
|
|
||||||
prepareMainThreadExecution();
|
prepareMainThreadExecution();
|
||||||
|
|
||||||
|
// --entry-type flag not supported in REPL
|
||||||
|
if (require('internal/options').getOptionValue('--entry-type')) {
|
||||||
|
console.error('Cannot specify --entry-type for REPL');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
const cliRepl = require('internal/repl');
|
const cliRepl = require('internal/repl');
|
||||||
cliRepl.createInternalRepl(process.env, (err, repl) => {
|
cliRepl.createInternalRepl(process.env, (err, repl) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -896,9 +896,11 @@ Module.runMain = function() {
|
|||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
internalBinding('task_queue').triggerFatalException(e);
|
internalBinding('task_queue').triggerFatalException(e);
|
||||||
});
|
});
|
||||||
} else {
|
// Handle any nextTicks added in the first tick of the program
|
||||||
Module._load(process.argv[1], null, true);
|
process._tickCallback();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
Module._load(process.argv[1], null, true);
|
||||||
// Handle any nextTicks added in the first tick of the program
|
// Handle any nextTicks added in the first tick of the program
|
||||||
process._tickCallback();
|
process._tickCallback();
|
||||||
};
|
};
|
||||||
|
@ -1,57 +1,54 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { URL } = require('url');
|
|
||||||
const CJSmodule = require('internal/modules/cjs/loader');
|
|
||||||
const internalFS = require('internal/fs/utils');
|
const internalFS = require('internal/fs/utils');
|
||||||
const { NativeModule } = require('internal/bootstrap/loaders');
|
const { NativeModule } = require('internal/bootstrap/loaders');
|
||||||
const { extname } = require('path');
|
const { extname } = require('path');
|
||||||
const { realpathSync } = require('fs');
|
const { realpathSync } = require('fs');
|
||||||
const { getOptionValue } = require('internal/options');
|
const { getOptionValue } = require('internal/options');
|
||||||
|
|
||||||
const preserveSymlinks = getOptionValue('--preserve-symlinks');
|
const preserveSymlinks = getOptionValue('--preserve-symlinks');
|
||||||
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
|
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
|
||||||
const {
|
const experimentalJsonModules = getOptionValue('--experimental-json-modules');
|
||||||
ERR_MISSING_MODULE,
|
const typeFlag = getOptionValue('--entry-type');
|
||||||
ERR_MODULE_RESOLUTION_LEGACY,
|
|
||||||
ERR_UNKNOWN_FILE_EXTENSION
|
const { resolve: moduleWrapResolve,
|
||||||
} = require('internal/errors').codes;
|
getPackageType } = internalBinding('module_wrap');
|
||||||
const { resolve: moduleWrapResolve } = internalBinding('module_wrap');
|
|
||||||
const StringStartsWith = Function.call.bind(String.prototype.startsWith);
|
|
||||||
const { pathToFileURL, fileURLToPath } = require('internal/url');
|
const { pathToFileURL, fileURLToPath } = require('internal/url');
|
||||||
|
const { ERR_ENTRY_TYPE_MISMATCH,
|
||||||
|
ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes;
|
||||||
|
|
||||||
const realpathCache = new Map();
|
const realpathCache = new Map();
|
||||||
|
|
||||||
function search(target, base) {
|
// const TYPE_NONE = 0;
|
||||||
if (base === undefined) {
|
const TYPE_COMMONJS = 1;
|
||||||
// We cannot search without a base.
|
const TYPE_MODULE = 2;
|
||||||
throw new ERR_MISSING_MODULE(target);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return moduleWrapResolve(target, base);
|
|
||||||
} catch (e) {
|
|
||||||
e.stack; // cause V8 to generate stack before rethrow
|
|
||||||
let error = e;
|
|
||||||
try {
|
|
||||||
const questionedBase = new URL(base);
|
|
||||||
const tmpMod = new CJSmodule(questionedBase.pathname, null);
|
|
||||||
tmpMod.paths = CJSmodule._nodeModulePaths(
|
|
||||||
new URL('./', questionedBase).pathname);
|
|
||||||
const found = CJSmodule._resolveFilename(target, tmpMod);
|
|
||||||
error = new ERR_MODULE_RESOLUTION_LEGACY(target, base, found);
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const extensionFormatMap = {
|
const extensionFormatMap = {
|
||||||
'__proto__': null,
|
'__proto__': null,
|
||||||
'.mjs': 'esm',
|
'.cjs': 'commonjs',
|
||||||
'.json': 'json',
|
'.js': 'module',
|
||||||
'.node': 'addon',
|
'.mjs': 'module'
|
||||||
'.js': 'cjs'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const legacyExtensionFormatMap = {
|
||||||
|
'__proto__': null,
|
||||||
|
'.cjs': 'commonjs',
|
||||||
|
'.js': 'commonjs',
|
||||||
|
'.json': 'commonjs',
|
||||||
|
'.mjs': 'module',
|
||||||
|
'.node': 'commonjs'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (experimentalJsonModules) {
|
||||||
|
// This is a total hack
|
||||||
|
Object.assign(extensionFormatMap, {
|
||||||
|
'.json': 'json'
|
||||||
|
});
|
||||||
|
Object.assign(legacyExtensionFormatMap, {
|
||||||
|
'.json': 'json'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function resolve(specifier, parentURL) {
|
function resolve(specifier, parentURL) {
|
||||||
if (NativeModule.canBeRequiredByUsers(specifier)) {
|
if (NativeModule.canBeRequiredByUsers(specifier)) {
|
||||||
return {
|
return {
|
||||||
@ -60,21 +57,11 @@ function resolve(specifier, parentURL) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let url;
|
|
||||||
try {
|
|
||||||
url = search(specifier,
|
|
||||||
parentURL || pathToFileURL(`${process.cwd()}/`).href);
|
|
||||||
} catch (e) {
|
|
||||||
if (typeof e.message === 'string' &&
|
|
||||||
StringStartsWith(e.message, 'Cannot find module')) {
|
|
||||||
e.code = 'MODULE_NOT_FOUND';
|
|
||||||
// TODO: also add e.requireStack to match behavior with CJS
|
|
||||||
// MODULE_NOT_FOUND.
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isMain = parentURL === undefined;
|
const isMain = parentURL === undefined;
|
||||||
|
if (isMain)
|
||||||
|
parentURL = pathToFileURL(`${process.cwd()}/`).href;
|
||||||
|
|
||||||
|
let url = moduleWrapResolve(specifier, parentURL);
|
||||||
|
|
||||||
if (isMain ? !preserveSymlinksMain : !preserveSymlinks) {
|
if (isMain ? !preserveSymlinksMain : !preserveSymlinks) {
|
||||||
const real = realpathSync(fileURLToPath(url), {
|
const real = realpathSync(fileURLToPath(url), {
|
||||||
@ -86,19 +73,40 @@ function resolve(specifier, parentURL) {
|
|||||||
url.hash = old.hash;
|
url.hash = old.hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const type = getPackageType(url.href);
|
||||||
|
|
||||||
const ext = extname(url.pathname);
|
const ext = extname(url.pathname);
|
||||||
|
const extMap =
|
||||||
|
type !== TYPE_MODULE ? legacyExtensionFormatMap : extensionFormatMap;
|
||||||
|
let format = extMap[ext];
|
||||||
|
|
||||||
let format = extensionFormatMap[ext];
|
if (isMain && typeFlag) {
|
||||||
if (!format) {
|
// Conflict between explicit extension (.mjs, .cjs) and --entry-type
|
||||||
if (isMain)
|
if (ext === '.cjs' && typeFlag === 'module' ||
|
||||||
format = 'cjs';
|
ext === '.mjs' && typeFlag === 'commonjs') {
|
||||||
else
|
throw new ERR_ENTRY_TYPE_MISMATCH(
|
||||||
throw new ERR_UNKNOWN_FILE_EXTENSION(url.pathname);
|
fileURLToPath(url), ext, typeFlag, 'extension');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conflict between package scope type and --entry-type
|
||||||
|
if (ext === '.js') {
|
||||||
|
if (type === TYPE_MODULE && typeFlag === 'commonjs' ||
|
||||||
|
type === TYPE_COMMONJS && typeFlag === 'module') {
|
||||||
|
throw new ERR_ENTRY_TYPE_MISMATCH(
|
||||||
|
fileURLToPath(url), ext, typeFlag, 'scope');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!format) {
|
||||||
|
if (isMain && typeFlag)
|
||||||
|
format = typeFlag;
|
||||||
|
else if (isMain)
|
||||||
|
format = type === TYPE_MODULE ? 'module' : 'commonjs';
|
||||||
|
else
|
||||||
|
throw new ERR_UNKNOWN_FILE_EXTENSION(fileURLToPath(url),
|
||||||
|
fileURLToPath(parentURL));
|
||||||
}
|
}
|
||||||
|
|
||||||
return { url: `${url}`, format };
|
return { url: `${url}`, format };
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = resolve;
|
module.exports = resolve;
|
||||||
// exported for tests
|
|
||||||
module.exports.search = search;
|
|
||||||
|
@ -11,10 +11,12 @@ const { URL } = require('url');
|
|||||||
const { validateString } = require('internal/validators');
|
const { validateString } = require('internal/validators');
|
||||||
const ModuleMap = require('internal/modules/esm/module_map');
|
const ModuleMap = require('internal/modules/esm/module_map');
|
||||||
const ModuleJob = require('internal/modules/esm/module_job');
|
const ModuleJob = require('internal/modules/esm/module_job');
|
||||||
|
|
||||||
const defaultResolve = require('internal/modules/esm/default_resolve');
|
const defaultResolve = require('internal/modules/esm/default_resolve');
|
||||||
const createDynamicModule = require(
|
const createDynamicModule = require(
|
||||||
'internal/modules/esm/create_dynamic_module');
|
'internal/modules/esm/create_dynamic_module');
|
||||||
const translators = require('internal/modules/esm/translators');
|
const { translators } = require('internal/modules/esm/translators');
|
||||||
|
const { ModuleWrap } = internalBinding('module_wrap');
|
||||||
|
|
||||||
const FunctionBind = Function.call.bind(Function.prototype.bind);
|
const FunctionBind = Function.call.bind(Function.prototype.bind);
|
||||||
|
|
||||||
@ -32,6 +34,9 @@ class Loader {
|
|||||||
// Registry of loaded modules, akin to `require.cache`
|
// Registry of loaded modules, akin to `require.cache`
|
||||||
this.moduleMap = new ModuleMap();
|
this.moduleMap = new ModuleMap();
|
||||||
|
|
||||||
|
// Map of already-loaded CJS modules to use
|
||||||
|
this.cjsCache = new Map();
|
||||||
|
|
||||||
// The resolver has the signature
|
// The resolver has the signature
|
||||||
// (specifier : string, parentURL : string, defaultResolve)
|
// (specifier : string, parentURL : string, defaultResolve)
|
||||||
// -> Promise<{ url : string, format: string }>
|
// -> Promise<{ url : string, format: string }>
|
||||||
@ -48,6 +53,8 @@ class Loader {
|
|||||||
// an object with the same keys as `exports`, whose values are get/set
|
// an object with the same keys as `exports`, whose values are get/set
|
||||||
// functions for the actual exported values.
|
// functions for the actual exported values.
|
||||||
this._dynamicInstantiate = undefined;
|
this._dynamicInstantiate = undefined;
|
||||||
|
// The index for assigning unique URLs to anonymous module evaluation
|
||||||
|
this.evalIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolve(specifier, parentURL) {
|
async resolve(specifier, parentURL) {
|
||||||
@ -95,9 +102,25 @@ class Loader {
|
|||||||
return { url, format };
|
return { url, format };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async eval(source, url = `eval:${++this.evalIndex}`) {
|
||||||
|
const evalInstance = async (url) => {
|
||||||
|
return {
|
||||||
|
module: new ModuleWrap(source, url),
|
||||||
|
reflect: undefined
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const job = new ModuleJob(this, url, evalInstance, false);
|
||||||
|
this.moduleMap.set(url, job);
|
||||||
|
const { module, result } = await job.run();
|
||||||
|
return {
|
||||||
|
namespace: module.namespace(),
|
||||||
|
result
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async import(specifier, parent) {
|
async import(specifier, parent) {
|
||||||
const job = await this.getModuleJob(specifier, parent);
|
const job = await this.getModuleJob(specifier, parent);
|
||||||
const module = await job.run();
|
const { module } = await job.run();
|
||||||
return module.namespace();
|
return module.namespace();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,4 +166,4 @@ class Loader {
|
|||||||
|
|
||||||
Object.setPrototypeOf(Loader.prototype, null);
|
Object.setPrototypeOf(Loader.prototype, null);
|
||||||
|
|
||||||
module.exports = Loader;
|
exports.Loader = Loader;
|
||||||
|
@ -23,7 +23,7 @@ class ModuleJob {
|
|||||||
|
|
||||||
// This is a Promise<{ module, reflect }>, whose fields will be copied
|
// This is a Promise<{ module, reflect }>, whose fields will be copied
|
||||||
// onto `this` by `link()` below once it has been resolved.
|
// onto `this` by `link()` below once it has been resolved.
|
||||||
this.modulePromise = moduleProvider(url, isMain);
|
this.modulePromise = moduleProvider.call(loader, url, isMain);
|
||||||
this.module = undefined;
|
this.module = undefined;
|
||||||
this.reflect = undefined;
|
this.reflect = undefined;
|
||||||
|
|
||||||
@ -101,8 +101,9 @@ class ModuleJob {
|
|||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
const module = await this.instantiate();
|
const module = await this.instantiate();
|
||||||
module.evaluate(-1, false);
|
const timeout = -1;
|
||||||
return module;
|
const breakOnSigint = false;
|
||||||
|
return { module, result: module.evaluate(timeout, breakOnSigint) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Object.setPrototypeOf(ModuleJob.prototype, null);
|
Object.setPrototypeOf(ModuleJob.prototype, null);
|
||||||
|
@ -11,14 +11,10 @@ const internalURLModule = require('internal/url');
|
|||||||
const createDynamicModule = require(
|
const createDynamicModule = require(
|
||||||
'internal/modules/esm/create_dynamic_module');
|
'internal/modules/esm/create_dynamic_module');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { _makeLong } = require('path');
|
|
||||||
const {
|
const {
|
||||||
SafeMap,
|
SafeMap,
|
||||||
JSON,
|
|
||||||
FunctionPrototype,
|
|
||||||
StringPrototype
|
|
||||||
} = primordials;
|
} = primordials;
|
||||||
const { URL } = require('url');
|
const { fileURLToPath, URL } = require('url');
|
||||||
const { debuglog } = require('internal/util/debuglog');
|
const { debuglog } = require('internal/util/debuglog');
|
||||||
const { promisify } = require('internal/util');
|
const { promisify } = require('internal/util');
|
||||||
const esmLoader = require('internal/process/esm_loader');
|
const esmLoader = require('internal/process/esm_loader');
|
||||||
@ -26,14 +22,13 @@ const {
|
|||||||
ERR_UNKNOWN_BUILTIN_MODULE
|
ERR_UNKNOWN_BUILTIN_MODULE
|
||||||
} = require('internal/errors').codes;
|
} = require('internal/errors').codes;
|
||||||
const readFileAsync = promisify(fs.readFile);
|
const readFileAsync = promisify(fs.readFile);
|
||||||
const readFileSync = fs.readFileSync;
|
const StringReplace = Function.call.bind(String.prototype.replace);
|
||||||
const StringReplace = FunctionPrototype.call.bind(StringPrototype.replace);
|
|
||||||
const JsonParse = JSON.parse;
|
const JsonParse = JSON.parse;
|
||||||
|
|
||||||
const debug = debuglog('esm');
|
const debug = debuglog('esm');
|
||||||
|
|
||||||
const translators = new SafeMap();
|
const translators = new SafeMap();
|
||||||
module.exports = translators;
|
exports.translators = translators;
|
||||||
|
|
||||||
function initializeImportMeta(meta, { url }) {
|
function initializeImportMeta(meta, { url }) {
|
||||||
meta.url = url;
|
meta.url = url;
|
||||||
@ -45,7 +40,7 @@ async function importModuleDynamically(specifier, { url }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Strategy for loading a standard JavaScript module
|
// Strategy for loading a standard JavaScript module
|
||||||
translators.set('esm', async (url) => {
|
translators.set('module', async function moduleStrategy(url) {
|
||||||
const source = `${await readFileAsync(new URL(url))}`;
|
const source = `${await readFileAsync(new URL(url))}`;
|
||||||
debug(`Translating StandardModule ${url}`);
|
debug(`Translating StandardModule ${url}`);
|
||||||
const module = new ModuleWrap(stripShebang(source), url);
|
const module = new ModuleWrap(stripShebang(source), url);
|
||||||
@ -62,9 +57,14 @@ translators.set('esm', async (url) => {
|
|||||||
// Strategy for loading a node-style CommonJS module
|
// Strategy for loading a node-style CommonJS module
|
||||||
const isWindows = process.platform === 'win32';
|
const isWindows = process.platform === 'win32';
|
||||||
const winSepRegEx = /\//g;
|
const winSepRegEx = /\//g;
|
||||||
translators.set('cjs', async (url, isMain) => {
|
translators.set('commonjs', async function commonjsStrategy(url, isMain) {
|
||||||
debug(`Translating CJSModule ${url}`);
|
debug(`Translating CJSModule ${url}`);
|
||||||
const pathname = internalURLModule.fileURLToPath(new URL(url));
|
const pathname = internalURLModule.fileURLToPath(new URL(url));
|
||||||
|
const cached = this.cjsCache.get(url);
|
||||||
|
if (cached) {
|
||||||
|
this.cjsCache.delete(url);
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
const module = CJSModule._cache[
|
const module = CJSModule._cache[
|
||||||
isWindows ? StringReplace(pathname, winSepRegEx, '\\') : pathname];
|
isWindows ? StringReplace(pathname, winSepRegEx, '\\') : pathname];
|
||||||
if (module && module.loaded) {
|
if (module && module.loaded) {
|
||||||
@ -84,7 +84,7 @@ translators.set('cjs', async (url, isMain) => {
|
|||||||
|
|
||||||
// Strategy for loading a node builtin CommonJS module that isn't
|
// Strategy for loading a node builtin CommonJS module that isn't
|
||||||
// through normal resolution
|
// through normal resolution
|
||||||
translators.set('builtin', async (url) => {
|
translators.set('builtin', async function builtinStrategy(url) {
|
||||||
debug(`Translating BuiltinModule ${url}`);
|
debug(`Translating BuiltinModule ${url}`);
|
||||||
// Slice 'node:' scheme
|
// Slice 'node:' scheme
|
||||||
const id = url.slice(5);
|
const id = url.slice(5);
|
||||||
@ -103,31 +103,38 @@ translators.set('builtin', async (url) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Strategy for loading a node native module
|
// Strategy for loading a JSON file
|
||||||
translators.set('addon', async (url) => {
|
translators.set('json', async function jsonStrategy(url) {
|
||||||
debug(`Translating NativeModule ${url}`);
|
debug(`Translating JSONModule ${url}`);
|
||||||
|
debug(`Loading JSONModule ${url}`);
|
||||||
|
const pathname = fileURLToPath(url);
|
||||||
|
const modulePath = isWindows ?
|
||||||
|
StringReplace(pathname, winSepRegEx, '\\') : pathname;
|
||||||
|
let module = CJSModule._cache[modulePath];
|
||||||
|
if (module && module.loaded) {
|
||||||
|
const exports = module.exports;
|
||||||
|
return createDynamicModule(['default'], url, (reflect) => {
|
||||||
|
reflect.exports.default.set(exports);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const content = await readFileAsync(pathname, 'utf-8');
|
||||||
|
try {
|
||||||
|
const exports = JsonParse(stripBOM(content));
|
||||||
|
module = {
|
||||||
|
exports,
|
||||||
|
loaded: true
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
// TODO (BridgeAR): We could add a NodeCore error that wraps the JSON
|
||||||
|
// parse error instead of just manipulating the original error message.
|
||||||
|
// That would allow to add further properties and maybe additional
|
||||||
|
// debugging information.
|
||||||
|
err.message = pathname + ': ' + err.message;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
CJSModule._cache[modulePath] = module;
|
||||||
return createDynamicModule(['default'], url, (reflect) => {
|
return createDynamicModule(['default'], url, (reflect) => {
|
||||||
debug(`Loading NativeModule ${url}`);
|
debug(`Parsing JSONModule ${url}`);
|
||||||
const module = { exports: {} };
|
|
||||||
const pathname = internalURLModule.fileURLToPath(new URL(url));
|
|
||||||
process.dlopen(module, _makeLong(pathname));
|
|
||||||
reflect.exports.default.set(module.exports);
|
reflect.exports.default.set(module.exports);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Strategy for loading a JSON file
|
|
||||||
translators.set('json', async (url) => {
|
|
||||||
debug(`Translating JSONModule ${url}`);
|
|
||||||
return createDynamicModule(['default'], url, (reflect) => {
|
|
||||||
debug(`Loading JSONModule ${url}`);
|
|
||||||
const pathname = internalURLModule.fileURLToPath(new URL(url));
|
|
||||||
const content = readFileSync(pathname, 'utf8');
|
|
||||||
try {
|
|
||||||
const exports = JsonParse(stripBOM(content));
|
|
||||||
reflect.exports.default.set(exports);
|
|
||||||
} catch (err) {
|
|
||||||
err.message = pathname + ': ' + err.message;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
const {
|
const {
|
||||||
callbackMap,
|
callbackMap,
|
||||||
} = internalBinding('module_wrap');
|
} = internalBinding('module_wrap');
|
||||||
|
|
||||||
const { pathToFileURL } = require('internal/url');
|
|
||||||
const Loader = require('internal/modules/esm/loader');
|
|
||||||
const {
|
|
||||||
wrapToModuleMap,
|
|
||||||
} = require('internal/vm/source_text_module');
|
|
||||||
const {
|
const {
|
||||||
ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING,
|
ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING,
|
||||||
} = require('internal/errors').codes;
|
} = require('internal/errors').codes;
|
||||||
|
|
||||||
|
const { Loader } = require('internal/modules/esm/loader');
|
||||||
|
const { pathToFileURL } = require('internal/url');
|
||||||
|
const {
|
||||||
|
wrapToModuleMap,
|
||||||
|
} = require('internal/vm/source_text_module');
|
||||||
|
|
||||||
exports.initializeImportMetaObject = function(wrap, meta) {
|
exports.initializeImportMetaObject = function(wrap, meta) {
|
||||||
if (callbackMap.has(wrap)) {
|
if (callbackMap.has(wrap)) {
|
||||||
const { initializeImportMeta } = callbackMap.get(wrap);
|
const { initializeImportMeta } = callbackMap.get(wrap);
|
||||||
@ -34,9 +34,7 @@ exports.importModuleDynamicallyCallback = async function(wrap, specifier) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let loaderResolve;
|
let loaderResolve;
|
||||||
exports.loaderPromise = new Promise((resolve, reject) => {
|
exports.loaderPromise = new Promise((resolve) => loaderResolve = resolve);
|
||||||
loaderResolve = resolve;
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.ESMLoader = undefined;
|
exports.ESMLoader = undefined;
|
||||||
|
|
||||||
|
@ -33,6 +33,24 @@ function tryGetCwd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function evalModule(source) {
|
||||||
|
const { decorateErrorStack } = require('internal/util');
|
||||||
|
const asyncESM = require('internal/process/esm_loader');
|
||||||
|
asyncESM.loaderPromise.then(async (loader) => {
|
||||||
|
const { result } = await loader.eval(source);
|
||||||
|
if (require('internal/options').getOptionValue('--print')) {
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
decorateErrorStack(e);
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
// Handle any nextTicks added in the first tick of the program.
|
||||||
|
process._tickCallback();
|
||||||
|
}
|
||||||
|
|
||||||
function evalScript(name, body, breakFirstLine) {
|
function evalScript(name, body, breakFirstLine) {
|
||||||
const CJSModule = require('internal/modules/cjs/loader');
|
const CJSModule = require('internal/modules/cjs/loader');
|
||||||
const { kVmBreakFirstLineSymbol } = require('internal/util');
|
const { kVmBreakFirstLineSymbol } = require('internal/util');
|
||||||
@ -176,6 +194,7 @@ function readStdin(callback) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
readStdin,
|
readStdin,
|
||||||
tryGetCwd,
|
tryGetCwd,
|
||||||
|
evalModule,
|
||||||
evalScript,
|
evalScript,
|
||||||
fatalException: createFatalException(),
|
fatalException: createFatalException(),
|
||||||
setUncaughtExceptionCaptureCallback,
|
setUncaughtExceptionCaptureCallback,
|
||||||
|
11
src/env.h
11
src/env.h
@ -77,11 +77,13 @@ struct PackageConfig {
|
|||||||
enum class Exists { Yes, No };
|
enum class Exists { Yes, No };
|
||||||
enum class IsValid { Yes, No };
|
enum class IsValid { Yes, No };
|
||||||
enum class HasMain { Yes, No };
|
enum class HasMain { Yes, No };
|
||||||
|
enum PackageType : uint32_t { None = 0, CommonJS, Module };
|
||||||
|
|
||||||
Exists exists;
|
const Exists exists;
|
||||||
IsValid is_valid;
|
const IsValid is_valid;
|
||||||
HasMain has_main;
|
const HasMain has_main;
|
||||||
std::string main;
|
const std::string main;
|
||||||
|
const PackageType type;
|
||||||
};
|
};
|
||||||
} // namespace loader
|
} // namespace loader
|
||||||
|
|
||||||
@ -141,6 +143,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
|
|||||||
V(channel_string, "channel") \
|
V(channel_string, "channel") \
|
||||||
V(chunks_sent_since_last_write_string, "chunksSentSinceLastWrite") \
|
V(chunks_sent_since_last_write_string, "chunksSentSinceLastWrite") \
|
||||||
V(code_string, "code") \
|
V(code_string, "code") \
|
||||||
|
V(commonjs_string, "commonjs") \
|
||||||
V(config_string, "config") \
|
V(config_string, "config") \
|
||||||
V(constants_string, "constants") \
|
V(constants_string, "constants") \
|
||||||
V(crypto_dsa_string, "dsa") \
|
V(crypto_dsa_string, "dsa") \
|
||||||
|
@ -29,7 +29,6 @@ using v8::HandleScope;
|
|||||||
using v8::Integer;
|
using v8::Integer;
|
||||||
using v8::IntegrityLevel;
|
using v8::IntegrityLevel;
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::JSON;
|
|
||||||
using v8::Just;
|
using v8::Just;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::Maybe;
|
using v8::Maybe;
|
||||||
@ -46,7 +45,13 @@ using v8::String;
|
|||||||
using v8::Undefined;
|
using v8::Undefined;
|
||||||
using v8::Value;
|
using v8::Value;
|
||||||
|
|
||||||
static const char* const EXTENSIONS[] = {".mjs", ".js", ".json", ".node"};
|
static const char* const EXTENSIONS[] = {
|
||||||
|
".mjs",
|
||||||
|
".cjs",
|
||||||
|
".js",
|
||||||
|
".json",
|
||||||
|
".node"
|
||||||
|
};
|
||||||
|
|
||||||
ModuleWrap::ModuleWrap(Environment* env,
|
ModuleWrap::ModuleWrap(Environment* env,
|
||||||
Local<Object> object,
|
Local<Object> object,
|
||||||
@ -471,100 +476,233 @@ std::string ReadFile(uv_file file) {
|
|||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CheckFileOptions {
|
enum DescriptorType {
|
||||||
LEAVE_OPEN_AFTER_CHECK,
|
FILE,
|
||||||
CLOSE_AFTER_CHECK
|
DIRECTORY,
|
||||||
|
NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
Maybe<uv_file> CheckFile(const std::string& path,
|
// When DescriptorType cache is added, this can also return
|
||||||
CheckFileOptions opt = CLOSE_AFTER_CHECK) {
|
// Nothing for the "null" cache entries.
|
||||||
|
inline Maybe<uv_file> OpenDescriptor(const std::string& path) {
|
||||||
uv_fs_t fs_req;
|
uv_fs_t fs_req;
|
||||||
if (path.empty()) {
|
|
||||||
return Nothing<uv_file>();
|
|
||||||
}
|
|
||||||
|
|
||||||
uv_file fd = uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr);
|
uv_file fd = uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr);
|
||||||
uv_fs_req_cleanup(&fs_req);
|
uv_fs_req_cleanup(&fs_req);
|
||||||
|
if (fd < 0) return Nothing<uv_file>();
|
||||||
if (fd < 0) {
|
|
||||||
return Nothing<uv_file>();
|
|
||||||
}
|
|
||||||
|
|
||||||
uv_fs_fstat(nullptr, &fs_req, fd, nullptr);
|
|
||||||
uint64_t is_directory = fs_req.statbuf.st_mode & S_IFDIR;
|
|
||||||
uv_fs_req_cleanup(&fs_req);
|
|
||||||
|
|
||||||
if (is_directory) {
|
|
||||||
CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, fd, nullptr));
|
|
||||||
uv_fs_req_cleanup(&fs_req);
|
|
||||||
return Nothing<uv_file>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt == CLOSE_AFTER_CHECK) {
|
|
||||||
CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, fd, nullptr));
|
|
||||||
uv_fs_req_cleanup(&fs_req);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Just(fd);
|
return Just(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void CloseDescriptor(uv_file fd) {
|
||||||
|
uv_fs_t fs_req;
|
||||||
|
uv_fs_close(nullptr, &fs_req, fd, nullptr);
|
||||||
|
uv_fs_req_cleanup(&fs_req);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DescriptorType CheckDescriptorAtFile(uv_file fd) {
|
||||||
|
uv_fs_t fs_req;
|
||||||
|
int rc = uv_fs_fstat(nullptr, &fs_req, fd, nullptr);
|
||||||
|
if (rc == 0) {
|
||||||
|
uint64_t is_directory = fs_req.statbuf.st_mode & S_IFDIR;
|
||||||
|
uv_fs_req_cleanup(&fs_req);
|
||||||
|
return is_directory ? DIRECTORY : FILE;
|
||||||
|
}
|
||||||
|
uv_fs_req_cleanup(&fs_req);
|
||||||
|
return NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(@guybedford): Add a DescriptorType cache layer here.
|
||||||
|
// Should be directory based -> if path/to/dir doesn't exist
|
||||||
|
// then the cache should early-fail any path/to/dir/file check.
|
||||||
|
DescriptorType CheckDescriptorAtPath(const std::string& path) {
|
||||||
|
Maybe<uv_file> fd = OpenDescriptor(path);
|
||||||
|
if (fd.IsNothing()) return NONE;
|
||||||
|
DescriptorType type = CheckDescriptorAtFile(fd.FromJust());
|
||||||
|
CloseDescriptor(fd.FromJust());
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<std::string> ReadIfFile(const std::string& path) {
|
||||||
|
Maybe<uv_file> fd = OpenDescriptor(path);
|
||||||
|
if (fd.IsNothing()) return Nothing<std::string>();
|
||||||
|
DescriptorType type = CheckDescriptorAtFile(fd.FromJust());
|
||||||
|
if (type != FILE) return Nothing<std::string>();
|
||||||
|
std::string source = ReadFile(fd.FromJust());
|
||||||
|
CloseDescriptor(fd.FromJust());
|
||||||
|
return Just(source);
|
||||||
|
}
|
||||||
|
|
||||||
using Exists = PackageConfig::Exists;
|
using Exists = PackageConfig::Exists;
|
||||||
using IsValid = PackageConfig::IsValid;
|
using IsValid = PackageConfig::IsValid;
|
||||||
using HasMain = PackageConfig::HasMain;
|
using HasMain = PackageConfig::HasMain;
|
||||||
|
using PackageType = PackageConfig::PackageType;
|
||||||
|
|
||||||
const PackageConfig& GetPackageConfig(Environment* env,
|
Maybe<const PackageConfig*> GetPackageConfig(Environment* env,
|
||||||
const std::string& path) {
|
const std::string& path,
|
||||||
|
const URL& base) {
|
||||||
auto existing = env->package_json_cache.find(path);
|
auto existing = env->package_json_cache.find(path);
|
||||||
if (existing != env->package_json_cache.end()) {
|
if (existing != env->package_json_cache.end()) {
|
||||||
return existing->second;
|
const PackageConfig* pcfg = &existing->second;
|
||||||
|
if (pcfg->is_valid == IsValid::No) {
|
||||||
|
std::string msg = "Invalid JSON in '" + path +
|
||||||
|
"' imported from " + base.ToFilePath();
|
||||||
|
node::THROW_ERR_INVALID_PACKAGE_CONFIG(env, msg.c_str());
|
||||||
|
return Nothing<const PackageConfig*>();
|
||||||
|
}
|
||||||
|
return Just(pcfg);
|
||||||
}
|
}
|
||||||
Maybe<uv_file> check = CheckFile(path, LEAVE_OPEN_AFTER_CHECK);
|
|
||||||
if (check.IsNothing()) {
|
Maybe<std::string> source = ReadIfFile(path);
|
||||||
|
|
||||||
|
if (source.IsNothing()) {
|
||||||
auto entry = env->package_json_cache.emplace(path,
|
auto entry = env->package_json_cache.emplace(path,
|
||||||
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" });
|
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "",
|
||||||
return entry.first->second;
|
PackageType::None });
|
||||||
|
return Just(&entry.first->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string pkg_src = source.FromJust();
|
||||||
|
|
||||||
Isolate* isolate = env->isolate();
|
Isolate* isolate = env->isolate();
|
||||||
v8::HandleScope handle_scope(isolate);
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
|
||||||
std::string pkg_src = ReadFile(check.FromJust());
|
|
||||||
uv_fs_t fs_req;
|
|
||||||
CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, check.FromJust(), nullptr));
|
|
||||||
uv_fs_req_cleanup(&fs_req);
|
|
||||||
|
|
||||||
Local<String> src;
|
|
||||||
if (!String::NewFromUtf8(isolate,
|
|
||||||
pkg_src.c_str(),
|
|
||||||
v8::NewStringType::kNormal,
|
|
||||||
pkg_src.length()).ToLocal(&src)) {
|
|
||||||
auto entry = env->package_json_cache.emplace(path,
|
|
||||||
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" });
|
|
||||||
return entry.first->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
Local<Value> pkg_json_v;
|
|
||||||
Local<Object> pkg_json;
|
Local<Object> pkg_json;
|
||||||
|
{
|
||||||
|
Local<Value> src;
|
||||||
|
Local<Value> pkg_json_v;
|
||||||
|
Local<Context> context = env->context();
|
||||||
|
|
||||||
if (!JSON::Parse(env->context(), src).ToLocal(&pkg_json_v) ||
|
if (!ToV8Value(context, pkg_src).ToLocal(&src) ||
|
||||||
!pkg_json_v->ToObject(env->context()).ToLocal(&pkg_json)) {
|
!v8::JSON::Parse(context, src.As<String>()).ToLocal(&pkg_json_v) ||
|
||||||
auto entry = env->package_json_cache.emplace(path,
|
!pkg_json_v->ToObject(context).ToLocal(&pkg_json)) {
|
||||||
PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "" });
|
env->package_json_cache.emplace(path,
|
||||||
return entry.first->second;
|
PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "",
|
||||||
|
PackageType::None });
|
||||||
|
std::string msg = "Invalid JSON in '" + path +
|
||||||
|
"' imported from " + base.ToFilePath();
|
||||||
|
node::THROW_ERR_INVALID_PACKAGE_CONFIG(env, msg.c_str());
|
||||||
|
return Nothing<const PackageConfig*>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Local<Value> pkg_main;
|
Local<Value> pkg_main;
|
||||||
HasMain has_main = HasMain::No;
|
HasMain has_main = HasMain::No;
|
||||||
std::string main_std;
|
std::string main_std;
|
||||||
if (pkg_json->Get(env->context(), env->main_string()).ToLocal(&pkg_main)) {
|
if (pkg_json->Get(env->context(), env->main_string()).ToLocal(&pkg_main)) {
|
||||||
has_main = HasMain::Yes;
|
if (pkg_main->IsString()) {
|
||||||
|
has_main = HasMain::Yes;
|
||||||
|
}
|
||||||
Utf8Value main_utf8(isolate, pkg_main);
|
Utf8Value main_utf8(isolate, pkg_main);
|
||||||
main_std.assign(std::string(*main_utf8, main_utf8.length()));
|
main_std.assign(std::string(*main_utf8, main_utf8.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PackageType pkg_type = PackageType::None;
|
||||||
|
Local<Value> type_v;
|
||||||
|
if (pkg_json->Get(env->context(), env->type_string()).ToLocal(&type_v)) {
|
||||||
|
if (type_v->StrictEquals(env->module_string())) {
|
||||||
|
pkg_type = PackageType::Module;
|
||||||
|
} else if (type_v->StrictEquals(env->commonjs_string())) {
|
||||||
|
pkg_type = PackageType::CommonJS;
|
||||||
|
}
|
||||||
|
// ignore unknown types for forwards compatibility
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Value> exports_v;
|
||||||
|
if (pkg_json->Get(env->context(),
|
||||||
|
env->exports_string()).ToLocal(&exports_v) &&
|
||||||
|
(exports_v->IsObject() || exports_v->IsString() ||
|
||||||
|
exports_v->IsBoolean())) {
|
||||||
|
Persistent<Value> exports;
|
||||||
|
exports.Reset(env->isolate(), exports_v);
|
||||||
|
|
||||||
|
auto entry = env->package_json_cache.emplace(path,
|
||||||
|
PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std,
|
||||||
|
pkg_type });
|
||||||
|
return Just(&entry.first->second);
|
||||||
|
}
|
||||||
|
|
||||||
auto entry = env->package_json_cache.emplace(path,
|
auto entry = env->package_json_cache.emplace(path,
|
||||||
PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std });
|
PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std,
|
||||||
return entry.first->second;
|
pkg_type });
|
||||||
|
return Just(&entry.first->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<const PackageConfig*> GetPackageScopeConfig(Environment* env,
|
||||||
|
const URL& resolved,
|
||||||
|
const URL& base) {
|
||||||
|
URL pjson_url("./package.json", &resolved);
|
||||||
|
while (true) {
|
||||||
|
Maybe<const PackageConfig*> pkg_cfg =
|
||||||
|
GetPackageConfig(env, pjson_url.ToFilePath(), base);
|
||||||
|
if (pkg_cfg.IsNothing()) return pkg_cfg;
|
||||||
|
if (pkg_cfg.FromJust()->exists == Exists::Yes) return pkg_cfg;
|
||||||
|
|
||||||
|
URL last_pjson_url = pjson_url;
|
||||||
|
pjson_url = URL("../package.json", pjson_url);
|
||||||
|
|
||||||
|
// Terminates at root where ../package.json equals ../../package.json
|
||||||
|
// (can't just check "/package.json" for Windows support).
|
||||||
|
if (pjson_url.path() == last_pjson_url.path()) {
|
||||||
|
auto entry = env->package_json_cache.emplace(pjson_url.ToFilePath(),
|
||||||
|
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "",
|
||||||
|
PackageType::None });
|
||||||
|
const PackageConfig* pcfg = &entry.first->second;
|
||||||
|
return Just(pcfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Legacy CommonJS main resolution:
|
||||||
|
* 1. let M = pkg_url + (json main field)
|
||||||
|
* 2. TRY(M, M.js, M.json, M.node)
|
||||||
|
* 3. TRY(M/index.js, M/index.json, M/index.node)
|
||||||
|
* 4. TRY(pkg_url/index.js, pkg_url/index.json, pkg_url/index.node)
|
||||||
|
* 5. NOT_FOUND
|
||||||
|
*/
|
||||||
|
inline bool FileExists(const URL& url) {
|
||||||
|
return CheckDescriptorAtPath(url.ToFilePath()) == FILE;
|
||||||
|
}
|
||||||
|
Maybe<URL> LegacyMainResolve(const URL& pjson_url,
|
||||||
|
const PackageConfig& pcfg) {
|
||||||
|
URL guess;
|
||||||
|
if (pcfg.has_main == HasMain::Yes) {
|
||||||
|
// Note: fs check redundances will be handled by Descriptor cache here.
|
||||||
|
if (FileExists(guess = URL("./" + pcfg.main, pjson_url))) {
|
||||||
|
return Just(guess);
|
||||||
|
}
|
||||||
|
if (FileExists(guess = URL("./" + pcfg.main + ".js", pjson_url))) {
|
||||||
|
return Just(guess);
|
||||||
|
}
|
||||||
|
if (FileExists(guess = URL("./" + pcfg.main + ".json", pjson_url))) {
|
||||||
|
return Just(guess);
|
||||||
|
}
|
||||||
|
if (FileExists(guess = URL("./" + pcfg.main + ".node", pjson_url))) {
|
||||||
|
return Just(guess);
|
||||||
|
}
|
||||||
|
if (FileExists(guess = URL("./" + pcfg.main + "/index.js", pjson_url))) {
|
||||||
|
return Just(guess);
|
||||||
|
}
|
||||||
|
// Such stat.
|
||||||
|
if (FileExists(guess = URL("./" + pcfg.main + "/index.json", pjson_url))) {
|
||||||
|
return Just(guess);
|
||||||
|
}
|
||||||
|
if (FileExists(guess = URL("./" + pcfg.main + "/index.node", pjson_url))) {
|
||||||
|
return Just(guess);
|
||||||
|
}
|
||||||
|
// Fallthrough.
|
||||||
|
}
|
||||||
|
if (FileExists(guess = URL("./index.js", pjson_url))) {
|
||||||
|
return Just(guess);
|
||||||
|
}
|
||||||
|
// So fs.
|
||||||
|
if (FileExists(guess = URL("./index.json", pjson_url))) {
|
||||||
|
return Just(guess);
|
||||||
|
}
|
||||||
|
if (FileExists(guess = URL("./index.node", pjson_url))) {
|
||||||
|
return Just(guess);
|
||||||
|
}
|
||||||
|
// Not found.
|
||||||
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ResolveExtensionsOptions {
|
enum ResolveExtensionsOptions {
|
||||||
@ -575,17 +713,14 @@ enum ResolveExtensionsOptions {
|
|||||||
template <ResolveExtensionsOptions options>
|
template <ResolveExtensionsOptions options>
|
||||||
Maybe<URL> ResolveExtensions(const URL& search) {
|
Maybe<URL> ResolveExtensions(const URL& search) {
|
||||||
if (options == TRY_EXACT_NAME) {
|
if (options == TRY_EXACT_NAME) {
|
||||||
std::string filePath = search.ToFilePath();
|
if (FileExists(search)) {
|
||||||
Maybe<uv_file> check = CheckFile(filePath);
|
|
||||||
if (!check.IsNothing()) {
|
|
||||||
return Just(search);
|
return Just(search);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const char* extension : EXTENSIONS) {
|
for (const char* extension : EXTENSIONS) {
|
||||||
URL guess(search.path() + extension, &search);
|
URL guess(search.path() + extension, &search);
|
||||||
Maybe<uv_file> check = CheckFile(guess.ToFilePath());
|
if (FileExists(guess)) {
|
||||||
if (!check.IsNothing()) {
|
|
||||||
return Just(guess);
|
return Just(guess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -597,93 +732,150 @@ inline Maybe<URL> ResolveIndex(const URL& search) {
|
|||||||
return ResolveExtensions<ONLY_VIA_EXTENSIONS>(URL("index", search));
|
return ResolveExtensions<ONLY_VIA_EXTENSIONS>(URL("index", search));
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<URL> ResolveMain(Environment* env, const URL& search) {
|
Maybe<URL> FinalizeResolution(Environment* env,
|
||||||
URL pkg("package.json", &search);
|
const URL& resolved,
|
||||||
|
const URL& base) {
|
||||||
const PackageConfig& pjson =
|
if (env->options()->es_module_specifier_resolution == "node") {
|
||||||
GetPackageConfig(env, pkg.ToFilePath());
|
Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
|
||||||
// Note invalid package.json should throw in resolver
|
if (!file.IsNothing()) {
|
||||||
// currently we silently ignore which is incorrect
|
return file;
|
||||||
if (pjson.exists == Exists::No ||
|
}
|
||||||
pjson.is_valid == IsValid::No ||
|
if (resolved.path().back() != '/') {
|
||||||
pjson.has_main == HasMain::No) {
|
file = ResolveIndex(URL(resolved.path() + "/", &base));
|
||||||
|
} else {
|
||||||
|
file = ResolveIndex(resolved);
|
||||||
|
}
|
||||||
|
if (!file.IsNothing()) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
std::string msg = "Cannot find module '" + resolved.path() +
|
||||||
|
"' imported from " + base.ToFilePath();
|
||||||
|
node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
|
||||||
return Nothing<URL>();
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
if (!ShouldBeTreatedAsRelativeOrAbsolutePath(pjson.main)) {
|
|
||||||
return Resolve(env, "./" + pjson.main, search, IgnoreMain);
|
const std::string& path = resolved.ToFilePath();
|
||||||
|
if (CheckDescriptorAtPath(path) != FILE) {
|
||||||
|
std::string msg = "Cannot find module '" + path +
|
||||||
|
"' imported from " + base.ToFilePath();
|
||||||
|
node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
|
||||||
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
return Resolve(env, pjson.main, search, IgnoreMain);
|
|
||||||
|
return Just(resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<URL> ResolveModule(Environment* env,
|
Maybe<URL> PackageMainResolve(Environment* env,
|
||||||
const std::string& specifier,
|
const URL& pjson_url,
|
||||||
const URL& base) {
|
const PackageConfig& pcfg,
|
||||||
URL parent(".", base);
|
const URL& base) {
|
||||||
URL dir("");
|
if (pcfg.exists == Exists::Yes) {
|
||||||
do {
|
if (pcfg.has_main == HasMain::Yes) {
|
||||||
dir = parent;
|
URL resolved(pcfg.main, pjson_url);
|
||||||
Maybe<URL> check =
|
const std::string& path = resolved.ToFilePath();
|
||||||
Resolve(env, "./node_modules/" + specifier, dir, CheckMain);
|
if (CheckDescriptorAtPath(path) == FILE) {
|
||||||
if (!check.IsNothing()) {
|
return Just(resolved);
|
||||||
const size_t limit = specifier.find('/');
|
|
||||||
const size_t spec_len =
|
|
||||||
limit == std::string::npos ? specifier.length() :
|
|
||||||
limit + 1;
|
|
||||||
std::string chroot =
|
|
||||||
dir.path() + "node_modules/" + specifier.substr(0, spec_len);
|
|
||||||
if (check.FromJust().path().substr(0, chroot.length()) != chroot) {
|
|
||||||
return Nothing<URL>();
|
|
||||||
}
|
}
|
||||||
return check;
|
|
||||||
} else {
|
|
||||||
// TODO(bmeck) PREVENT FALLTHROUGH
|
|
||||||
}
|
}
|
||||||
parent = URL("..", &dir);
|
if (env->options()->es_module_specifier_resolution == "node") {
|
||||||
} while (parent.path() != dir.path());
|
if (pcfg.has_main == HasMain::Yes) {
|
||||||
|
return FinalizeResolution(env, URL(pcfg.main, pjson_url), base);
|
||||||
|
} else {
|
||||||
|
return FinalizeResolution(env, URL("index", pjson_url), base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pcfg.type != PackageType::Module) {
|
||||||
|
Maybe<URL> resolved = LegacyMainResolve(pjson_url, pcfg);
|
||||||
|
if (!resolved.IsNothing()) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string msg = "Cannot find main entry point for '" +
|
||||||
|
URL(".", pjson_url).ToFilePath() + "' imported from " +
|
||||||
|
base.ToFilePath();
|
||||||
|
node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
|
||||||
return Nothing<URL>();
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<URL> ResolveDirectory(Environment* env,
|
Maybe<URL> PackageResolve(Environment* env,
|
||||||
const URL& search,
|
const std::string& specifier,
|
||||||
PackageMainCheck check_pjson_main) {
|
const URL& base) {
|
||||||
if (check_pjson_main) {
|
size_t sep_index = specifier.find('/');
|
||||||
Maybe<URL> main = ResolveMain(env, search);
|
if (specifier[0] == '@' && (sep_index == std::string::npos ||
|
||||||
if (!main.IsNothing())
|
specifier.length() == 0)) {
|
||||||
return main;
|
std::string msg = "Invalid package name '" + specifier +
|
||||||
|
"' imported from " + base.ToFilePath();
|
||||||
|
node::THROW_ERR_INVALID_MODULE_SPECIFIER(env, msg.c_str());
|
||||||
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
return ResolveIndex(search);
|
bool scope = false;
|
||||||
|
if (specifier[0] == '@') {
|
||||||
|
scope = true;
|
||||||
|
sep_index = specifier.find('/', sep_index + 1);
|
||||||
|
}
|
||||||
|
std::string pkg_name = specifier.substr(0,
|
||||||
|
sep_index == std::string::npos ? std::string::npos : sep_index);
|
||||||
|
std::string pkg_subpath;
|
||||||
|
if ((sep_index == std::string::npos ||
|
||||||
|
sep_index == specifier.length() - 1)) {
|
||||||
|
pkg_subpath = "";
|
||||||
|
} else {
|
||||||
|
pkg_subpath = "." + specifier.substr(sep_index);
|
||||||
|
}
|
||||||
|
URL pjson_url("./node_modules/" + pkg_name + "/package.json", &base);
|
||||||
|
std::string pjson_path = pjson_url.ToFilePath();
|
||||||
|
std::string last_path;
|
||||||
|
do {
|
||||||
|
DescriptorType check =
|
||||||
|
CheckDescriptorAtPath(pjson_path.substr(0, pjson_path.length() - 13));
|
||||||
|
if (check != DIRECTORY) {
|
||||||
|
last_path = pjson_path;
|
||||||
|
pjson_url = URL((scope ?
|
||||||
|
"../../../../node_modules/" : "../../../node_modules/") +
|
||||||
|
pkg_name + "/package.json", &pjson_url);
|
||||||
|
pjson_path = pjson_url.ToFilePath();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package match.
|
||||||
|
Maybe<const PackageConfig*> pcfg = GetPackageConfig(env, pjson_path, base);
|
||||||
|
// Invalid package configuration error.
|
||||||
|
if (pcfg.IsNothing()) return Nothing<URL>();
|
||||||
|
if (!pkg_subpath.length()) {
|
||||||
|
return PackageMainResolve(env, pjson_url, *pcfg.FromJust(), base);
|
||||||
|
} else {
|
||||||
|
return FinalizeResolution(env, URL(pkg_subpath, pjson_url), base);
|
||||||
|
}
|
||||||
|
CHECK(false);
|
||||||
|
// Cross-platform root check.
|
||||||
|
} while (pjson_path.length() != last_path.length());
|
||||||
|
|
||||||
|
std::string msg = "Cannot find package '" + pkg_name +
|
||||||
|
"' imported from " + base.ToFilePath();
|
||||||
|
node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
|
||||||
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
Maybe<URL> Resolve(Environment* env,
|
Maybe<URL> Resolve(Environment* env,
|
||||||
const std::string& specifier,
|
const std::string& specifier,
|
||||||
const URL& base,
|
const URL& base) {
|
||||||
PackageMainCheck check_pjson_main) {
|
// Order swapped from spec for minor perf gain.
|
||||||
URL pure_url(specifier);
|
// Ok since relative URLs cannot parse as URLs.
|
||||||
if (!(pure_url.flags() & URL_FLAGS_FAILED)) {
|
URL resolved;
|
||||||
// just check existence, without altering
|
|
||||||
Maybe<uv_file> check = CheckFile(pure_url.ToFilePath());
|
|
||||||
if (check.IsNothing()) {
|
|
||||||
return Nothing<URL>();
|
|
||||||
}
|
|
||||||
return Just(pure_url);
|
|
||||||
}
|
|
||||||
if (specifier.length() == 0) {
|
|
||||||
return Nothing<URL>();
|
|
||||||
}
|
|
||||||
if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
|
if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
|
||||||
URL resolved(specifier, base);
|
resolved = URL(specifier, base);
|
||||||
Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
|
|
||||||
if (!file.IsNothing())
|
|
||||||
return file;
|
|
||||||
if (specifier.back() != '/') {
|
|
||||||
resolved = URL(specifier + "/", base);
|
|
||||||
}
|
|
||||||
return ResolveDirectory(env, resolved, check_pjson_main);
|
|
||||||
} else {
|
} else {
|
||||||
return ResolveModule(env, specifier, base);
|
URL pure_url(specifier);
|
||||||
|
if (!(pure_url.flags() & URL_FLAGS_FAILED)) {
|
||||||
|
resolved = pure_url;
|
||||||
|
} else {
|
||||||
|
return PackageResolve(env, specifier, base);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return FinalizeResolution(env, resolved, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
|
void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
|
||||||
@ -705,15 +897,40 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) {
|
|||||||
env, "second argument is not a URL string");
|
env, "second argument is not a URL string");
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<URL> result = node::loader::Resolve(env, specifier_std, url);
|
Maybe<URL> result =
|
||||||
if (result.IsNothing() || (result.FromJust().flags() & URL_FLAGS_FAILED)) {
|
node::loader::Resolve(env,
|
||||||
std::string msg = "Cannot find module " + specifier_std;
|
specifier_std,
|
||||||
return node::THROW_ERR_MISSING_MODULE(env, msg.c_str());
|
url);
|
||||||
|
if (result.IsNothing()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeLocal<Value> obj = result.FromJust().ToObject(env);
|
URL resolution = result.FromJust();
|
||||||
if (!obj.IsEmpty())
|
CHECK(!(resolution.flags() & URL_FLAGS_FAILED));
|
||||||
args.GetReturnValue().Set(obj.ToLocalChecked());
|
|
||||||
|
Local<Value> resolution_obj;
|
||||||
|
if (resolution.ToObject(env).ToLocal(&resolution_obj))
|
||||||
|
args.GetReturnValue().Set(resolution_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleWrap::GetPackageType(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
|
// module.getPackageType(url)
|
||||||
|
CHECK_EQ(args.Length(), 1);
|
||||||
|
|
||||||
|
CHECK(args[0]->IsString());
|
||||||
|
Utf8Value url_utf8(env->isolate(), args[0]);
|
||||||
|
URL url(*url_utf8, url_utf8.length());
|
||||||
|
|
||||||
|
PackageType pkg_type = PackageType::None;
|
||||||
|
Maybe<const PackageConfig*> pcfg =
|
||||||
|
GetPackageScopeConfig(env, url, url);
|
||||||
|
if (!pcfg.IsNothing()) {
|
||||||
|
pkg_type = pcfg.FromJust()->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(Integer::New(env->isolate(), pkg_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
static MaybeLocal<Promise> ImportModuleDynamically(
|
static MaybeLocal<Promise> ImportModuleDynamically(
|
||||||
@ -849,6 +1066,7 @@ void ModuleWrap::Initialize(Local<Object> target,
|
|||||||
target->Set(env->context(), FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"),
|
target->Set(env->context(), FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"),
|
||||||
tpl->GetFunction(context).ToLocalChecked()).FromJust();
|
tpl->GetFunction(context).ToLocalChecked()).FromJust();
|
||||||
env->SetMethod(target, "resolve", Resolve);
|
env->SetMethod(target, "resolve", Resolve);
|
||||||
|
env->SetMethod(target, "getPackageType", GetPackageType);
|
||||||
env->SetMethod(target,
|
env->SetMethod(target,
|
||||||
"setImportModuleDynamicallyCallback",
|
"setImportModuleDynamicallyCallback",
|
||||||
SetImportModuleDynamicallyCallback);
|
SetImportModuleDynamicallyCallback);
|
||||||
|
@ -12,11 +12,6 @@
|
|||||||
namespace node {
|
namespace node {
|
||||||
namespace loader {
|
namespace loader {
|
||||||
|
|
||||||
enum PackageMainCheck : bool {
|
|
||||||
CheckMain = true,
|
|
||||||
IgnoreMain = false
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ScriptType : int {
|
enum ScriptType : int {
|
||||||
kScript,
|
kScript,
|
||||||
kModule,
|
kModule,
|
||||||
@ -29,11 +24,6 @@ enum HostDefinedOptions : int {
|
|||||||
kLength = 10,
|
kLength = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
v8::Maybe<url::URL> Resolve(Environment* env,
|
|
||||||
const std::string& specifier,
|
|
||||||
const url::URL& base,
|
|
||||||
PackageMainCheck read_pkg_json = CheckMain);
|
|
||||||
|
|
||||||
class ModuleWrap : public BaseObject {
|
class ModuleWrap : public BaseObject {
|
||||||
public:
|
public:
|
||||||
static const std::string EXTENSIONS[];
|
static const std::string EXTENSIONS[];
|
||||||
@ -75,6 +65,7 @@ class ModuleWrap : public BaseObject {
|
|||||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
|
||||||
static void Resolve(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void Resolve(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void GetPackageType(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void SetImportModuleDynamicallyCallback(
|
static void SetImportModuleDynamicallyCallback(
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void SetInitializeImportMetaObjectCallback(
|
static void SetInitializeImportMetaObjectCallback(
|
||||||
|
@ -45,12 +45,14 @@ void FatalException(v8::Isolate* isolate,
|
|||||||
V(ERR_CONSTRUCT_CALL_REQUIRED, Error) \
|
V(ERR_CONSTRUCT_CALL_REQUIRED, Error) \
|
||||||
V(ERR_INVALID_ARG_VALUE, TypeError) \
|
V(ERR_INVALID_ARG_VALUE, TypeError) \
|
||||||
V(ERR_INVALID_ARG_TYPE, TypeError) \
|
V(ERR_INVALID_ARG_TYPE, TypeError) \
|
||||||
|
V(ERR_INVALID_MODULE_SPECIFIER, TypeError) \
|
||||||
|
V(ERR_INVALID_PACKAGE_CONFIG, SyntaxError) \
|
||||||
V(ERR_INVALID_TRANSFER_OBJECT, TypeError) \
|
V(ERR_INVALID_TRANSFER_OBJECT, TypeError) \
|
||||||
V(ERR_MEMORY_ALLOCATION_FAILED, Error) \
|
V(ERR_MEMORY_ALLOCATION_FAILED, Error) \
|
||||||
V(ERR_MISSING_ARGS, TypeError) \
|
V(ERR_MISSING_ARGS, TypeError) \
|
||||||
V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, TypeError) \
|
V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, TypeError) \
|
||||||
V(ERR_MISSING_MODULE, Error) \
|
|
||||||
V(ERR_MISSING_PLATFORM_FOR_WORKER, Error) \
|
V(ERR_MISSING_PLATFORM_FOR_WORKER, Error) \
|
||||||
|
V(ERR_MODULE_NOT_FOUND, Error) \
|
||||||
V(ERR_OUT_OF_RANGE, RangeError) \
|
V(ERR_OUT_OF_RANGE, RangeError) \
|
||||||
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \
|
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \
|
||||||
V(ERR_SCRIPT_EXECUTION_TIMEOUT, Error) \
|
V(ERR_SCRIPT_EXECUTION_TIMEOUT, Error) \
|
||||||
|
@ -107,6 +107,32 @@ void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors) {
|
|||||||
errors->push_back("--loader requires --experimental-modules be enabled");
|
errors->push_back("--loader requires --experimental-modules be enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!module_type.empty()) {
|
||||||
|
if (!experimental_modules) {
|
||||||
|
errors->push_back("--entry-type requires "
|
||||||
|
"--experimental-modules to be enabled");
|
||||||
|
}
|
||||||
|
if (module_type != "commonjs" && module_type != "module") {
|
||||||
|
errors->push_back("--entry-type must \"module\" or \"commonjs\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (experimental_json_modules && !experimental_modules) {
|
||||||
|
errors->push_back("--experimental-json-modules requires "
|
||||||
|
"--experimental-modules be enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!es_module_specifier_resolution.empty()) {
|
||||||
|
if (!experimental_modules) {
|
||||||
|
errors->push_back("--es-module-specifier-resolution requires "
|
||||||
|
"--experimental-modules be enabled");
|
||||||
|
}
|
||||||
|
if (es_module_specifier_resolution != "node" &&
|
||||||
|
es_module_specifier_resolution != "explicit") {
|
||||||
|
errors->push_back("invalid value for --es-module-specifier-resolution");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (syntax_check_only && has_eval_string) {
|
if (syntax_check_only && has_eval_string) {
|
||||||
errors->push_back("either --check or --eval can be used, not both");
|
errors->push_back("either --check or --eval can be used, not both");
|
||||||
}
|
}
|
||||||
@ -214,6 +240,10 @@ DebugOptionsParser::DebugOptionsParser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EnvironmentOptionsParser::EnvironmentOptionsParser() {
|
EnvironmentOptionsParser::EnvironmentOptionsParser() {
|
||||||
|
AddOption("--experimental-json-modules",
|
||||||
|
"experimental JSON interop support for the ES Module loader",
|
||||||
|
&EnvironmentOptions::experimental_json_modules,
|
||||||
|
kAllowedInEnvironment);
|
||||||
AddOption("--experimental-modules",
|
AddOption("--experimental-modules",
|
||||||
"experimental ES Module support and caching modules",
|
"experimental ES Module support and caching modules",
|
||||||
&EnvironmentOptions::experimental_modules,
|
&EnvironmentOptions::experimental_modules,
|
||||||
@ -253,6 +283,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
|
|||||||
"custom loader",
|
"custom loader",
|
||||||
&EnvironmentOptions::userland_loader,
|
&EnvironmentOptions::userland_loader,
|
||||||
kAllowedInEnvironment);
|
kAllowedInEnvironment);
|
||||||
|
AddOption("--es-module-specifier-resolution",
|
||||||
|
"Select extension resolution algorithm for es modules; "
|
||||||
|
"either 'explicit' (default) or 'node'",
|
||||||
|
&EnvironmentOptions::es_module_specifier_resolution,
|
||||||
|
kAllowedInEnvironment);
|
||||||
AddOption("--no-deprecation",
|
AddOption("--no-deprecation",
|
||||||
"silence deprecation warnings",
|
"silence deprecation warnings",
|
||||||
&EnvironmentOptions::no_deprecation,
|
&EnvironmentOptions::no_deprecation,
|
||||||
@ -271,10 +306,12 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
|
|||||||
kAllowedInEnvironment);
|
kAllowedInEnvironment);
|
||||||
AddOption("--preserve-symlinks",
|
AddOption("--preserve-symlinks",
|
||||||
"preserve symbolic links when resolving",
|
"preserve symbolic links when resolving",
|
||||||
&EnvironmentOptions::preserve_symlinks);
|
&EnvironmentOptions::preserve_symlinks,
|
||||||
|
kAllowedInEnvironment);
|
||||||
AddOption("--preserve-symlinks-main",
|
AddOption("--preserve-symlinks-main",
|
||||||
"preserve symbolic links when resolving the main module",
|
"preserve symbolic links when resolving the main module",
|
||||||
&EnvironmentOptions::preserve_symlinks_main);
|
&EnvironmentOptions::preserve_symlinks_main,
|
||||||
|
kAllowedInEnvironment);
|
||||||
AddOption("--prof-process",
|
AddOption("--prof-process",
|
||||||
"process V8 profiler output generated using --prof",
|
"process V8 profiler output generated using --prof",
|
||||||
&EnvironmentOptions::prof_process);
|
&EnvironmentOptions::prof_process);
|
||||||
@ -301,6 +338,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
|
|||||||
"show stack traces on process warnings",
|
"show stack traces on process warnings",
|
||||||
&EnvironmentOptions::trace_warnings,
|
&EnvironmentOptions::trace_warnings,
|
||||||
kAllowedInEnvironment);
|
kAllowedInEnvironment);
|
||||||
|
AddOption("--entry-type",
|
||||||
|
"set module type name of the entry point",
|
||||||
|
&EnvironmentOptions::module_type,
|
||||||
|
kAllowedInEnvironment);
|
||||||
|
|
||||||
AddOption("--check",
|
AddOption("--check",
|
||||||
"syntax check script without executing",
|
"syntax check script without executing",
|
||||||
|
@ -91,7 +91,10 @@ class DebugOptions : public Options {
|
|||||||
class EnvironmentOptions : public Options {
|
class EnvironmentOptions : public Options {
|
||||||
public:
|
public:
|
||||||
bool abort_on_uncaught_exception = false;
|
bool abort_on_uncaught_exception = false;
|
||||||
|
bool experimental_json_modules = false;
|
||||||
bool experimental_modules = false;
|
bool experimental_modules = false;
|
||||||
|
std::string es_module_specifier_resolution;
|
||||||
|
std::string module_type;
|
||||||
std::string experimental_policy;
|
std::string experimental_policy;
|
||||||
bool experimental_repl_await = false;
|
bool experimental_repl_await = false;
|
||||||
bool experimental_vm_modules = false;
|
bool experimental_vm_modules = false;
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
#include <node.h>
|
|
||||||
#include <v8.h>
|
|
||||||
|
|
||||||
void Method(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
||||||
v8::Isolate* isolate = args.GetIsolate();
|
|
||||||
args.GetReturnValue().Set(v8::String::NewFromUtf8(
|
|
||||||
isolate, "world", v8::NewStringType::kNormal).ToLocalChecked());
|
|
||||||
}
|
|
||||||
|
|
||||||
void init(v8::Local<v8::Object> exports) {
|
|
||||||
NODE_SET_METHOD(exports, "hello", Method);
|
|
||||||
}
|
|
||||||
|
|
||||||
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
'targets': [
|
|
||||||
{
|
|
||||||
'target_name': 'binding',
|
|
||||||
'sources': [ 'binding.cc' ],
|
|
||||||
'includes': ['../common.gypi'],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const common = require('../../common');
|
|
||||||
|
|
||||||
const assert = require('assert');
|
|
||||||
const { spawnSync } = require('child_process');
|
|
||||||
const { copyFileSync } = require('fs');
|
|
||||||
const { join } = require('path');
|
|
||||||
|
|
||||||
const buildDir = join(__dirname, 'build');
|
|
||||||
|
|
||||||
copyFileSync(join(buildDir, common.buildType, 'binding.node'),
|
|
||||||
join(buildDir, 'binding.node'));
|
|
||||||
|
|
||||||
const result = spawnSync(process.execPath,
|
|
||||||
['--experimental-modules', `${__dirname}/test.mjs`]);
|
|
||||||
|
|
||||||
assert.ifError(result.error);
|
|
||||||
// TODO: Uncomment this once ESM is no longer experimental.
|
|
||||||
// assert.strictEqual(result.stderr.toString().trim(), '');
|
|
||||||
assert.strictEqual(result.stdout.toString().trim(), 'binding.hello() = world');
|
|
@ -1,6 +0,0 @@
|
|||||||
/* eslint-disable node-core/required-modules */
|
|
||||||
|
|
||||||
import assert from 'assert';
|
|
||||||
import binding from './build/binding.node';
|
|
||||||
assert.strictEqual(binding.hello(), 'world');
|
|
||||||
console.log('binding.hello() =', binding.hello());
|
|
@ -1,6 +1,15 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
/* eslint-disable node-core/required-modules */
|
/* eslint-disable node-core/required-modules */
|
||||||
import common from './index.js';
|
|
||||||
|
import { createRequireFromPath } from 'module';
|
||||||
|
import { fileURLToPath as toPath } from 'url';
|
||||||
|
|
||||||
|
function createRequire(metaUrl) {
|
||||||
|
return createRequireFromPath(toPath(metaUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const common = require('./index.js');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isMainThread,
|
isMainThread,
|
||||||
@ -91,5 +100,6 @@ export {
|
|||||||
getBufferSources,
|
getBufferSources,
|
||||||
disableCrashOnUnhandledRejection,
|
disableCrashOnUnhandledRejection,
|
||||||
getTTYfd,
|
getTTYfd,
|
||||||
runWithInvalidFD
|
runWithInvalidFD,
|
||||||
|
createRequire
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import '../common/index.mjs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import ok from '../fixtures/es-modules/test-esm-ok.mjs';
|
import ok from '../fixtures/es-modules/test-esm-ok.mjs';
|
||||||
import okShebang from './test-esm-shebang.mjs';
|
import okShebang from './test-esm-shebang.mjs';
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
import('./test-esm-cyclic-dynamic-import');
|
import '../common/index.mjs';
|
||||||
|
import('./test-esm-cyclic-dynamic-import.mjs');
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import '../common/index.mjs';
|
||||||
|
|
||||||
// Assert we can import files with `%` in their pathname.
|
// Assert we can import files with `%` in their pathname.
|
||||||
|
|
||||||
import '../fixtures/es-modules/test-esm-double-encoding-native%2520.js';
|
import '../fixtures/es-modules/test-esm-double-encoding-native%2520.mjs';
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
@ -17,7 +18,7 @@ function expectErrorProperty(result, propertyKey, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function expectMissingModuleError(result) {
|
function expectMissingModuleError(result) {
|
||||||
expectErrorProperty(result, 'code', 'MODULE_NOT_FOUND');
|
expectErrorProperty(result, 'code', 'ERR_MODULE_NOT_FOUND');
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectOkNamespace(result) {
|
function expectOkNamespace(result) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import '../common/index.mjs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
// ./test-esm-ok.mjs
|
// ./test-esm-ok.mjs
|
||||||
import ok from '../fixtures/es-modules/test-%65%73%6d-ok.mjs';
|
import ok from '../fixtures/es-modules/test-%65%73%6d-ok.mjs';
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
require('../common');
|
require('../common');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
const file = '../fixtures/syntax/bad_syntax.js';
|
const file = '../fixtures/syntax/bad_syntax.mjs';
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import '../common/index.mjs';
|
||||||
|
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
if (typeof arguments !== 'undefined') {
|
if (typeof arguments !== 'undefined') {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
|
/* eslint-disable node-core/required-modules */
|
||||||
|
|
||||||
import '../common';
|
import '../common/index.mjs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
assert.strictEqual(Object.getPrototypeOf(import.meta), null);
|
assert.strictEqual(Object.getPrototypeOf(import.meta), null);
|
||||||
|
26
test/es-module/test-esm-json-cache.mjs
Normal file
26
test/es-module/test-esm-json-cache.mjs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Flags: --experimental-modules --experimental-json-modules
|
||||||
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import '../common/index.mjs';
|
||||||
|
|
||||||
|
import { strictEqual, deepStrictEqual } from 'assert';
|
||||||
|
|
||||||
|
import { createRequireFromPath as createRequire } from 'module';
|
||||||
|
import { fileURLToPath as fromURL } from 'url';
|
||||||
|
|
||||||
|
import mod from '../fixtures/es-modules/json-cache/mod.cjs';
|
||||||
|
import another from '../fixtures/es-modules/json-cache/another.cjs';
|
||||||
|
import test from '../fixtures/es-modules/json-cache/test.json';
|
||||||
|
|
||||||
|
const require = createRequire(fromURL(import.meta.url));
|
||||||
|
|
||||||
|
const modCjs = require('../fixtures/es-modules/json-cache/mod.cjs');
|
||||||
|
const anotherCjs = require('../fixtures/es-modules/json-cache/another.cjs');
|
||||||
|
const testCjs = require('../fixtures/es-modules/json-cache/test.json');
|
||||||
|
|
||||||
|
strictEqual(mod.one, 1);
|
||||||
|
strictEqual(another.one, 'zalgo');
|
||||||
|
strictEqual(test.one, 'it comes');
|
||||||
|
|
||||||
|
deepStrictEqual(mod, modCjs);
|
||||||
|
deepStrictEqual(another, anotherCjs);
|
||||||
|
deepStrictEqual(test, testCjs);
|
@ -1,8 +1,9 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules --experimental-json-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
import assert from 'assert';
|
|
||||||
import ok from '../fixtures/es-modules/test-esm-ok.mjs';
|
|
||||||
import json from '../fixtures/es-modules/json.json';
|
|
||||||
|
|
||||||
assert(ok);
|
import '../common/index.mjs';
|
||||||
assert.strictEqual(json.val, 42);
|
import { strictEqual } from 'assert';
|
||||||
|
|
||||||
|
import secret from '../fixtures/experimental.json';
|
||||||
|
|
||||||
|
strictEqual(secret.ofLife, 42);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
|
/* eslint-disable node-core/required-modules */
|
||||||
|
|
||||||
import '../common';
|
import '../common/index.mjs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
import fs, { readFile, readFileSync } from 'fs';
|
import fs, { readFile, readFileSync } from 'fs';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-invalid-format.mjs
|
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-invalid-format.mjs
|
||||||
import { expectsError, mustCall } from '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import { expectsError, mustCall } from '../common/index.mjs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
import('../fixtures/es-modules/test-esm-ok.mjs')
|
import('../fixtures/es-modules/test-esm-ok.mjs')
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-invalid-url.mjs
|
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-invalid-url.mjs
|
||||||
import { expectsError, mustCall } from '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
|
||||||
|
import { expectsError, mustCall } from '../common/index.mjs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
import('../fixtures/es-modules/test-esm-ok.mjs')
|
import('../fixtures/es-modules/test-esm-ok.mjs')
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs
|
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs
|
||||||
|
/* eslint-disable node-core/required-modules */
|
||||||
|
|
||||||
import { expectsError } from '../common';
|
import { expectsError } from '../common/index.mjs';
|
||||||
|
|
||||||
import('test').catch(expectsError({
|
import('test').catch(expectsError({
|
||||||
code: 'ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK',
|
code: 'ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK',
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
|
|
||||||
const { URL } = require('url');
|
const { URL } = require('url');
|
||||||
const Loader = require('internal/modules/esm/loader');
|
const { Loader } = require('internal/modules/esm/loader');
|
||||||
const ModuleMap = require('internal/modules/esm/module_map');
|
const ModuleMap = require('internal/modules/esm/module_map');
|
||||||
const ModuleJob = require('internal/modules/esm/module_job');
|
const ModuleJob = require('internal/modules/esm/module_job');
|
||||||
const createDynamicModule = require(
|
const createDynamicModule = require(
|
||||||
|
@ -5,13 +5,13 @@
|
|||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
|
|
||||||
const { search } = require('internal/modules/esm/default_resolve');
|
const resolve = require('internal/modules/esm/default_resolve');
|
||||||
|
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => search('target', undefined),
|
() => resolve('target', undefined),
|
||||||
{
|
{
|
||||||
code: 'ERR_MISSING_MODULE',
|
code: 'ERR_MODULE_NOT_FOUND',
|
||||||
type: Error,
|
type: Error,
|
||||||
message: 'Cannot find module target'
|
message: /Cannot find package 'target'/
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,26 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import '../common/index.mjs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import main from '../fixtures/es-modules/pjson-main';
|
|
||||||
|
|
||||||
assert.strictEqual(main, 'main');
|
async function main() {
|
||||||
|
let mod;
|
||||||
|
try {
|
||||||
|
mod = await import('../fixtures/es-modules/pjson-main');
|
||||||
|
} catch (e) {
|
||||||
|
assert.strictEqual(e.code, 'ERR_MODULE_NOT_FOUND');
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.strictEqual(mod, undefined);
|
||||||
|
|
||||||
|
try {
|
||||||
|
mod = await import('../fixtures/es-modules/pjson-main/main.mjs');
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
assert.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.strictEqual(mod.main, 'main');
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs
|
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import '../common/index.mjs';
|
||||||
import { readFile } from 'fs';
|
import { readFile } from 'fs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import ok from '../fixtures/es-modules/test-esm-ok.mjs';
|
import ok from '../fixtures/es-modules/test-esm-ok.mjs';
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
|
||||||
|
import '../common/index.mjs';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import Module from 'module';
|
import Module from 'module';
|
||||||
|
35
test/es-module/test-esm-no-extension.js
Normal file
35
test/es-module/test-esm-no-extension.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const fixtures = require('../common/fixtures');
|
||||||
|
const { spawn } = require('child_process');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const entry = fixtures.path('/es-modules/noext-esm');
|
||||||
|
|
||||||
|
// Run a module that does not have extension
|
||||||
|
// This is to ensure the --entry-type works as expected
|
||||||
|
|
||||||
|
const child = spawn(process.execPath, [
|
||||||
|
'--experimental-modules',
|
||||||
|
'--entry-type=module',
|
||||||
|
entry
|
||||||
|
]);
|
||||||
|
|
||||||
|
let stderr = '';
|
||||||
|
child.stderr.setEncoding('utf8');
|
||||||
|
child.stderr.on('data', (data) => {
|
||||||
|
stderr += data;
|
||||||
|
});
|
||||||
|
let stdout = '';
|
||||||
|
child.stdout.setEncoding('utf8');
|
||||||
|
child.stdout.on('data', (data) => {
|
||||||
|
stdout += data;
|
||||||
|
});
|
||||||
|
child.on('close', common.mustCall((code, signal) => {
|
||||||
|
assert.strictEqual(code, 0);
|
||||||
|
assert.strictEqual(signal, null);
|
||||||
|
assert.strictEqual(stdout, 'executed\n');
|
||||||
|
assert.strictEqual(stderr, `(node:${child.pid}) ` +
|
||||||
|
'ExperimentalWarning: The ESM module loader is experimental.\n');
|
||||||
|
}));
|
@ -1,3 +1,3 @@
|
|||||||
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/not-found-assert-loader.mjs
|
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/not-found-assert-loader.mjs
|
||||||
/* eslint-disable node-core/required-modules */
|
/* eslint-disable node-core/required-modules */
|
||||||
import './not-found';
|
import './not-found.mjs';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import '../common/index.mjs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import process from 'process';
|
import process from 'process';
|
||||||
|
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
import '../fixtures/es-module-require-cache/preload.js';
|
import { createRequire } from '../common/index.mjs';
|
||||||
import '../fixtures/es-module-require-cache/counter.js';
|
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
//
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
|
require('../fixtures/es-module-require-cache/preload.js');
|
||||||
|
require('../fixtures/es-module-require-cache/counter.js');
|
||||||
|
|
||||||
assert.strictEqual(global.counter, 1);
|
assert.strictEqual(global.counter, 1);
|
||||||
delete global.counter;
|
delete global.counter;
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-shared-dep.mjs
|
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-shared-dep.mjs
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import { createRequire } from '../common/index.mjs';
|
||||||
|
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import '../fixtures/es-modules/test-esm-ok.mjs';
|
import '../fixtures/es-modules/test-esm-ok.mjs';
|
||||||
import dep from '../fixtures/es-module-loaders/loader-dep.js';
|
|
||||||
|
|
||||||
assert.strictEqual(dep.format, 'esm');
|
const require = createRequire(import.meta.url);
|
||||||
|
const dep = require('../fixtures/es-module-loaders/loader-dep.js');
|
||||||
|
|
||||||
|
assert.strictEqual(dep.format, 'module');
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#! }]) // isn't js
|
#! }]) // isn't js
|
||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import '../common/index.mjs';
|
||||||
|
|
||||||
const isJs = true;
|
const isJs = true;
|
||||||
export default isJs;
|
export default isJs;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
import '../fixtures/es-modules/esm-snapshot-mutator';
|
import '../common/index.mjs';
|
||||||
import one from '../fixtures/es-modules/esm-snapshot';
|
import '../fixtures/es-modules/esm-snapshot-mutator.js';
|
||||||
|
import one from '../fixtures/es-modules/esm-snapshot.js';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
assert.strictEqual(one, 1);
|
assert.strictEqual(one, 1);
|
||||||
|
35
test/es-module/test-esm-specifiers.mjs
Normal file
35
test/es-module/test-esm-specifiers.mjs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Flags: --experimental-modules --es-module-specifier-resolution=node
|
||||||
|
import { mustNotCall } from '../common';
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
// commonJS index.js
|
||||||
|
import commonjs from '../fixtures/es-module-specifiers/package-type-commonjs';
|
||||||
|
// esm index.js
|
||||||
|
import module from '../fixtures/es-module-specifiers/package-type-module';
|
||||||
|
// Notice the trailing slash
|
||||||
|
import success, { explicit, implicit, implicitModule, getImplicitCommonjs }
|
||||||
|
from '../fixtures/es-module-specifiers/';
|
||||||
|
|
||||||
|
assert.strictEqual(commonjs, 'commonjs');
|
||||||
|
assert.strictEqual(module, 'module');
|
||||||
|
assert.strictEqual(success, 'success');
|
||||||
|
assert.strictEqual(explicit, 'esm');
|
||||||
|
assert.strictEqual(implicit, 'esm');
|
||||||
|
assert.strictEqual(implicitModule, 'esm');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
await import('../fixtures/es-module-specifiers/do-not-exist.js');
|
||||||
|
} catch (e) {
|
||||||
|
// Files that do not exist should throw
|
||||||
|
assert.strictEqual(e.name, 'Error');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await getImplicitCommonjs();
|
||||||
|
} catch (e) {
|
||||||
|
// Legacy loader cannot resolve .mjs automatically from main
|
||||||
|
assert.strictEqual(e.name, 'Error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(mustNotCall);
|
@ -9,7 +9,7 @@ const fs = require('fs');
|
|||||||
tmpdir.refresh();
|
tmpdir.refresh();
|
||||||
|
|
||||||
const realPath = path.resolve(__dirname, '../fixtures/es-modules/symlink.mjs');
|
const realPath = path.resolve(__dirname, '../fixtures/es-modules/symlink.mjs');
|
||||||
const symlinkPath = path.resolve(tmpdir.path, 'symlink.js');
|
const symlinkPath = path.resolve(tmpdir.path, 'symlink.mjs');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs.symlinkSync(realPath, symlinkPath);
|
fs.symlinkSync(realPath, symlinkPath);
|
||||||
|
77
test/es-module/test-esm-symlink-type.js
Normal file
77
test/es-module/test-esm-symlink-type.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
const fixtures = require('../common/fixtures');
|
||||||
|
const path = require('path');
|
||||||
|
const assert = require('assert');
|
||||||
|
const exec = require('child_process').execFile;
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const tmpdir = require('../common/tmpdir');
|
||||||
|
tmpdir.refresh();
|
||||||
|
const tmpDir = tmpdir.path;
|
||||||
|
|
||||||
|
// Check that running the symlink executes the target as the correct type
|
||||||
|
const symlinks = [
|
||||||
|
{
|
||||||
|
source: 'extensionless-symlink-to-mjs-file',
|
||||||
|
target: fixtures.path('es-modules/mjs-file.mjs'),
|
||||||
|
prints: '.mjs file',
|
||||||
|
errorsWithPreserveSymlinksMain: false
|
||||||
|
}, {
|
||||||
|
source: 'extensionless-symlink-to-cjs-file',
|
||||||
|
target: fixtures.path('es-modules/cjs-file.cjs'),
|
||||||
|
prints: '.cjs file',
|
||||||
|
errorsWithPreserveSymlinksMain: false
|
||||||
|
}, {
|
||||||
|
source: 'extensionless-symlink-to-file-in-module-scope',
|
||||||
|
target: fixtures.path('es-modules/package-type-module/index.js'),
|
||||||
|
prints: 'package-type-module',
|
||||||
|
// The package scope of the symlinks' sources is commonjs, and this
|
||||||
|
// symlink's target is a .js file in a module scope, so when the scope
|
||||||
|
// is evaluated based on the source (commonjs) this esm file should error
|
||||||
|
errorsWithPreserveSymlinksMain: true
|
||||||
|
}, {
|
||||||
|
source: 'extensionless-symlink-to-file-in-explicit-commonjs-scope',
|
||||||
|
target: fixtures.path('es-modules/package-type-commonjs/index.js'),
|
||||||
|
prints: 'package-type-commonjs',
|
||||||
|
errorsWithPreserveSymlinksMain: false
|
||||||
|
}, {
|
||||||
|
source: 'extensionless-symlink-to-file-in-implicit-commonjs-scope',
|
||||||
|
target: fixtures.path('es-modules/package-without-type/index.js'),
|
||||||
|
prints: 'package-without-type',
|
||||||
|
errorsWithPreserveSymlinksMain: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
symlinks.forEach((symlink) => {
|
||||||
|
const mainPath = path.join(tmpDir, symlink.source);
|
||||||
|
fs.symlinkSync(symlink.target, mainPath);
|
||||||
|
|
||||||
|
const flags = [
|
||||||
|
'--experimental-modules',
|
||||||
|
'--experimental-modules --preserve-symlinks-main'
|
||||||
|
];
|
||||||
|
flags.forEach((nodeOptions) => {
|
||||||
|
const opts = {
|
||||||
|
env: Object.assign({}, process.env, { NODE_OPTIONS: nodeOptions })
|
||||||
|
};
|
||||||
|
exec(process.execPath, [mainPath], opts, common.mustCall(
|
||||||
|
(err, stdout) => {
|
||||||
|
if (nodeOptions.includes('--preserve-symlinks-main')) {
|
||||||
|
if (symlink.errorsWithPreserveSymlinksMain &&
|
||||||
|
err.toString().includes('Error')) return;
|
||||||
|
else if (!symlink.errorsWithPreserveSymlinksMain &&
|
||||||
|
stdout.includes(symlink.prints)) return;
|
||||||
|
assert.fail(`For ${JSON.stringify(symlink)}, ${
|
||||||
|
(symlink.errorsWithPreserveSymlinksMain) ?
|
||||||
|
'failed to error' : 'errored unexpectedly'
|
||||||
|
} with --preserve-symlinks-main`);
|
||||||
|
} else {
|
||||||
|
if (stdout.includes(symlink.prints)) return;
|
||||||
|
assert.fail(`For ${JSON.stringify(symlink)}, failed to find ` +
|
||||||
|
`${symlink.prints} in: <\n${stdout}\n>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
|
});
|
||||||
|
});
|
@ -12,8 +12,8 @@ const tmpDir = tmpdir.path;
|
|||||||
|
|
||||||
const entry = path.join(tmpDir, 'entry.mjs');
|
const entry = path.join(tmpDir, 'entry.mjs');
|
||||||
const real = path.join(tmpDir, 'index.mjs');
|
const real = path.join(tmpDir, 'index.mjs');
|
||||||
const link_absolute_path = path.join(tmpDir, 'absolute');
|
const link_absolute_path = path.join(tmpDir, 'absolute.mjs');
|
||||||
const link_relative_path = path.join(tmpDir, 'relative');
|
const link_relative_path = path.join(tmpDir, 'relative.mjs');
|
||||||
const link_ignore_extension = path.join(tmpDir,
|
const link_ignore_extension = path.join(tmpDir,
|
||||||
'ignore_extension.json');
|
'ignore_extension.json');
|
||||||
const link_directory = path.join(tmpDir, 'directory');
|
const link_directory = path.join(tmpDir, 'directory');
|
||||||
@ -22,15 +22,13 @@ fs.writeFileSync(real, 'export default [];');
|
|||||||
fs.writeFileSync(entry, `
|
fs.writeFileSync(entry, `
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import real from './index.mjs';
|
import real from './index.mjs';
|
||||||
import absolute from './absolute';
|
import absolute from './absolute.mjs';
|
||||||
import relative from './relative';
|
import relative from './relative.mjs';
|
||||||
import ignoreExtension from './ignore_extension.json';
|
import ignoreExtension from './ignore_extension.json';
|
||||||
import directory from './directory';
|
|
||||||
|
|
||||||
assert.strictEqual(absolute, real);
|
assert.strictEqual(absolute, real);
|
||||||
assert.strictEqual(relative, real);
|
assert.strictEqual(relative, real);
|
||||||
assert.strictEqual(ignoreExtension, real);
|
assert.strictEqual(ignoreExtension, real);
|
||||||
assert.strictEqual(directory, real);
|
|
||||||
`);
|
`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
// Flags: --experimental-modules
|
// Flags: --experimental-modules
|
||||||
import '../common';
|
/* eslint-disable node-core/required-modules */
|
||||||
|
|
||||||
|
import '../common/index.mjs';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
async function doTest() {
|
async function doTest() {
|
||||||
await assert.rejects(
|
await assert.rejects(
|
||||||
async () => {
|
async () => {
|
||||||
await import('../fixtures/es-module-loaders/throw-undefined');
|
await import('../fixtures/es-module-loaders/throw-undefined.mjs');
|
||||||
},
|
},
|
||||||
(e) => e === undefined
|
(e) => e === undefined
|
||||||
);
|
);
|
||||||
|
53
test/es-module/test-esm-type-flag-errors.js
Normal file
53
test/es-module/test-esm-type-flag-errors.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const exec = require('child_process').execFile;
|
||||||
|
|
||||||
|
const mjsFile = require.resolve('../fixtures/es-modules/mjs-file.mjs');
|
||||||
|
const cjsFile = require.resolve('../fixtures/es-modules/cjs-file.cjs');
|
||||||
|
const packageWithoutTypeMain =
|
||||||
|
require.resolve('../fixtures/es-modules/package-without-type/index.js');
|
||||||
|
const packageTypeCommonJsMain =
|
||||||
|
require.resolve('../fixtures/es-modules/package-type-commonjs/index.js');
|
||||||
|
const packageTypeModuleMain =
|
||||||
|
require.resolve('../fixtures/es-modules/package-type-module/index.js');
|
||||||
|
|
||||||
|
// Check that running `node` without options works
|
||||||
|
expect('', mjsFile, '.mjs file');
|
||||||
|
expect('', cjsFile, '.cjs file');
|
||||||
|
expect('', packageTypeModuleMain, 'package-type-module');
|
||||||
|
expect('', packageTypeCommonJsMain, 'package-type-commonjs');
|
||||||
|
expect('', packageWithoutTypeMain, 'package-without-type');
|
||||||
|
|
||||||
|
// Check that running with --entry-type and no package.json "type" works
|
||||||
|
expect('--entry-type=commonjs', packageWithoutTypeMain, 'package-without-type');
|
||||||
|
expect('--entry-type=module', packageWithoutTypeMain, 'package-without-type');
|
||||||
|
|
||||||
|
// Check that running with conflicting --entry-type flags throws errors
|
||||||
|
expect('--entry-type=commonjs', mjsFile, 'ERR_ENTRY_TYPE_MISMATCH', true);
|
||||||
|
expect('--entry-type=module', cjsFile, 'ERR_ENTRY_TYPE_MISMATCH', true);
|
||||||
|
expect('--entry-type=commonjs', packageTypeModuleMain,
|
||||||
|
'ERR_ENTRY_TYPE_MISMATCH', true);
|
||||||
|
expect('--entry-type=module', packageTypeCommonJsMain,
|
||||||
|
'ERR_ENTRY_TYPE_MISMATCH', true);
|
||||||
|
|
||||||
|
function expect(opt = '', inputFile, want, wantsError = false) {
|
||||||
|
// TODO: Remove when --experimental-modules is unflagged
|
||||||
|
opt = `--experimental-modules ${opt}`;
|
||||||
|
const argv = [inputFile];
|
||||||
|
const opts = {
|
||||||
|
env: Object.assign({}, process.env, { NODE_OPTIONS: opt }),
|
||||||
|
maxBuffer: 1e6,
|
||||||
|
};
|
||||||
|
exec(process.execPath, argv, opts, common.mustCall((err, stdout, stderr) => {
|
||||||
|
if (wantsError) {
|
||||||
|
stdout = stderr;
|
||||||
|
} else {
|
||||||
|
assert.ifError(err);
|
||||||
|
}
|
||||||
|
if (stdout.includes(want)) return;
|
||||||
|
|
||||||
|
const o = JSON.stringify(opt);
|
||||||
|
assert.fail(`For ${o}, failed to find ${want} in: <\n${stdout}\n>`);
|
||||||
|
}));
|
||||||
|
}
|
11
test/es-module/test-esm-type-flag.mjs
Normal file
11
test/es-module/test-esm-type-flag.mjs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Flags: --experimental-modules --entry-type=module
|
||||||
|
/* eslint-disable node-core/required-modules */
|
||||||
|
import cjs from '../fixtures/baz.js';
|
||||||
|
import '../common/index.mjs';
|
||||||
|
import { message } from '../fixtures/es-modules/message.mjs';
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
// Assert we loaded esm dependency as ".js" in this mode
|
||||||
|
assert.strictEqual(message, 'A message');
|
||||||
|
// Assert we loaded CommonJS dependency
|
||||||
|
assert.strictEqual(cjs, 'perhaps I work');
|
@ -29,6 +29,6 @@ export function resolve(specifier, parentModuleURL = baseURL /*, defaultResolve
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
url: resolved.href,
|
url: resolved.href,
|
||||||
format: 'esm'
|
format: 'module'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,6 @@ export function resolve (specifier, base = baseURL) {
|
|||||||
const url = new URL(specifier, base).href;
|
const url = new URL(specifier, base).href;
|
||||||
return {
|
return {
|
||||||
url,
|
url,
|
||||||
format: 'esm'
|
format: 'module'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
exports.format = 'esm';
|
exports.format = 'module';
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable node-core/required-modules */
|
||||||
export async function resolve(specifier, parentModuleURL, defaultResolve) {
|
export async function resolve(specifier, parentModuleURL, defaultResolve) {
|
||||||
if (parentModuleURL && specifier === '../fixtures/es-modules/test-esm-ok.mjs') {
|
if (parentModuleURL && specifier === '../fixtures/es-modules/test-esm-ok.mjs') {
|
||||||
return {
|
return {
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import dep from './loader-dep.js';
|
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
|
import {createRequire} from '../../common/index.mjs';
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const dep = require('./loader-dep.js');
|
||||||
|
|
||||||
export function resolve(specifier, base, defaultResolve) {
|
export function resolve(specifier, base, defaultResolve) {
|
||||||
assert.strictEqual(dep.format, 'esm');
|
assert.strictEqual(dep.format, 'module');
|
||||||
return defaultResolve(specifier, base);
|
return defaultResolve(specifier, base);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import dep from './loader-dep.js';
|
import {createRequire} from '../../common/index.mjs';
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const dep = require('./loader-dep.js');
|
||||||
|
|
||||||
export function resolve (specifier, base, defaultResolve) {
|
export function resolve (specifier, base, defaultResolve) {
|
||||||
return {
|
return {
|
||||||
url: defaultResolve(specifier, base).url,
|
url: defaultResolve(specifier, base).url,
|
||||||
|
@ -12,11 +12,11 @@ export async function resolve (specifier, base, defaultResolve) {
|
|||||||
await defaultResolve(specifier, base);
|
await defaultResolve(specifier, base);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
assert.strictEqual(e.code, 'MODULE_NOT_FOUND');
|
assert.strictEqual(e.code, 'ERR_MODULE_NOT_FOUND');
|
||||||
return {
|
return {
|
||||||
format: 'builtin',
|
format: 'builtin',
|
||||||
url: 'fs'
|
url: 'fs'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
assert.fail(`Module resolution for ${specifier} should be throw MODULE_NOT_FOUND`);
|
assert.fail(`Module resolution for ${specifier} should be throw ERR_MODULE_NOT_FOUND`);
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
import { foo, notfound } from './module-named-exports';
|
import { foo, notfound } from './module-named-exports.mjs';
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
/* eslint-disable node-core/required-modules */
|
||||||
|
|
||||||
throw undefined;
|
throw undefined;
|
||||||
|
10
test/fixtures/es-module-specifiers/index.mjs
vendored
Normal file
10
test/fixtures/es-module-specifiers/index.mjs
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import explicit from 'explicit-main';
|
||||||
|
import implicit from 'implicit-main';
|
||||||
|
import implicitModule from 'implicit-main-type-module';
|
||||||
|
|
||||||
|
function getImplicitCommonjs () {
|
||||||
|
return import('implicit-main-type-commonjs');
|
||||||
|
}
|
||||||
|
|
||||||
|
export {explicit, implicit, implicitModule, getImplicitCommonjs};
|
||||||
|
export default 'success';
|
1
test/fixtures/es-module-specifiers/node_modules/explicit-main/entry.mjs
generated
vendored
Normal file
1
test/fixtures/es-module-specifiers/node_modules/explicit-main/entry.mjs
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default 'esm';
|
3
test/fixtures/es-module-specifiers/node_modules/explicit-main/package.json
generated
vendored
Normal file
3
test/fixtures/es-module-specifiers/node_modules/explicit-main/package.json
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"main": "entry.mjs"
|
||||||
|
}
|
1
test/fixtures/es-module-specifiers/node_modules/implicit-main-type-commonjs/entry.mjs
generated
vendored
Normal file
1
test/fixtures/es-module-specifiers/node_modules/implicit-main-type-commonjs/entry.mjs
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default 'esm';
|
4
test/fixtures/es-module-specifiers/node_modules/implicit-main-type-commonjs/package.json
generated
vendored
Normal file
4
test/fixtures/es-module-specifiers/node_modules/implicit-main-type-commonjs/package.json
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"main": "entry",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
1
test/fixtures/es-module-specifiers/node_modules/implicit-main-type-module/entry.js
generated
vendored
Normal file
1
test/fixtures/es-module-specifiers/node_modules/implicit-main-type-module/entry.js
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default 'nope';
|
1
test/fixtures/es-module-specifiers/node_modules/implicit-main-type-module/entry.mjs
generated
vendored
Normal file
1
test/fixtures/es-module-specifiers/node_modules/implicit-main-type-module/entry.mjs
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default 'esm';
|
4
test/fixtures/es-module-specifiers/node_modules/implicit-main-type-module/package.json
generated
vendored
Normal file
4
test/fixtures/es-module-specifiers/node_modules/implicit-main-type-module/package.json
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"main": "entry",
|
||||||
|
"type": "module"
|
||||||
|
}
|
1
test/fixtures/es-module-specifiers/node_modules/implicit-main/entry.js
generated
vendored
Normal file
1
test/fixtures/es-module-specifiers/node_modules/implicit-main/entry.js
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = 'cjs';
|
1
test/fixtures/es-module-specifiers/node_modules/implicit-main/entry.mjs
generated
vendored
Normal file
1
test/fixtures/es-module-specifiers/node_modules/implicit-main/entry.mjs
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default 'esm';
|
3
test/fixtures/es-module-specifiers/node_modules/implicit-main/package.json
generated
vendored
Normal file
3
test/fixtures/es-module-specifiers/node_modules/implicit-main/package.json
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"main": "entry"
|
||||||
|
}
|
1
test/fixtures/es-module-specifiers/package-type-commonjs/a.js
vendored
Normal file
1
test/fixtures/es-module-specifiers/package-type-commonjs/a.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = 'a';
|
1
test/fixtures/es-module-specifiers/package-type-commonjs/b.mjs
vendored
Normal file
1
test/fixtures/es-module-specifiers/package-type-commonjs/b.mjs
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const b = 'b';
|
5
test/fixtures/es-module-specifiers/package-type-commonjs/c.cjs
vendored
Normal file
5
test/fixtures/es-module-specifiers/package-type-commonjs/c.cjs
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
one: 1,
|
||||||
|
two: 2,
|
||||||
|
three: 3
|
||||||
|
};
|
21
test/fixtures/es-module-specifiers/package-type-commonjs/index.mjs
vendored
Normal file
21
test/fixtures/es-module-specifiers/package-type-commonjs/index.mjs
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// js file that is common.js
|
||||||
|
import a from './a.js';
|
||||||
|
// ESM with named export
|
||||||
|
import {b} from './b.mjs';
|
||||||
|
// import 'c.cjs';
|
||||||
|
import cjs from './c.cjs';
|
||||||
|
// proves cross boundary fun bits
|
||||||
|
import jsAsEsm from '../package-type-module/a.js';
|
||||||
|
|
||||||
|
// named export from core
|
||||||
|
import {strictEqual, deepStrictEqual} from 'assert';
|
||||||
|
|
||||||
|
strictEqual(a, jsAsEsm);
|
||||||
|
strictEqual(b, 'b');
|
||||||
|
deepStrictEqual(cjs, {
|
||||||
|
one: 1,
|
||||||
|
two: 2,
|
||||||
|
three: 3
|
||||||
|
});
|
||||||
|
|
||||||
|
export default 'commonjs';
|
3
test/fixtures/es-module-specifiers/package-type-commonjs/package.json
vendored
Normal file
3
test/fixtures/es-module-specifiers/package-type-commonjs/package.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
1
test/fixtures/es-module-specifiers/package-type-module/a.js
vendored
Normal file
1
test/fixtures/es-module-specifiers/package-type-module/a.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default 'a'
|
1
test/fixtures/es-module-specifiers/package-type-module/b.mjs
vendored
Normal file
1
test/fixtures/es-module-specifiers/package-type-module/b.mjs
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const b = 'b';
|
5
test/fixtures/es-module-specifiers/package-type-module/c.cjs
vendored
Normal file
5
test/fixtures/es-module-specifiers/package-type-module/c.cjs
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
one: 1,
|
||||||
|
two: 2,
|
||||||
|
three: 3
|
||||||
|
};
|
21
test/fixtures/es-module-specifiers/package-type-module/index.js
vendored
Normal file
21
test/fixtures/es-module-specifiers/package-type-module/index.js
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// ESM with only default
|
||||||
|
import a from './a.js';
|
||||||
|
// ESM with named export
|
||||||
|
import {b} from './b.mjs';
|
||||||
|
// import 'c.cjs';
|
||||||
|
import cjs from './c.cjs';
|
||||||
|
// import across boundaries
|
||||||
|
import jsAsCjs from '../package-type-commonjs/a.js'
|
||||||
|
|
||||||
|
// named export from core
|
||||||
|
import {strictEqual, deepStrictEqual} from 'assert';
|
||||||
|
|
||||||
|
strictEqual(a, jsAsCjs);
|
||||||
|
strictEqual(b, 'b');
|
||||||
|
deepStrictEqual(cjs, {
|
||||||
|
one: 1,
|
||||||
|
two: 2,
|
||||||
|
three: 3
|
||||||
|
});
|
||||||
|
|
||||||
|
export default 'module';
|
3
test/fixtures/es-module-specifiers/package-type-module/package.json
vendored
Normal file
3
test/fixtures/es-module-specifiers/package-type-module/package.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
1
test/fixtures/es-module-specifiers/package.json
vendored
Normal file
1
test/fixtures/es-module-specifiers/package.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
1
test/fixtures/es-modules/cjs-file.cjs
vendored
Normal file
1
test/fixtures/es-modules/cjs-file.cjs
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
console.log('.cjs file');
|
7
test/fixtures/es-modules/json-cache/another.cjs
vendored
Normal file
7
test/fixtures/es-modules/json-cache/another.cjs
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const test = require('./test.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...test
|
||||||
|
};
|
||||||
|
|
||||||
|
test.one = 'it comes';
|
7
test/fixtures/es-modules/json-cache/mod.cjs
vendored
Normal file
7
test/fixtures/es-modules/json-cache/mod.cjs
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const test = require('./test.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...test
|
||||||
|
};
|
||||||
|
|
||||||
|
test.one = 'zalgo';
|
5
test/fixtures/es-modules/json-cache/test.json
vendored
Normal file
5
test/fixtures/es-modules/json-cache/test.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"one": 1,
|
||||||
|
"two": 2,
|
||||||
|
"three": 3
|
||||||
|
}
|
3
test/fixtures/es-modules/json.json
vendored
3
test/fixtures/es-modules/json.json
vendored
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"val": 42
|
|
||||||
}
|
|
2
test/fixtures/es-modules/loop.mjs
vendored
2
test/fixtures/es-modules/loop.mjs
vendored
@ -1,4 +1,4 @@
|
|||||||
import { message } from './message';
|
import { message } from './message.mjs';
|
||||||
|
|
||||||
var t = 1;
|
var t = 1;
|
||||||
var k = 1;
|
var k = 1;
|
||||||
|
1
test/fixtures/es-modules/mjs-file.mjs
vendored
Normal file
1
test/fixtures/es-modules/mjs-file.mjs
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
console.log('.mjs file');
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user