Skip to content

Commit 072c7d2

Browse files
committed
src,lib: reducing C++ calls of esm legacy main resolve
Instead of many C++ calls, now we make only one C++ call to return a enum number that represents the selected state.
1 parent 4bb06db commit 072c7d2

File tree

5 files changed

+353
-44
lines changed

5 files changed

+353
-44
lines changed
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Tests the impact on eager operations required for policies affecting
2+
// general startup, does not test lazy operations
3+
'use strict';
4+
const fs = require('node:fs');
5+
const path = require('node:path');
6+
const common = require('../common.js');
7+
8+
const tmpdir = require('../../test/common/tmpdir.js');
9+
const { pathToFileURL } = require('node:url');
10+
11+
const benchmarkDirectory =
12+
path.resolve(tmpdir.path, 'benchmark-import-meta-resolve');
13+
14+
const configs = {
15+
n: [1e4],
16+
packageJsonUrl: [
17+
'node_modules/test/package.json',
18+
],
19+
packageConfigMain: ['', './index.js'],
20+
resolvedFile: [
21+
'node_modules/test/index.js',
22+
'node_modules/test/index.json',
23+
'node_modules/test/index.node',
24+
'node_modules/non-exist',
25+
],
26+
};
27+
28+
const options = {
29+
flags: ['--expose-internals'],
30+
};
31+
32+
const bench = common.createBenchmark(main, configs, options);
33+
34+
function main(conf) {
35+
const { legacyMainResolve } = require('internal/modules/esm/resolve');
36+
tmpdir.refresh();
37+
38+
fs.mkdirSync(path.join(benchmarkDirectory, 'node_modules', 'test'), { recursive: true });
39+
fs.writeFileSync(path.join(benchmarkDirectory, conf.resolvedFile), '\n');
40+
41+
const packageJsonUrl = pathToFileURL(conf.packageJsonUrl);
42+
const packageConfigMain = { main: conf.packageConfigMain };
43+
44+
bench.start();
45+
46+
for (let i = 0; i < conf.n; i++) {
47+
try {
48+
legacyMainResolve(packageJsonUrl, packageConfigMain, undefined);
49+
} catch { /* empty */ }
50+
}
51+
52+
bench.end(conf.n);
53+
}

lib/internal/modules/esm/resolve.js

