esm: replace --entry-type with --input-type

New flag is for string input only

PR-URL: https://github.com/nodejs/node/pull/27184
Reviewed-By: Jan Krems <jan.krems@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
This commit is contained in:
Geoffrey Booth 2019-04-15 10:29:56 -07:00 committed by Myles Borins
parent f85ef977e6
commit 96e46d37c4
No known key found for this signature in database
GPG Key ID: 933B01F40B5CA946
17 changed files with 119 additions and 130 deletions

View File

@ -134,19 +134,6 @@ added: v6.0.0
Enable FIPS-compliant crypto at startup. (Requires Node.js to be built with
`./configure --openssl-fips`.)
### `--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`.
### `--es-module-specifier-resolution=mode`
<!-- YAML
added: REPLACEME
@ -261,6 +248,17 @@ added: v0.11.15
Specify ICU data load path. (Overrides `NODE_ICU_DATA`.)
### `--input-type=type`
<!-- YAML
added: REPLACEME
-->
Used with `--experimental-modules`, this configures Node.js to interpret string
input as CommonJS or as an ES module. String input is input via `--eval`,
`--print`, or `STDIN`.
Valid values are `"commonjs"` and `"module"`. The default is `"commonjs"`.
### `--inspect-brk[=[host:]port]`
<!-- YAML
added: v7.6.0

View File

