module: custom --conditions flag option

PR-URL: https://github.com/nodejs/node/pull/34637
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Geoffrey Booth <webmaster@geoffreybooth.com>
Reviewed-By: Jan Krems <jan.krems@gmail.com>
This commit is contained in:
Guy Bedford committed 2020-08-11 14:35:44 -07:00
1 parent 420da0ccd1
commit 77a515c31a
10 files changed
+93 -38

No files matched your search

+16
View File
@@ -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`
+15
View File
@@ -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
+4
View File
@@ -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
+33 -31
View File
@@ -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;
+4 -6
View File
@@ -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;
+5
View File
@@ -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)",
+1
View File
@@ -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;
@@ -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');
}));
});
+1
View File
@@ -0,0 +1 @@
module.exports = 'from custom condition';
+4 -1
View File
@@ -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": {