Skip to content

Commit f260c35

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. Backport-PR-URL: nodejs#48325
1 parent 7fa1551 commit f260c35

File tree

10 files changed

+707
-43
lines changed

10 files changed

+707
-43
lines changed
Lines changed: 53 additions & 0 deletions
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

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const inputTypeFlag = getOptionValue('--input-type');
4040
const { URL, pathToFileURL, fileURLToPath, isURL, toPathIfFileURL } = require('internal/url');
4141
const { getCWDURL } = require('internal/util');
4242
const { canParse: URLCanParse } = internalBinding('url');
43+
const { legacyMainResolve: FSLegacyMainResolve } = internalBinding('fs');
4344
const {
4445
ERR_INPUT_TYPE_NOT_ALLOWED,
4546
ERR_INVALID_ARG_TYPE,
@@ -161,6 +162,35 @@ function fileExists(url) {
161162
return internalModuleStat(toNamespacedPath(toPathIfFileURL(url))) === 0;
162163
}
163164

165+
const legacyMainResolveExtensions = [
166+
'',
167+
'.js',
168+
'.json',
169+
'.node',
170+
'/index.js',
171+
'/index.json',
172+
'/index.node',
173+
'./index.js',
174+
'./index.json',
175+
'./index.node',
176+
];
177+
178+
const legacyMainResolveExtensionsIndexes = {
179+
// 0-6: when packageConfig.main is defined
180+
kResolvedByMain: 0,
181+
kResolvedByMainJs: 1,
182+
kResolvedByMainJson: 2,
183+
kResolvedByMainNode: 3,
184+
kResolvedByMainIndexJs: 4,
185+
kResolvedByMainIndexJson: 5,
186+
kResolvedByMainIndexNode: 6,
187+
// 7-9: when packageConfig.main is NOT defined,
188+
// or when the previous case didn't found the file
189+
kResolvedByPackageAndJs: 7,
190+
kResolvedByPackageAndJson: 8,
191+
kResolvedByPackageAndNode: 9,
192+
};
193+
164194
/**
165195
* Legacy CommonJS main resolution:
166196
* 1. let M = pkg_url + (json main field)
@@ -174,49 +204,22 @@ function fileExists(url) {
174204
* @returns {URL}
175205
*/
176206
function legacyMainResolve(packageJSONUrl, packageConfig, base) {
177-
let guess;
178-
if (packageConfig.main !== undefined) {
179-
// Note: fs check redundances will be handled by Descriptor cache here.
180-
if (fileExists(guess = new URL(`./${packageConfig.main}`, packageJSONUrl))) {
181-
return guess;
182-
} else if (fileExists(guess = new URL(`./${packageConfig.main}.js`, packageJSONUrl))) {
183-
// Handled below.
184-
} else if (fileExists(guess = new URL(`./${packageConfig.main}.json`, packageJSONUrl))) {
185-
// Handled below.
186-
} else if (fileExists(guess = new URL(`./${packageConfig.main}.node`, packageJSONUrl))) {
187-
// Handled below.
188-
} else if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`, packageJSONUrl))) {
189-
// Handled below.
190-
} else if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`, packageJSONUrl))) {
191-
// Handled below.
192-
} else if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`, packageJSONUrl))) {
193-
// Handled below.
194-
} else {
195-
guess = undefined;
196-
}
197-
if (guess) {
198-
emitLegacyIndexDeprecation(guess, packageJSONUrl, base,
199-
packageConfig.main);
200-
return guess;
201-
}
202-
// Fallthrough.
203-
}
204-
if (fileExists(guess = new URL('./index.js', packageJSONUrl))) {
205-
// Handled below.
206-
} else if (fileExists(guess = new URL('./index.json', packageJSONUrl))) {
207-
// Handled below.
208-
} else if (fileExists(guess = new URL('./index.node', packageJSONUrl))) {
209-
// Handled below.
210-
} else {
211-
guess = undefined;
212-
}
213-
if (guess) {
214-
emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main);
215-
return guess;
207+
const packageJsonUrlString = packageJSONUrl.href;
208+
209+
if (typeof packageJsonUrlString !== 'string') {
210+
throw new ERR_INVALID_ARG_TYPE('packageJSONUrl', ['URL'], packageJSONUrl);
216211
}
217-
// Not found.
218-
throw new ERR_MODULE_NOT_FOUND(
219-
fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base));
212+
213+
const baseStringified = isURL(base) ? base.href : base;
214+
215+
const resolvedOption = FSLegacyMainResolve(packageJsonUrlString, packageConfig.main, baseStringified);
216+
217+
const baseUrl = resolvedOption <= legacyMainResolveExtensionsIndexes.kResolvedByMainIndexNode ? `./${packageConfig.main}` : '';
218+
const resolvedUrl = new URL(baseUrl + legacyMainResolveExtensions[resolvedOption], packageJSONUrl);
219+
220+
emitLegacyIndexDeprecation(resolvedUrl, packageJSONUrl, base, packageConfig.main);
221+
222+
return resolvedUrl;
220223
}
221224

222225
/**

src/node_errors.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,22 @@ void AppendExceptionLine(Environment* env,
6767
V(ERR_INVALID_ARG_VALUE, TypeError) \
6868
V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \
6969
V(ERR_INVALID_ARG_TYPE, TypeError) \
70+
V(ERR_INVALID_FILE_URL_HOST, TypeError) \
71+
V(ERR_INVALID_FILE_URL_PATH, TypeError) \
7072
V(ERR_INVALID_OBJECT_DEFINE_PROPERTY, TypeError) \
7173
V(ERR_INVALID_MODULE, Error) \
7274
V(ERR_INVALID_STATE, Error) \
7375
V(ERR_INVALID_THIS, TypeError) \
7476
V(ERR_INVALID_TRANSFER_OBJECT, TypeError) \
77+
V(ERR_INVALID_URL, TypeError) \
78+
V(ERR_INVALID_URL_SCHEME, TypeError) \
7579
V(ERR_MEMORY_ALLOCATION_FAILED, Error) \
7680
V(ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE, Error) \
7781
V(ERR_MISSING_ARGS, TypeError) \
7882
V(ERR_MISSING_TRANSFERABLE_IN_TRANSFER_LIST, TypeError) \
7983
V(ERR_MISSING_PASSPHRASE, TypeError) \
8084
V(ERR_MISSING_PLATFORM_FOR_WORKER, Error) \
85+
V(ERR_MODULE_NOT_FOUND, Error) \
8186
V(ERR_NON_CONTEXT_AWARE_DISABLED, Error) \
8287
V(ERR_OUT_OF_RANGE, RangeError) \
8388
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \
@@ -160,6 +165,7 @@ ERRORS_WITH_CODE(V)
160165
V(ERR_INVALID_MODULE, "No such module") \
161166
V(ERR_INVALID_THIS, "Value of \"this\" is the wrong type") \
162167
V(ERR_INVALID_TRANSFER_OBJECT, "Found invalid object in transferList") \
168+
V(ERR_INVALID_URL_SCHEME, "The URL must be of scheme file:") \
163169
V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") \
164170
V(ERR_OSSL_EVP_INVALID_DIGEST, "Invalid digest used") \
165171
V(ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE, \

0 commit comments

Comments
 (0)