@ -854,18 +854,6 @@ provided.
Encoding provided to `TextDecoder()` API was not one of the
[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>
### ERR_FALSY_VALUE_REJECTION
@ -1166,6 +1154,14 @@ is set for the `Http2Stream`.
An option pair is incompatible with each other and can not be used at the same
time.
<a id="ERR_INPUT_TYPE_NOT_ALLOWED"></a>
### ERR_INPUT_TYPE_NOT_ALLOWED
> Stability: 1 - Experimental
The `--input-type` flag was used to attempt to execute a file. This flag can
only be used with input via `--eval`, `--print` or `STDIN`.
<a id="ERR_INSPECTOR_ALREADY_CONNECTED"></a>
### ERR_INSPECTOR_ALREADY_CONNECTED
@ -2223,6 +2219,18 @@ closed.
These errors have never been released, but had been present on master between
releases.
<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_FS_WATCHER_ALREADY_STARTED"></a>
#### ERR_FS_WATCHER_ALREADY_STARTED

View File

@ -30,45 +30,37 @@ specifier resolution, and default behavior.
The `--experimental-modules` flag can be used to enable support for
ECMAScript modules (ES modules).
## Running Node.js with an ECMAScript Module
Once enabled, Node.js will treat the following as ES modules when passed to
`node` as the initial input, or when referenced by `import` statements within
ES module code:
There are a few ways to start Node.js with an ES module as its input.
- Files ending in `.mjs`.
### Initial entry point with an <code>.mjs</code> extension
- Files ending in `.js`, or extensionless files, when the nearest parent
`package.json` file contains a top-level field `"type"` with a value of
`"module"`.
A file ending with `.mjs` passed to Node.js as an initial entry point will be
loaded as an ES module.
- Strings passed in as an argument to `--eval` or `--print`, or piped to
`node` via `STDIN`, with the flag `--input-type=module`.
```sh
node --experimental-modules my-app.mjs
```
Node.js will treat as CommonJS all other forms of input, such as `.js` files
where the nearest parent `package.json` file contains no top-level `"type"`
field, or string input without the flag `--input-type`. This behavior is to
preserve backward compatibility. However, now that Node.js supports both
CommonJS and ES modules, it is best to be explicit whenever possible. Node.js
will treat the following as CommonJS when passed to `node` as the initial input,
or when referenced by `import` statements within ES module code:
### <code>--entry-type=module</code> flag
- Files ending in `.cjs`.
Files ending with `.js` or `.mjs`, or lacking any extension,
will be loaded as ES modules when the `--entry-type=module` flag is set.
- Files ending in `.js`, or extensionless files, when the nearest parent
`package.json` file contains a top-level field `"type"` with a value of
`"commonjs"`.
```sh
node --experimental-modules --entry-type=module my-app.js
```
- Strings passed in as an argument to `--eval` or `--print`, or piped to
`node` via `STDIN`, with the flag `--input-type=commonjs`.
For completeness there is also `--entry-type=commonjs`, for explicitly running
a `.js` file as CommonJS. This is the default behavior if `--entry-type` is
unspecified.
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
## <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
@ -97,6 +89,14 @@ 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.
`import` statements of `.js` and extensionless files are treated as ES modules
if the nearest parent `package.json` contains `"type": "module"`.
```js
// my-app.js, part of the same example as above
import './startup.js'; // Loaded as ES module because of package.json
```
## Package Scope and File Extensions
A folder containing a `package.json` file, and all subfolders below that
@ -156,6 +156,24 @@ package scope:
extension (since both `.js` and `.cjs` files are treated as CommonJS within a
`"commonjs"` package scope).
## <code>--input-type</code> flag
Strings passed in as an argument to `--eval` or `--print` (or `-e` or `-p`), or
piped to `node` via `STDIN`, will be treated as ES modules when the
`--input-type=module` flag is set.
```sh
node --experimental-modules --input-type=module --eval \
"import { sep } from 'path'; console.log(sep);"
echo "import { sep } from 'path'; console.log(sep);" | \
node --experimental-modules --input-type=module
```
For completeness there is also `--input-type=commonjs`, for explicitly running
string input as CommonJS. This is the default behavior if `--input-type` is
unspecified.
## Package Entry Points
The `package.json` `"main"` field defines the entry point for a package,

View File

@ -119,9 +119,6 @@ Enable FIPS-compliant crypto at startup.
Requires Node.js to be built with
.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'
.
@ -170,6 +167,9 @@ Specify ICU data load path.
Overrides
.Ev NODE_ICU_DATA .
.
.It Fl -input-type Ns = Ns Ar type
Set the module resolution type for input via --eval, --print or STDIN.
.
.It Fl -inspect-brk Ns = Ns Ar [host:]port
Activate inspector on
.Ar host:port

View File

@ -679,24 +679,6 @@ E('ERR_ENCODING_INVALID_ENCODED_DATA', function(encoding, ret) {
}, TypeError);
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported',
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) {
this.reason = reason;
return 'Promise was rejected with falsy value';
@ -809,6 +791,8 @@ E('ERR_HTTP_TRAILER_INVALID',
'Trailers are invalid with this transfer encoding', Error);
E('ERR_INCOMPATIBLE_OPTION_PAIR',
'Option "%s" can not be used in combination with option "%s"', TypeError);
E('ERR_INPUT_TYPE_NOT_ALLOWED', '--input-type can only be used with string ' +
'input via --eval, --print, or STDIN', Error);
E('ERR_INSPECTOR_ALREADY_CONNECTED', '%s is already connected', Error);
E('ERR_INSPECTOR_CLOSED', 'Session was closed', Error);
E('ERR_INSPECTOR_COMMAND', 'Inspector error %d: %s', Error);

View File

@ -60,7 +60,7 @@ function checkSyntax(source, filename) {
if (experimentalModules) {
let isModule = false;
if (filename === '[stdin]' || filename === '[eval]') {
isModule = getOptionValue('--entry-type') === 'module';
isModule = getOptionValue('--input-type') === 'module';
} else {
const resolve = require('internal/modules/esm/default_resolve');
const { format } = resolve(pathToFileURL(filename).toString());

View File

@ -17,7 +17,7 @@ markBootstrapComplete();
readStdin((code) => {
process._eval = code;
if (require('internal/options').getOptionValue('--entry-type') === 'module')
if (require('internal/options').getOptionValue('--input-type') === 'module')
evalModule(process._eval);
else
evalScript('[stdin]', process._eval, process._breakFirstLine);

View File

@ -14,7 +14,7 @@ const source = getOptionValue('--eval');
prepareMainThreadExecution();
addBuiltinLibsToObject(global);
markBootstrapComplete();
if (getOptionValue('--entry-type') === 'module')
if (getOptionValue('--input-type') === 'module')
evalModule(source);
else
evalScript('[eval]', source, process._breakFirstLine);

View File

@ -15,11 +15,11 @@ const console = require('internal/console/global');
prepareMainThreadExecution();
// --entry-type flag not supported in REPL
if (require('internal/options').getOptionValue('--entry-type')) {
// --input-type flag not supported in REPL
if (require('internal/options').getOptionValue('--input-type')) {
// If we can't write to stderr, we'd like to make this a noop,
// so use console.error.
console.error('Cannot specify --entry-type for REPL');
console.error('Cannot specify --input-type for REPL');
process.exit(1);
}

View File

@ -9,12 +9,12 @@ const { getOptionValue } = require('internal/options');
const preserveSymlinks = getOptionValue('--preserve-symlinks');
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const experimentalJsonModules = getOptionValue('--experimental-json-modules');
const typeFlag = getOptionValue('--entry-type');
const typeFlag = getOptionValue('--input-type');
const { resolve: moduleWrapResolve,
getPackageType } = internalBinding('module_wrap');
const { pathToFileURL, fileURLToPath } = require('internal/url');
const { ERR_ENTRY_TYPE_MISMATCH,
const { ERR_INPUT_TYPE_NOT_ALLOWED,
ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes;
const {
@ -25,7 +25,7 @@ const {
const realpathCache = new SafeMap();
// const TYPE_NONE = 0;
const TYPE_COMMONJS = 1;
// const TYPE_COMMONJS = 1;
const TYPE_MODULE = 2;
const extensionFormatMap = {
@ -86,26 +86,16 @@ function resolve(specifier, parentURL) {
let format = extMap[ext];
if (isMain && typeFlag) {
// Conflict between explicit extension (.mjs, .cjs) and --entry-type
if (ext === '.cjs' && typeFlag === 'module' ||
ext === '.mjs' && typeFlag === 'commonjs') {
throw new ERR_ENTRY_TYPE_MISMATCH(
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');
}
}
// This is the initial entry point to the program, and --input-type has
// been passed as an option; but --input-type can only be used with
// --eval, --print or STDIN string input. It is not allowed with file
// input, to avoid user confusion over how expansive the effect of the
// flag should be (i.e. entry point only, package scope surrounding the
// entry point, etc.).
throw new ERR_INPUT_TYPE_NOT_ALLOWED();
}
if (!format) {
if (isMain && typeFlag)
format = typeFlag;
else if (isMain)
if (isMain)
format = type === TYPE_MODULE ? 'module' : 'commonjs';
else
throw new ERR_UNKNOWN_FILE_EXTENSION(fileURLToPath(url),

View File

@ -109,11 +109,11 @@ void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors) {
if (!module_type.empty()) {
if (!experimental_modules) {
errors->push_back("--entry-type requires "
errors->push_back("--input-type requires "
"--experimental-modules to be enabled");
}
if (module_type != "commonjs" && module_type != "module") {
errors->push_back("--entry-type must be \"module\" or \"commonjs\"");
errors->push_back("--input-type must be \"module\" or \"commonjs\"");
}
}
@ -289,15 +289,15 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"(default: llhttp).",
&EnvironmentOptions::http_parser,
kAllowedInEnvironment);
AddOption("--input-type",
"set module type for string input",
&EnvironmentOptions::module_type,
kAllowedInEnvironment);
AddOption("--loader",
"(with --experimental-modules) use the specified file as a "
"custom loader",
&EnvironmentOptions::userland_loader,
kAllowedInEnvironment);
AddOption("--entry-type",
"set module type name of the entry point",
&EnvironmentOptions::module_type,
kAllowedInEnvironment);
AddOption("--es-module-specifier-resolution",
"Select extension resolution algorithm for es modules; "
"either 'explicit' (default) or 'node'",

View File

@ -5,14 +5,13 @@ const fixtures = require('../common/fixtures');
const { spawn } = require('child_process');
const assert = require('assert');
const entry = fixtures.path('/es-modules/noext-esm');
const entry = fixtures.path('/es-modules/package-type-module/noext-esm');
// Run a module that does not have extension
// This is to ensure the --entry-type works as expected
// Run a module that does not have extension.
// This is to ensure that "type": "module" applies to extensionless files.
const child = spawn(process.execPath, [
'--experimental-modules',
'--entry-type=module',
entry
]);

View File

@ -19,17 +19,9 @@ 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);
// Check that --input-type isn't allowed for files
expect('--input-type=module', packageTypeModuleMain,
'ERR_INPUT_TYPE_NOT_ALLOWED', true);
function expect(opt = '', inputFile, want, wantsError = false) {
// TODO: Remove when --experimental-modules is unflagged

View File

@ -1,4 +1,4 @@
// Flags: --experimental-modules --entry-type=module
// Flags: --experimental-modules
/* eslint-disable node-core/required-modules */
import cjs from '../fixtures/baz.js';
import '../common/index.mjs';

View File

@ -34,12 +34,12 @@ syntaxArgs.forEach(function(arg) {
assert.strictEqual(c.status, 1);
});
// Check --entry-type=module
// Check --input-type=module
syntaxArgs.forEach(function(arg) {
const stdin = 'export var p = 5; var foo bar;';
const c = spawnSync(
node,
['--experimental-modules', '--entry-type=module', '--no-warnings', arg],
['--experimental-modules', '--input-type=module', '--no-warnings', arg],
{ encoding: 'utf8', input: stdin }
);

View File

@ -25,12 +25,12 @@ syntaxArgs.forEach(function(arg) {
assert.strictEqual(c.status, 0);
});
// Check --entry-type=module
// Check --input-type=module
syntaxArgs.forEach(function(arg) {
const stdin = 'export var p = 5; throw new Error("should not get run");';
const c = spawnSync(
node,
['--experimental-modules', '--no-warnings', '--entry-type=module', arg],
['--experimental-modules', '--no-warnings', '--input-type=module', arg],
{ encoding: 'utf8', input: stdin }
);