Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/cli.md16
-rw-r--r--doc/api/esm.md15
-rw-r--r--doc/node.14
-rw-r--r--lib/internal/modules/cjs/loader.js64
-rw-r--r--lib/internal/modules/esm/resolve.js10
-rw-r--r--src/node_options.cc5
-rw-r--r--src/node_options.h1
-rw-r--r--test/es-module/test-esm-custom-exports.mjs10
-rw-r--r--test/fixtures/node_modules/pkgexports/custom-condition.js1
-rw-r--r--test/fixtures/node_modules/pkgexports/package.json5
10 files changed, 93 insertions, 38 deletions
diff --git a/doc/api/cli.md b/doc/api/cli.md
index 899c28f6f76..413784538d2 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -76,6 +76,21 @@ $ node --completion-bash > node_bash_completion
$ source node_bash_completion
```
+### `-u`, `--conditions=condition`
+<!-- YAML
+added: REPLACEME
+-->
+
+> Stability: 1 - Experimental
+
+Enable experimental support for custom conditional exports resolution
+conditions.
+
+Any number of custom string condition names are permitted.
+
+The default Node.js conditions of `"node"`, `"default"`, `"import"`, and
+`"require"` will always apply as defined.
+
### `--cpu-prof`
<!-- YAML
added: v12.0.0
@@ -1232,6 +1247,7 @@ node --require "./a.js" --require "./b.js"
Node.js options that are allowed are:
<!-- node-options-node start -->
+* `--conditions`, `-u`
* `--diagnostic-dir`
* `--disable-proto`
* `--enable-fips`
diff --git a/doc/api/esm.md b/doc/api/esm.md
index 551eef604ea..adbb55f0d82 100644
--- a/doc/api/esm.md
+++ b/doc/api/esm.md
@@ -501,6 +501,21 @@ a nested conditional does not have any mapping it will continue checking
the remaining conditions of the parent condition. In this way nested
conditions behave analogously to nested JavaScript `if` statements.
+#### Resolving user conditions
+
+When running Node.js, custom user conditions can be added with the
+`--conditions` or `-u` flag:
+
+```bash
+node --conditions=development main.js
+```
+
+which would then resolve the `"development"` condition in package imports and
+exports, while resolving the existing `"node"`, `"default"`, `"import"`, and
+`"require"` conditions as appropriate.
+
+Any number of custom conditions can be set with repeat flags.
+
#### Self-referencing a package using its name
Within a package, the values defined in the package’s
diff --git a/doc/node.1 b/doc/node.1
index 3da44752bd3..7eb877bb1c8 100644
--- a/doc/node.1
+++ b/doc/node.1
@@ -78,6 +78,10 @@ Aborting instead of exiting causes a core file to be generated for analysis.
.It Fl -completion-bash
Print source-able bash completion script for Node.js.
.
+.It Fl u , Fl -conditions Ar string
+Use custom conditional exports conditions
+.Ar string
+.
.It Fl -cpu-prof
Start the V8 CPU profiler on start up, and write the CPU profile to disk
before exit. If
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index 80cae532cbe..794f84c1285 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -85,6 +85,7 @@ const manifest = getOptionValue('--experimental-policy') ?
require('internal/process/policy').manifest :
null;
const { compileFunction } = internalBinding('contextify');
+const userConditions = getOptionValue('--conditions');
// Whether any user-provided CJS modules had been loaded (executed).
// Used for internal assertions.
@@ -491,8 +492,12 @@ function applyExports(basePath, expansion) {
if (typeof pkgExports === 'object') {
if (ObjectPrototypeHasOwnProperty(pkgExports, mappingKey)) {
const mapping = pkgExports[mappingKey];
- return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, '',
- mappingKey);
+ const resolved = resolveExportsTarget(
+ pathToFileURL(basePath + '/'), mapping, '', mappingKey);
+ if (resolved === null || resolved === undefined)
+ throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
+ basePath, mappingKey);
+ return resolved;
}
let dirMatch = '';
@@ -509,6 +514,9 @@ function applyExports(basePath, expansion) {
const subpath = StringPrototypeSlice(mappingKey, dirMatch.length);
const resolved = resolveExportsTarget(pathToFileURL(basePath + '/'),
mapping, subpath, mappingKey);
+ if (resolved === null || resolved === undefined)
+ throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
+ basePath, mappingKey + subpath);
// Extension searching for folder exports only
const rc = stat(resolved);
if (rc === 0) return resolved;
@@ -596,21 +604,29 @@ function resolveExportsTarget(baseUrl, target, subpath, mappingKey) {
throw new ERR_INVALID_MODULE_SPECIFIER(mappingKey + subpath, reason);
} else if (ArrayIsArray(target)) {
if (target.length === 0)
- throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
- baseUrl.pathname, mappingKey + subpath);
+ return null;
let lastException;
for (const targetValue of target) {
+ let resolved;
try {
- return resolveExportsTarget(baseUrl, targetValue, subpath, mappingKey);
+ resolved = resolveExportsTarget(baseUrl, targetValue, subpath,
+ mappingKey);
} catch (e) {
lastException = e;
- if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED' &&
- e.code !== 'ERR_INVALID_PACKAGE_TARGET')
+ if (e.code !== 'ERR_INVALID_PACKAGE_TARGET')
throw e;
}
+ if (resolved === undefined)
+ continue;
+ if (resolved === null) {
+ lastException = null;
+ continue;
+ }
+ return resolved;
}
// Throw last fallback error
- assert(lastException !== undefined);
+ if (lastException === undefined || lastException === null)
+ return lastException;
throw lastException;
} else if (typeof target === 'object' && target !== null) {
const keys = ObjectKeys(target);
@@ -619,30 +635,17 @@ function resolveExportsTarget(baseUrl, target, subpath, mappingKey) {
'contain numeric property keys.');
}
for (const p of keys) {
- switch (p) {
- case 'node':
- case 'require':
- try {
- return resolveExportsTarget(baseUrl, target[p], subpath,
- mappingKey);
- } catch (e) {
- if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw e;
- }
- break;
- case 'default':
- try {
- return resolveExportsTarget(baseUrl, target.default, subpath,
- mappingKey);
- } catch (e) {
- if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw e;
- }
+ if (cjsConditions.has(p) || p === 'default') {
+ const resolved = resolveExportsTarget(baseUrl, target[p], subpath,
+ mappingKey);
+ if (resolved === undefined)
+ continue;
+ return resolved;
}
}
- throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
- baseUrl.pathname, mappingKey + subpath);
+ return undefined;
} else if (target === null) {
- throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
- baseUrl.pathname, mappingKey + subpath);
+ return null;
}
throw new ERR_INVALID_PACKAGE_TARGET(baseUrl.pathname, mappingKey, target);
}
@@ -999,8 +1002,7 @@ Module._load = function(request, parent, isMain) {
return module.exports;
};
-// TODO: Use this set when resolving pkg#exports conditions.
-const cjsConditions = new SafeSet(['require', 'node']);
+const cjsConditions = new SafeSet(['require', 'node', ...userConditions]);
Module._resolveFilename = function(request, parent, isMain, options) {
if (NativeModule.canBeRequiredByUsers(request)) {
return request;
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
index 7ea59f30c68..7cf35529481 100644
--- a/lib/internal/modules/esm/resolve.js
+++ b/lib/internal/modules/esm/resolve.js
@@ -51,7 +51,8 @@ const {
const { Module: CJSModule } = require('internal/modules/cjs/loader');
const packageJsonReader = require('internal/modules/package_json_reader');
-const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import']);
+const userConditions = getOptionValue('--conditions');
+const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import', ...userConditions]);
const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS);
@@ -359,12 +360,9 @@ function isArrayIndex(key) {
function resolvePackageTarget(
packageJSONUrl, target, subpath, packageSubpath, base, internal, conditions) {
if (typeof target === 'string') {
- const resolved = resolvePackageTargetString(
+ return finalizeResolution(resolvePackageTargetString(
target, subpath, packageSubpath, packageJSONUrl, base, internal,
- conditions);
- if (resolved === null)
- return null;
- return finalizeResolution(resolved, base);
+ conditions), base);
} else if (ArrayIsArray(target)) {
if (target.length === 0)
return null;
diff --git a/src/node_options.cc b/src/node_options.cc
index 737c4c147d3..d3fdc459de1 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -281,6 +281,11 @@ DebugOptionsParser::DebugOptionsParser() {
}
EnvironmentOptionsParser::EnvironmentOptionsParser() {
+ AddOption("--conditions",
+ "additional user conditions for conditional exports and imports",
+ &EnvironmentOptions::conditions,
+ kAllowedInEnvironment);
+ AddAlias("-u", "--conditions");
AddOption("--diagnostic-dir",
"set dir for all output files"
" (default: current working directory)",
diff --git a/src/node_options.h b/src/node_options.h
index fe20022bb2f..3258d4b3f0d 100644
--- a/src/node_options.h
+++ b/src/node_options.h
@@ -100,6 +100,7 @@ class DebugOptions : public Options {
class EnvironmentOptions : public Options {
public:
bool abort_on_uncaught_exception = false;
+ std::vector<std::string> conditions;
bool enable_source_maps = false;
bool experimental_json_modules = false;
bool experimental_modules = false;
diff --git a/test/es-module/test-esm-custom-exports.mjs b/test/es-module/test-esm-custom-exports.mjs
new file mode 100644
index 00000000000..ad81abfdafd
--- /dev/null
+++ b/test/es-module/test-esm-custom-exports.mjs
@@ -0,0 +1,10 @@
+// Flags: --conditions=custom-condition -u another
+import { mustCall } from '../common/index.mjs';
+import { strictEqual } from 'assert';
+import { requireFixture, importFixture } from '../fixtures/pkgexports.mjs';
+[requireFixture, importFixture].forEach((loadFixture) => {
+ loadFixture('pkgexports/condition')
+ .then(mustCall((actual) => {
+ strictEqual(actual.default, 'from custom condition');
+ }));
+});
diff --git a/test/fixtures/node_modules/pkgexports/custom-condition.js b/test/fixtures/node_modules/pkgexports/custom-condition.js
new file mode 100644
index 00000000000..63d77460d8d
--- /dev/null
+++ b/test/fixtures/node_modules/pkgexports/custom-condition.js
@@ -0,0 +1 @@
+module.exports = 'from custom condition';
diff --git a/test/fixtures/node_modules/pkgexports/package.json b/test/fixtures/node_modules/pkgexports/package.json
index b99e5c7b79f..71406a407c4 100644
--- a/test/fixtures/node_modules/pkgexports/package.json
+++ b/test/fixtures/node_modules/pkgexports/package.json
@@ -21,7 +21,10 @@
"./nofallback2": [null, {}, "builtin:x"],
"./nodemodules": "./node_modules/internalpkg/x.js",
"./condition": [{
- "custom-condition": "./custom-condition.mjs",
+ "custom-condition": {
+ "import": "./custom-condition.mjs",
+ "require": "./custom-condition.js"
+ },
"import": "///overridden",
"require": {
"require": {