esm: port loader code to JS

There is no reason for this to be in C++. Using JavaScript means that
the code is more accessible to more developers, which is important
for any Node.js feature. This also simplifies the code significantly
in some areas. On the technical side, this potentially also enables
making some of the file system operations that are involved
asynchronous.

PR-URL: https://github.com/nodejs/node/pull/32201
Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Reviewed-By: Guy Bedford <guybedford@gmail.com>
This commit is contained in:
Anna Henningsen 2020-03-07 06:12:08 +01:00
parent 417d847de2
commit 605615e5f3
No known key found for this signature in database
GPG Key ID: A94130F0BFC8EBE9
9 changed files with 658 additions and 1021 deletions

View File

@ -1078,10 +1078,17 @@ E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s', TypeError);
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent', TypeError);
E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]', TypeError);
E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s', TypeError);
E('ERR_INVALID_MODULE_SPECIFIER', (pkgPath, subpath) => {
assert(subpath !== '.');
return `Package subpath '${subpath}' is not a valid module request for the ` +
`"exports" resolution of ${pkgPath}${sep}package.json`;
E('ERR_INVALID_MODULE_SPECIFIER', (pkgPath, subpath, base = undefined) => {
if (subpath === undefined) {
return `Invalid package name '${pkgPath}' imported from ${base}`;
} else if (base === undefined) {
assert(subpath !== '.');
return `Package subpath '${subpath}' is not a valid module request for ` +
`the "exports" resolution of ${pkgPath}${sep}package.json`;
} else {
return `Package subpath '${subpath}' is not a valid module request for ` +
`the "exports" resolution of ${pkgPath} imported from ${base}`;
}
}, TypeError);
E('ERR_INVALID_OPT_VALUE', (name, value) =>
`The value "${String(value)}" is invalid for option "${name}"`,
@ -1089,18 +1096,32 @@ E('ERR_INVALID_OPT_VALUE', (name, value) =>
RangeError);
E('ERR_INVALID_OPT_VALUE_ENCODING',
'The value "%s" is invalid for option "encoding"', TypeError);
E('ERR_INVALID_PACKAGE_CONFIG',
`Invalid package config %s${sep}package.json, %s`, Error);
E('ERR_INVALID_PACKAGE_TARGET', (pkgPath, key, subpath, target) => {
if (key === '.') {
return `Invalid "exports" main target ${JSONStringify(target)} defined ` +
`in the package config ${pkgPath}${sep}package.json`;
} else {
return `Invalid "exports" target ${JSONStringify(target)} defined for '${
StringPrototypeSlice(key, 0, -subpath.length || key.length)}' in the ` +
`package config ${pkgPath}${sep}package.json`;
}
E('ERR_INVALID_PACKAGE_CONFIG', (path, message, hasMessage = true) => {
if (hasMessage)
return `Invalid package config ${path}${sep}package.json, ${message}`;
else
return `Invalid JSON in ${path} imported from ${message}`;
}, Error);
E('ERR_INVALID_PACKAGE_TARGET',
(pkgPath, key, subpath, target, base = undefined) => {
if (key === null) {
if (subpath !== '') {
return `Invalid "exports" target ${JSONStringify(target)} defined ` +
`for '${subpath}' in the package config ${pkgPath} imported from ` +
base;
} else {
return `Invalid "exports" main target ${target} defined in the ` +
`package config ${pkgPath} imported from ${base}.`;
}
} else if (key === '.') {
return `Invalid "exports" main target ${JSONStringify(target)} defined ` +
`in the package config ${pkgPath}${sep}package.json`;
} else {
return `Invalid "exports" target ${JSONStringify(target)} defined for '${
StringPrototypeSlice(key, 0, -subpath.length || key.length)}' in the ` +
`package config ${pkgPath}${sep}package.json`;
}
}, Error);
E('ERR_INVALID_PERFORMANCE_MARK',
'The "%s" performance mark has not been set', Error);
E('ERR_INVALID_PROTOCOL',
@ -1211,6 +1232,9 @@ E('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK',
'The ES Module loader may not return a format of \'dynamic\' when no ' +
'dynamicInstantiate function was provided', Error);
E('ERR_MISSING_OPTION', '%s is required', TypeError);
E('ERR_MODULE_NOT_FOUND', (path, base, type = 'package') => {
return `Cannot find ${type} '${path}' imported from ${base}`;
}, Error);
E('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times', Error);
E('ERR_NAPI_CONS_FUNCTION', 'Constructor must be a function', TypeError);
E('ERR_NAPI_INVALID_DATAVIEW_ARGS',
@ -1245,12 +1269,15 @@ E('ERR_OUT_OF_RANGE',
msg += ` It must be ${range}. Received ${received}`;
return msg;
}, RangeError);
E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath) => {
E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath, base = undefined) => {
if (subpath === '.') {
return `No "exports" main resolved in ${pkgPath}${sep}package.json`;
} else {
} else if (base === undefined) {
return `Package subpath '${subpath}' is not defined by "exports" in ${
pkgPath}${sep}package.json`;
} else {
return `Package subpath '${subpath}' is not defined by "exports" in ${
pkgPath} imported from ${base}`;
}
}, Error);
E('ERR_REQUIRE_ESM',

View File

@ -1,5 +1,5 @@
'use strict';
const { StringPrototypeStartsWith } = primordials;
const { extname } = require('path');
const { getOptionValue } = require('internal/options');
@ -7,14 +7,10 @@ const experimentalJsonModules = getOptionValue('--experimental-json-modules');
const experimentalSpeciferResolution =
getOptionValue('--experimental-specifier-resolution');
const experimentalWasmModules = getOptionValue('--experimental-wasm-modules');
const { getPackageType } = internalBinding('module_wrap');
const { getPackageType } = require('internal/modules/esm/resolve');
const { URL, fileURLToPath } = require('internal/url');
const { ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes;
// const TYPE_NONE = 0;
// const TYPE_COMMONJS = 1;
const TYPE_MODULE = 2;
const extensionFormatMap = {
'__proto__': null,
'.cjs': 'commonjs',
@ -37,8 +33,8 @@ if (experimentalWasmModules)
if (experimentalJsonModules)
extensionFormatMap['.json'] = legacyExtensionFormatMap['.json'] = 'json';
function defaultGetFormat(url, context, defaultGetFormat) {
if (url.startsWith('nodejs:')) {
function defaultGetFormat(url, context, defaultGetFormatUnused) {
if (StringPrototypeStartsWith(url, 'nodejs:')) {
return { format: 'builtin' };
}
const parsed = new URL(url);
@ -55,8 +51,7 @@ function defaultGetFormat(url, context, defaultGetFormat) {
const ext = extname(parsed.pathname);
let format;
if (ext === '.js') {
format = getPackageType(parsed.href) === TYPE_MODULE ?
'module' : 'commonjs';
format = getPackageType(parsed.href) === 'module' ? 'module' : 'commonjs';
} else {
format = extensionFormatMap[ext];
}

View File

@ -1,26 +1,609 @@
'use strict';
const {
ArrayIsArray,
JSONParse,
JSONStringify,
ObjectGetOwnPropertyNames,
ObjectPrototypeHasOwnProperty,
SafeMap,
StringPrototypeEndsWith,
StringPrototypeIncludes,
StringPrototypeIndexOf,
StringPrototypeSlice,
StringPrototypeStartsWith,
StringPrototypeSubstr,
} = primordials;
const internalFS = require('internal/fs/utils');
const { NativeModule } = require('internal/bootstrap/loaders');
const { realpathSync } = require('fs');
const {
closeSync,
fstatSync,
openSync,
readFileSync,
realpathSync,
statSync,
Stats,
} = require('fs');
const { getOptionValue } = require('internal/options');
const { sep } = require('path');
const preserveSymlinks = getOptionValue('--preserve-symlinks');
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const typeFlag = getOptionValue('--input-type');
const { resolve: moduleWrapResolve } = internalBinding('module_wrap');
const { URL, pathToFileURL, fileURLToPath } = require('internal/url');
const { ERR_INPUT_TYPE_NOT_ALLOWED,
ERR_UNSUPPORTED_ESM_URL_SCHEME } = require('internal/errors').codes;
const {
ERR_INPUT_TYPE_NOT_ALLOWED,
ERR_INVALID_MODULE_SPECIFIER,
ERR_INVALID_PACKAGE_CONFIG,
ERR_INVALID_PACKAGE_TARGET,
ERR_MODULE_NOT_FOUND,
ERR_PACKAGE_PATH_NOT_EXPORTED,
ERR_UNSUPPORTED_ESM_URL_SCHEME,
} = require('internal/errors').codes;
const realpathCache = new SafeMap();
const packageJSONCache = new SafeMap(); /* string -> PackageConfig */
function defaultResolve(specifier, { parentURL } = {}, defaultResolve) {
function tryStatSync(path) {
try {
return statSync(path);
} catch {
return new Stats();
}
}
function readIfFile(path) {
let fd;
try {
fd = openSync(path, 'r');
} catch {
return undefined;
}
try {
if (!fstatSync(fd).isFile()) return undefined;
return readFileSync(fd, 'utf8');
} finally {
closeSync(fd);
}
}
function getPackageConfig(path, base) {
const existing = packageJSONCache.get(path);
if (existing !== undefined) {
if (!existing.isValid) {
throw new ERR_INVALID_PACKAGE_CONFIG(path, fileURLToPath(base), false);
}
return existing;
}
const source = readIfFile(path);
if (source === undefined) {
const packageConfig = {
exists: false,
main: undefined,
name: undefined,
isValid: true,
type: 'none',
exports: undefined
};
packageJSONCache.set(path, packageConfig);
return packageConfig;
}
let packageJSON;
try {
packageJSON = JSONParse(source);
} catch {
const packageConfig = {
exists: true,
main: undefined,
name: undefined,
isValid: false,
type: 'none',
exports: undefined
};
packageJSONCache.set(path, packageConfig);
return packageConfig;
}
let { main, name, type } = packageJSON;
const { exports } = packageJSON;
if (typeof main !== 'string') main = undefined;
if (typeof name !== 'string') name = undefined;
// Ignore unknown types for forwards compatibility
if (type !== 'module' && type !== 'commonjs') type = 'none';
const packageConfig = {
exists: true,
main,
name,
isValid: true,
type,
exports
};
packageJSONCache.set(path, packageConfig);
return packageConfig;
}
function getPackageScopeConfig(resolved, base) {
let packageJSONUrl = new URL('./package.json', resolved);
while (true) {
const packageJSONPath = packageJSONUrl.pathname;
if (StringPrototypeEndsWith(packageJSONPath, 'node_modules/package.json'))
break;
const packageConfig = getPackageConfig(fileURLToPath(packageJSONUrl), base);
if (packageConfig.exists) return packageConfig;
const lastPackageJSONUrl = packageJSONUrl;
packageJSONUrl = new URL('../package.json', packageJSONUrl);
// Terminates at root where ../package.json equals ../../package.json
// (can't just check "/package.json" for Windows support).
if (packageJSONUrl.pathname === lastPackageJSONUrl.pathname) break;
}
const packageConfig = {
exists: false,
main: undefined,
name: undefined,
isValid: true,
type: 'none',
exports: undefined
};
packageJSONCache.set(fileURLToPath(packageJSONUrl), packageConfig);
return packageConfig;
}
/*
* 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
*/
function fileExists(url) {
return tryStatSync(fileURLToPath(url)).isFile();
}
function legacyMainResolve(packageJSONUrl, packageConfig) {
let guess;
if (packageConfig.main !== undefined) {
// Note: fs check redundances will be handled by Descriptor cache here.
if (fileExists(guess = new URL(`./${packageConfig.main}`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
packageJSONUrl))) {
return guess;
}
// Fallthrough.
}
if (fileExists(guess = new URL('./index.js', packageJSONUrl))) {
return guess;
}
// So fs.
if (fileExists(guess = new URL('./index.json', packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL('./index.node', packageJSONUrl))) {
return guess;
}
// Not found.
return undefined;
}
function resolveExtensionsWithTryExactName(search) {
if (fileExists(search)) return search;
return resolveExtensions(search);
}
const extensions = ['.js', '.json', '.node', '.mjs'];
function resolveExtensions(search) {
for (let i = 0; i < extensions.length; i++) {
const extension = extensions[i];
const guess = new URL(`${search.pathname}${extension}`, search);
if (fileExists(guess)) return guess;
}
return undefined;
}
function resolveIndex(search) {
return resolveExtensions(new URL('index', search));
}
function finalizeResolution(resolved, base) {
if (getOptionValue('--experimental-specifier-resolution') === 'node') {
let file = resolveExtensionsWithTryExactName(resolved);
if (file !== undefined) return file;
if (!StringPrototypeEndsWith(resolved.pathname, '/')) {
file = resolveIndex(new URL(`${resolved.pathname}/`, base));
} else {
file = resolveIndex(resolved);
}
if (file !== undefined) return file;
throw new ERR_MODULE_NOT_FOUND(
resolved.pathname, fileURLToPath(base), 'module');
}
if (StringPrototypeEndsWith(resolved.pathname, '/')) return resolved;
const path = fileURLToPath(resolved);
if (!tryStatSync(path).isFile()) {
throw new ERR_MODULE_NOT_FOUND(
path || resolved.pathname, fileURLToPath(base), 'module');
}
return resolved;
}
function throwExportsNotFound(subpath, packageJSONUrl, base) {
throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
fileURLToPath(packageJSONUrl), subpath, fileURLToPath(base));
}
function throwSubpathInvalid(subpath, packageJSONUrl, base) {
throw new ERR_INVALID_MODULE_SPECIFIER(
fileURLToPath(packageJSONUrl), subpath, fileURLToPath(base));
}
function throwExportsInvalid(
subpath, target, packageJSONUrl, base) {
if (typeof target === 'object' && target !== null) {
target = JSONStringify(target, null, '');
} else if (ArrayIsArray(target)) {
target = `[${target}]`;
} else {
target = `${target}`;
}
throw new ERR_INVALID_PACKAGE_TARGET(
fileURLToPath(packageJSONUrl), null, subpath, target, fileURLToPath(base));
}
function resolveExportsTargetString(
target, subpath, match, packageJSONUrl, base) {
if (target[0] !== '.' || target[1] !== '/' ||
(subpath !== '' && target[target.length - 1] !== '/')) {
throwExportsInvalid(match, target, packageJSONUrl, base);
}
const resolved = new URL(target, packageJSONUrl);
const resolvedPath = resolved.pathname;
const packagePath = new URL('.', packageJSONUrl).pathname;
if (!StringPrototypeStartsWith(resolvedPath, packagePath) ||
StringPrototypeIncludes(
resolvedPath, '/node_modules/', packagePath.length - 1)) {
throwExportsInvalid(match, target, packageJSONUrl, base);
}
if (subpath === '') return resolved;
const subpathResolved = new URL(subpath, resolved);
const subpathResolvedPath = subpathResolved.pathname;
if (!StringPrototypeStartsWith(subpathResolvedPath, resolvedPath) ||
StringPrototypeIncludes(subpathResolvedPath,
'/node_modules/', packagePath.length - 1)) {
throwSubpathInvalid(match + subpath, packageJSONUrl, base);
}
return subpathResolved;
}
function isArrayIndex(key /* string */) { /* -> boolean */
const keyNum = +key;
if (`${keyNum}` !== key) return false;
return keyNum >= 0 && keyNum < 0xFFFF_FFFF;
}
function resolveExportsTarget(
packageJSONUrl, target, subpath, packageSubpath, base) {
if (typeof target === 'string') {
const resolved = resolveExportsTargetString(
target, subpath, packageSubpath, packageJSONUrl, base);
return finalizeResolution(resolved, base);
} else if (ArrayIsArray(target)) {
if (target.length === 0)
throwExportsInvalid(packageSubpath, target, packageJSONUrl, base);
let lastException;
for (let i = 0; i < target.length; i++) {
const targetItem = target[i];
let resolved;
try {
resolved = resolveExportsTarget(
packageJSONUrl, targetItem, subpath, packageSubpath, base);
} catch (e) {
lastException = e;
if (e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED' ||
e.code === 'ERR_INVALID_PACKAGE_TARGET') {
continue;
}
throw e;
}
return finalizeResolution(resolved, base);
}
throw lastException;
} else if (typeof target === 'object' && target !== null) {
const keys = ObjectGetOwnPropertyNames(target);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (isArrayIndex(key)) {
throw new ERR_INVALID_PACKAGE_CONFIG(
fileURLToPath(packageJSONUrl),
'"exports" cannot contain numeric property keys');
}
}
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key === 'node' || key === 'import' || key === 'default') {
const conditionalTarget = target[key];
try {
return resolveExportsTarget(
packageJSONUrl, conditionalTarget, subpath, packageSubpath, base);
} catch (e) {
if (e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') continue;
throw e;
}
}
}
throwExportsNotFound(packageSubpath, packageJSONUrl, base);
}
throwExportsInvalid(packageSubpath, target, packageJSONUrl, base);
}
function isConditionalExportsMainSugar(exports, packageJSONUrl, base) {
if (typeof exports === 'string' || ArrayIsArray(exports)) return true;
if (typeof exports !== 'object' || exports === null) return false;
const keys = ObjectGetOwnPropertyNames(exports);
let isConditionalSugar = false;
let i = 0;
for (let j = 0; j < keys.length; j++) {
const key = keys[j];
const curIsConditionalSugar = key === '' || key[0] !== '.';
if (i++ === 0) {
isConditionalSugar = curIsConditionalSugar;
} else if (isConditionalSugar !== curIsConditionalSugar) {
throw new ERR_INVALID_PACKAGE_CONFIG(
fileURLToPath(packageJSONUrl),
'"exports" cannot contain some keys starting with \'.\' and some not.' +
' The exports object must either be an object of package subpath keys' +
' or an object of main entry condition name keys only.');
}
}
return isConditionalSugar;
}
function packageMainResolve(packageJSONUrl, packageConfig, base) {
if (packageConfig.exists) {
const exports = packageConfig.exports;
if (exports !== undefined) {
if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) {
return resolveExportsTarget(packageJSONUrl, exports, '', '', base);
} else if (typeof exports === 'object' && exports !== null) {
const target = exports['.'];
if (target !== undefined)
return resolveExportsTarget(packageJSONUrl, target, '', '', base);
}
throw new ERR_PACKAGE_PATH_NOT_EXPORTED(packageJSONUrl, '.');
}
if (packageConfig.main !== undefined) {
const resolved = new URL(packageConfig.main, packageJSONUrl);
const path = fileURLToPath(resolved);
if (tryStatSync(path).isFile()) return resolved;
}
if (getOptionValue('--experimental-specifier-resolution') === 'node') {
if (packageConfig.main !== undefined) {
return finalizeResolution(
new URL(packageConfig.main, packageJSONUrl), base);
} else {
return finalizeResolution(
new URL('index', packageJSONUrl), base);
}
}
if (packageConfig.type !== 'module') {
return legacyMainResolve(packageJSONUrl, packageConfig);
}
}
throw new ERR_MODULE_NOT_FOUND(
fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base));
}
function packageExportsResolve(
packageJSONUrl, packageSubpath, packageConfig, base) /* -> URL */ {
const exports = packageConfig.exports;
if (exports === undefined ||
isConditionalExportsMainSugar(exports, packageJSONUrl, base)) {
throwExportsNotFound(packageSubpath, packageJSONUrl, base);
}
if (ObjectPrototypeHasOwnProperty(exports, packageSubpath)) {
const target = exports[packageSubpath];
const resolved = resolveExportsTarget(
packageJSONUrl, target, '', packageSubpath, base);
return finalizeResolution(resolved, base);
}
let bestMatch = '';
const keys = ObjectGetOwnPropertyNames(exports);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key[key.length - 1] !== '/') continue;
if (StringPrototypeStartsWith(packageSubpath, key) &&
key.length > bestMatch.length) {
bestMatch = key;
}
}
if (bestMatch) {
const target = exports[bestMatch];
const subpath = StringPrototypeSubstr(packageSubpath, bestMatch.length);
const resolved = resolveExportsTarget(
packageJSONUrl, target, subpath, packageSubpath, base);
return finalizeResolution(resolved, base);
}
throwExportsNotFound(packageSubpath, packageJSONUrl, base);
}
function getPackageType(url) {
const packageConfig = getPackageScopeConfig(url, url);
return packageConfig.type;
}
function packageResolve(specifier /* string */, base /* URL */) { /* -> URL */
let separatorIndex = StringPrototypeIndexOf(specifier, '/');
let validPackageName = true;
let isScoped = false;
if (specifier[0] === '@') {
isScoped = true;
if (separatorIndex === -1 || specifier.length === 0) {
validPackageName = false;
} else {
separatorIndex = StringPrototypeIndexOf(
specifier, '/', separatorIndex + 1);
}
}
const packageName = separatorIndex === -1 ?
specifier : StringPrototypeSlice(specifier, 0, separatorIndex);
// Package name cannot have leading . and cannot have percent-encoding or
// separators.
for (let i = 0; i < packageName.length; i++) {
if (packageName[i] === '%' || packageName[i] === '\\') {
validPackageName = false;
break;
}
}
if (!validPackageName) {
throw new ERR_INVALID_MODULE_SPECIFIER(
specifier, undefined, fileURLToPath(base));
}
const packageSubpath = separatorIndex === -1 ?
'' : '.' + StringPrototypeSlice(specifier, separatorIndex);
// ResolveSelf
const packageConfig = getPackageScopeConfig(base, base);
if (packageConfig.exists) {
// TODO(jkrems): Find a way to forward the pair/iterator already generated
// while executing GetPackageScopeConfig
let packageJSONUrl;
for (const [ filename, packageConfigCandidate ] of packageJSONCache) {
if (packageConfig === packageConfigCandidate) {
packageJSONUrl = pathToFileURL(filename);
break;
}
}
if (packageJSONUrl !== undefined &&
packageConfig.name === packageName &&
packageConfig.exports !== undefined) {
if (packageSubpath === './') {
return new URL('./', packageJSONUrl);
} else if (packageSubpath === '') {
return packageMainResolve(packageJSONUrl, packageConfig, base);
} else {
return packageExportsResolve(
packageJSONUrl, packageSubpath, packageConfig, base);
}
}
}
let packageJSONUrl =
new URL('./node_modules/' + packageName + '/package.json', base);
let packageJSONPath = fileURLToPath(packageJSONUrl);
let lastPath;
do {
const stat = tryStatSync(
StringPrototypeSlice(packageJSONPath, 0, packageJSONPath.length - 13));
if (!stat.isDirectory()) {
lastPath = packageJSONPath;
packageJSONUrl = new URL((isScoped ?
'../../../../node_modules/' : '../../../node_modules/') +
packageName + '/package.json', packageJSONUrl);
packageJSONPath = fileURLToPath(packageJSONUrl);
continue;
}
// Package match.
const packageConfig = getPackageConfig(packageJSONPath, base);
if (packageSubpath === './') {
return new URL('./', packageJSONUrl);
} else if (packageSubpath === '') {
return packageMainResolve(packageJSONUrl, packageConfig, base);
} else if (packageConfig.exports !== undefined) {
return packageExportsResolve(
packageJSONUrl, packageSubpath, packageConfig, base);
} else {
return finalizeResolution(
new URL(packageSubpath, packageJSONUrl), base);
}
// Cross-platform root check.
} while (packageJSONPath.length !== lastPath.length);
// eslint can't handle the above code.
// eslint-disable-next-line no-unreachable
throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base));
}
function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) {
if (specifier === '') return false;
if (specifier[0] === '/') return true;
if (specifier[0] === '.') {
if (specifier.length === 1 || specifier[1] === '/') return true;
if (specifier[1] === '.') {
if (specifier.length === 2 || specifier[2] === '/') return true;
}
}
return false;
}
function moduleResolve(specifier /* string */, base /* URL */) { /* -> URL */
// Order swapped from spec for minor perf gain.
// Ok since relative URLs cannot parse as URLs.
let resolved;
if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
resolved = new URL(specifier, base);
} else {
try {
resolved = new URL(specifier);
} catch {
return packageResolve(specifier, base);
}
}
return finalizeResolution(resolved, base);
}
function defaultResolve(specifier, { parentURL } = {}, defaultResolveUnused) {
let parsed;
try {
parsed = new URL(specifier);
@ -39,7 +622,7 @@ function defaultResolve(specifier, { parentURL } = {}, defaultResolve) {
url: 'nodejs:' + specifier
};
}
if (parentURL && parentURL.startsWith('data:')) {
if (parentURL && StringPrototypeStartsWith(parentURL, 'data:')) {
// This is gonna blow up, we want the error
new URL(specifier, parentURL);
}
@ -58,7 +641,7 @@ function defaultResolve(specifier, { parentURL } = {}, defaultResolve) {
throw new ERR_INPUT_TYPE_NOT_ALLOWED();
}
let url = moduleWrapResolve(specifier, parentURL);
let url = moduleResolve(specifier, new URL(parentURL));
if (isMain ? !preserveSymlinksMain : !preserveSymlinks) {
const urlPath = fileURLToPath(url);
@ -73,4 +656,8 @@ function defaultResolve(specifier, { parentURL } = {}, defaultResolve) {
return { url: `${url}` };
}
exports.defaultResolve = defaultResolve;
module.exports = {
defaultResolve,
getPackageType
};

View File

@ -219,7 +219,6 @@ constexpr size_t kFsStatsBufferLength =
V(dns_srv_string, "SRV") \
V(dns_txt_string, "TXT") \
V(done_string, "done") \
V(dot_string, ".") \
V(duration_string, "duration") \
V(ecdh_string, "ECDH") \
V(emit_warning_string, "emitWarning") \
@ -274,7 +273,6 @@ constexpr size_t kFsStatsBufferLength =
V(kind_string, "kind") \
V(library_string, "library") \
V(mac_string, "mac") \
V(main_string, "main") \
V(max_buffer_string, "maxBuffer") \
V(message_port_constructor_string, "MessagePort") \
V(message_port_string, "messagePort") \
@ -992,9 +990,6 @@ class Environment : public MemoryRetainer {
inline uint32_t get_next_script_id();
inline uint32_t get_next_function_id();
std::unordered_map<std::string, const loader::PackageConfig>
package_json_cache;
inline double* heap_statistics_buffer() const;
inline void set_heap_statistics_buffer(
std::shared_ptr<v8::BackingStore> backing_store);

File diff suppressed because it is too large Load Diff

View File

@ -65,8 +65,6 @@ class ModuleWrap : public BaseObject {
static void GetStaticDependencySpecifiers(
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(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetInitializeImportMetaObjectCallback(

View File

@ -43,9 +43,6 @@ void OnFatalError(const char* location, const char* message);
V(ERR_INVALID_ARG_VALUE, TypeError) \
V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \
V(ERR_INVALID_ARG_TYPE, TypeError) \
V(ERR_INVALID_MODULE_SPECIFIER, TypeError) \
V(ERR_INVALID_PACKAGE_CONFIG, Error) \
V(ERR_INVALID_PACKAGE_TARGET, Error) \
V(ERR_INVALID_TRANSFER_OBJECT, TypeError) \
V(ERR_MEMORY_ALLOCATION_FAILED, Error) \
V(ERR_MISSING_ARGS, TypeError) \
@ -53,9 +50,7 @@ void OnFatalError(const char* location, const char* message);
V(ERR_MISSING_PASSPHRASE, TypeError) \
V(ERR_MISSING_PLATFORM_FOR_WORKER, Error) \
V(ERR_NON_CONTEXT_AWARE_DISABLED, Error) \
V(ERR_MODULE_NOT_FOUND, Error) \
V(ERR_OUT_OF_RANGE, RangeError) \
V(ERR_PACKAGE_PATH_NOT_EXPORTED, Error) \
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \
V(ERR_SCRIPT_EXECUTION_TIMEOUT, Error) \
V(ERR_STRING_TOO_LONG, Error) \

View File

@ -129,8 +129,7 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js';
// The use of %2F escapes in paths fails loading
loadFixture('pkgexports/sub/..%2F..%2Fbar.js').catch(mustCall((err) => {
strictEqual(err.code, isRequire ? 'ERR_INVALID_FILE_URL_PATH' :
'ERR_MODULE_NOT_FOUND');
strictEqual(err.code, 'ERR_INVALID_FILE_URL_PATH');
}));
// Package export with numeric index properties must throw a validation error

View File

@ -1,10 +1,11 @@
(node:*) ExperimentalWarning: The ESM module loader is experimental.
(node:*) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
internal/modules/esm/resolve.js:*
let url = moduleWrapResolve(specifier, parentURL);
^
Error: Cannot find package 'i-dont-exist' imported from *
internal/modules/run_main.js:*
internalBinding('errors').triggerUncaughtException(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'i-dont-exist' imported from *
at packageResolve (internal/modules/esm/resolve.js:*:*)
at moduleResolve (internal/modules/esm/resolve.js:*:*)
at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:*:*)
at Loader.resolve (internal/modules/esm/loader.js:*:*)
at Loader.getModuleJob (internal/modules/esm/loader.js:*:*)
@ -12,7 +13,6 @@ Error: Cannot find package 'i-dont-exist' imported from *
at internal/process/esm_loader.js:*:*
at Object.initializeLoader (internal/process/esm_loader.js:*:*)
at runMainESM (internal/modules/run_main.js:*:*)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)
at internal/main/run_main_module.js:*:* {
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*) {
code: 'ERR_MODULE_NOT_FOUND'
}