+37-44
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,16 @@ const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
3636
const experimentalNetworkImports =
3737
getOptionValue('--experimental-network-imports');
3838
const typeFlag = getOptionValue('--input-type');
39-
const { URL, pathToFileURL, fileURLToPath, isURL, toPathIfFileURL } = require('internal/url');
39+
const { URL, pathToFileURL, fileURLToPath, isURL } = require('internal/url');
4040
const { canParse: URLCanParse } = internalBinding('url');
41+
const { legacyMainResolve: URLLegacyMainResolve } = internalBinding('fs');
4142
const {
4243
ERR_INPUT_TYPE_NOT_ALLOWED,
4344
ERR_INVALID_ARG_TYPE,
4445
ERR_INVALID_MODULE_SPECIFIER,
4546
ERR_INVALID_PACKAGE_CONFIG,
4647
ERR_INVALID_PACKAGE_TARGET,
48+
ERR_INVALID_URL,
4749
ERR_MANIFEST_DEPENDENCY_MISSING,
4850
ERR_MODULE_NOT_FOUND,
4951
ERR_PACKAGE_IMPORT_NOT_DEFINED,
@@ -132,13 +134,18 @@ function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) {
132134

133135
const realpathCache = new SafeMap();
134136

135-
/**
136-
* @param {string | URL} url
137-
* @returns {boolean}
138-
*/
139-
function fileExists(url) {
140-
return internalModuleStat(toNamespacedPath(toPathIfFileURL(url))) === 0;
141-
}
137+
const mainResolveExtensions = [
138+
'',
139+
'.js',
140+
'.json',
141+
'.node',
142+
'/index.js',
143+
'/index.json',
144+
'/index.node',
145+
'./index.js',
146+
'./index.json',
147+
'./index.node',
148+
];
142149

143150
/**
144151
* Legacy CommonJS main resolution:
@@ -153,44 +160,29 @@ function fileExists(url) {
153160
* @returns {URL}
154161
*/
155162
function legacyMainResolve(packageJSONUrl, packageConfig, base) {
156-
let guess;
157-
if (packageConfig.main !== undefined) {
158-
// Note: fs check redundances will be handled by Descriptor cache here.
159-
if (fileExists(guess = new URL(`./${packageConfig.main}`,
160-
packageJSONUrl))) {
161-
return guess;
162-
} else if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
163-
packageJSONUrl)));
164-
else if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
165-
packageJSONUrl)));
166-
else if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
167-
packageJSONUrl)));
168-
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
169-
packageJSONUrl)));
170-
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
171-
packageJSONUrl)));
172-
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
173-
packageJSONUrl)));
174-
else guess = undefined;
175-
if (guess) {
176-
emitLegacyIndexDeprecation(guess, packageJSONUrl, base,
177-
packageConfig.main);
178-
return guess;
163+
const resolvedOption = URLLegacyMainResolve(packageJSONUrl.href, packageConfig.main);
164+
165+
// less than zero means that happened some error
166+
// -2: LegacyMainResolveReturnType::kInvalidUrl
167+
// -1: LegacyMainResolveReturnType::kModuleNotFound
168+
if (resolvedOption < 0) {
169+
if (resolvedOption === -2) {
170+
throw new ERR_INVALID_URL(packageConfig.main || './index.js');
171+
} else {
172+
throw new ERR_MODULE_NOT_FOUND(
173+
fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base));
179174
}
180-
// Fallthrough.
181175
}
182-
if (fileExists(guess = new URL('./index.js', packageJSONUrl)));
183-
// So fs.
184-
else if (fileExists(guess = new URL('./index.json', packageJSONUrl)));
185-
else if (fileExists(guess = new URL('./index.node', packageJSONUrl)));
186-
else guess = undefined;
187-
if (guess) {
188-
emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main);
189-
return guess;
190-
}
191-
// Not found.
192-
throw new ERR_MODULE_NOT_FOUND(
193-
fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base));
176+
177+
// 0-6: when packageConfig.main is defined
178+
// 7-9: when packageConfig.main is NOT defined,
179+
// or when the previous case didn't found the file
180+
const baseUrl = resolvedOption <= 6 ? `./${packageConfig.main}` : '';
181+
const guess = new URL(baseUrl + mainResolveExtensions[resolvedOption], packageJSONUrl);
182+
183+
emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main);
184+
185+
return guess;
194186
}
195187

196188
const encodedSepRegEx = /%2F|%5C/i;
@@ -1078,6 +1070,7 @@ module.exports = {
10781070
packageExportsResolve,
10791071
packageImportsResolve,
10801072
throwIfInvalidParentURL,
1073+
legacyMainResolve,
10811074
};
10821075

10831076
// cycle

src/node_errors.h

+4
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,14 @@ void AppendExceptionLine(Environment* env,
6868
V(ERR_INVALID_ARG_VALUE, TypeError) \
6969
V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \
7070
V(ERR_INVALID_ARG_TYPE, TypeError) \
71+
V(ERR_INVALID_FILE_URL_HOST, TypeError) \
72+
V(ERR_INVALID_FILE_URL_PATH, TypeError) \
7173
V(ERR_INVALID_OBJECT_DEFINE_PROPERTY, TypeError) \
7274
V(ERR_INVALID_MODULE, Error) \
7375
V(ERR_INVALID_STATE, Error) \
7476
V(ERR_INVALID_THIS, TypeError) \
7577
V(ERR_INVALID_TRANSFER_OBJECT, TypeError) \
78+
V(ERR_INVALID_URL_SCHEME, TypeError) \
7679
V(ERR_MEMORY_ALLOCATION_FAILED, Error) \
7780
V(ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE, Error) \
7881
V(ERR_MISSING_ARGS, TypeError) \
@@ -163,6 +166,7 @@ ERRORS_WITH_CODE(V)
163166
V(ERR_INVALID_STATE, "Invalid state") \
164167
V(ERR_INVALID_THIS, "Value of \"this\" is the wrong type") \
165168
V(ERR_INVALID_TRANSFER_OBJECT, "Found invalid object in transferList") \
169+
V(ERR_INVALID_URL_SCHEME, "The URL must be of scheme file:") \
166170
V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") \
167171
V(ERR_OSSL_EVP_INVALID_DIGEST, "Invalid digest used") \
168172
V(ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE, \

0 commit comments

Comments
 (0)