fix: rework and improve legacyMainResolve
patch (#45754)
fix: rework and improve legacyMainResolve patch
This commit is contained in:
parent
e3f61b465d
commit
3e51ee516e
5 changed files with 131 additions and 173 deletions
|
@ -18,7 +18,7 @@ test_formally_mark_some_tests_as_flaky.patch
|
||||||
fix_do_not_resolve_electron_entrypoints.patch
|
fix_do_not_resolve_electron_entrypoints.patch
|
||||||
ci_ensure_node_tests_set_electron_run_as_node.patch
|
ci_ensure_node_tests_set_electron_run_as_node.patch
|
||||||
fix_assert_module_in_the_renderer_process.patch
|
fix_assert_module_in_the_renderer_process.patch
|
||||||
fix_revert_src_lb_reducing_c_calls_of_esm_legacy_main_resolve.patch
|
fix_allow_passing_fileexists_fn_to_legacymainresolve.patch
|
||||||
fix_remove_deprecated_errno_constants.patch
|
fix_remove_deprecated_errno_constants.patch
|
||||||
build_enable_perfetto.patch
|
build_enable_perfetto.patch
|
||||||
fix_add_source_location_for_v8_task_runner.patch
|
fix_add_source_location_for_v8_task_runner.patch
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||||
|
Date: Sat, 17 Feb 2024 22:17:13 -0800
|
||||||
|
Subject: fix: allow passing fileExists fn to legacyMainResolve
|
||||||
|
|
||||||
|
This switch to native legacyMainResolve doesn't take asar into account, and can
|
||||||
|
cause errors when a project using ESM and asar tries to load a dependency which
|
||||||
|
uses commonJS.
|
||||||
|
|
||||||
|
We can fix this by allowing the C++ implementation of legacyMainResolve to use
|
||||||
|
a fileExists function that does take Asar into account.
|
||||||
|
|
||||||
|
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
|
||||||
|
index 2879e5cf541fb4d226cfd7cc0fe367ca448fb926..03082f0ec4f91382933eec48e77331cdf6f04943 100644
|
||||||
|
--- a/lib/internal/modules/esm/resolve.js
|
||||||
|
+++ b/lib/internal/modules/esm/resolve.js
|
||||||
|
@@ -28,14 +28,13 @@ const { BuiltinModule } = require('internal/bootstrap/realm');
|
||||||
|
const fs = require('fs');
|
||||||
|
const { getOptionValue } = require('internal/options');
|
||||||
|
// Do not eagerly grab .manifest, it may be in TDZ
|
||||||
|
-const { sep, posix: { relative: relativePosixPath }, resolve } = require('path');
|
||||||
|
+const { sep, posix: { relative: relativePosixPath }, toNamespacedPath, resolve } = require('path');
|
||||||
|
const preserveSymlinks = getOptionValue('--preserve-symlinks');
|
||||||
|
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
|
||||||
|
const inputTypeFlag = getOptionValue('--input-type');
|
||||||
|
-const { URL, pathToFileURL, fileURLToPath, isURL, URLParse } = require('internal/url');
|
||||||
|
+const { URL, pathToFileURL, fileURLToPath, isURL, URLParse, toPathIfFileURL } = require('internal/url');
|
||||||
|
const { getCWDURL, setOwnProperty } = require('internal/util');
|
||||||
|
const { canParse: URLCanParse } = internalBinding('url');
|
||||||
|
-const { legacyMainResolve: FSLegacyMainResolve } = internalBinding('fs');
|
||||||
|
const {
|
||||||
|
ERR_INPUT_TYPE_NOT_ALLOWED,
|
||||||
|
ERR_INVALID_ARG_TYPE,
|
||||||
|
@@ -183,6 +182,11 @@ const legacyMainResolveExtensionsIndexes = {
|
||||||
|
kResolvedByPackageAndNode: 9,
|
||||||
|
};
|
||||||
|
|
||||||
|
+function fileExists(url) {
|
||||||
|
+ const namespaced = toNamespacedPath(toPathIfFileURL(url));
|
||||||
|
+ return internalFsBinding.internalModuleStat(internalFsBinding, namespaced) === 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* Legacy CommonJS main resolution:
|
||||||
|
* 1. let M = pkg_url + (json main field)
|
||||||
|
@@ -201,7 +205,7 @@ function legacyMainResolve(packageJSONUrl, packageConfig, base) {
|
||||||
|
|
||||||
|
const baseStringified = isURL(base) ? base.href : base;
|
||||||
|
|
||||||
|
- const resolvedOption = FSLegacyMainResolve(pkgPath, packageConfig.main, baseStringified);
|
||||||
|
+ const resolvedOption = internalFsBinding.legacyMainResolve(pkgPath, packageConfig.main, baseStringified, fileExists);
|
||||||
|
|
||||||
|
const maybeMain = resolvedOption <= legacyMainResolveExtensionsIndexes.kResolvedByMainIndexNode ?
|
||||||
|
packageConfig.main || './' : '';
|
||||||
|
diff --git a/src/node_file.cc b/src/node_file.cc
|
||||||
|
index 1d22e19f16d5ad82466b0724971b2e4a685682f7..9619d10710ffbbdc73fa7d59d1b797c8d0b3a956 100644
|
||||||
|
--- a/src/node_file.cc
|
||||||
|
+++ b/src/node_file.cc
|
||||||
|
@@ -3220,13 +3220,25 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingData::FilePathIsFileReturnType BindingData::FilePathIsFile(
|
||||||
|
- Environment* env, const std::string& file_path) {
|
||||||
|
+ Environment* env, const std::string& file_path, v8::Local<v8::Function> is_file_function) {
|
||||||
|
THROW_IF_INSUFFICIENT_PERMISSIONS(
|
||||||
|
env,
|
||||||
|
permission::PermissionScope::kFileSystemRead,
|
||||||
|
file_path,
|
||||||
|
BindingData::FilePathIsFileReturnType::kThrowInsufficientPermissions);
|
||||||
|
|
||||||
|
+ if (!is_file_function.IsEmpty()) {
|
||||||
|
+ v8::Local<v8::Value> argv[] = {v8::String::NewFromUtf8(
|
||||||
|
+ env->isolate(), file_path.c_str(), v8::NewStringType::kNormal)
|
||||||
|
+ .ToLocalChecked()};
|
||||||
|
+ MaybeLocal<Value> maybe_is_file = is_file_function->Call(env->context(), v8::Undefined(env->isolate()), 1, argv);
|
||||||
|
+ if (maybe_is_file.IsEmpty()) {
|
||||||
|
+ bool is_file = maybe_is_file.ToLocalChecked()->BooleanValue(env->isolate());
|
||||||
|
+ return is_file ? BindingData::FilePathIsFileReturnType::kIsFile
|
||||||
|
+ : BindingData::FilePathIsFileReturnType::kIsNotFile;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
uv_fs_t req;
|
||||||
|
|
||||||
|
int rc = uv_fs_stat(env->event_loop(), &req, file_path.c_str(), nullptr);
|
||||||
|
@@ -3284,6 +3296,11 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
std::optional<std::string> initial_file_path;
|
||||||
|
std::string file_path;
|
||||||
|
|
||||||
|
+ v8::Local<v8::Function> is_file_function;
|
||||||
|
+ if (args.Length() >= 3 && args[3]->IsFunction()) {
|
||||||
|
+ is_file_function = args[3].As<v8::Function>();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (args.Length() >= 2 && args[1]->IsString()) {
|
||||||
|
auto package_config_main = Utf8Value(isolate, args[1]).ToString();
|
||||||
|
|
||||||
|
@@ -3304,7 +3321,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
BufferValue buff_file_path(isolate, local_file_path);
|
||||||
|
ToNamespacedPath(env, &buff_file_path);
|
||||||
|
|
||||||
|
- switch (FilePathIsFile(env, buff_file_path.ToString())) {
|
||||||
|
+ switch (FilePathIsFile(env, buff_file_path.ToString(), is_file_function)) {
|
||||||
|
case BindingData::FilePathIsFileReturnType::kIsFile:
|
||||||
|
return args.GetReturnValue().Set(i);
|
||||||
|
case BindingData::FilePathIsFileReturnType::kIsNotFile:
|
||||||
|
@@ -3341,7 +3358,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
BufferValue buff_file_path(isolate, local_file_path);
|
||||||
|
ToNamespacedPath(env, &buff_file_path);
|
||||||
|
|
||||||
|
- switch (FilePathIsFile(env, buff_file_path.ToString())) {
|
||||||
|
+ switch (FilePathIsFile(env, buff_file_path.ToString(), is_file_function)) {
|
||||||
|
case BindingData::FilePathIsFileReturnType::kIsFile:
|
||||||
|
return args.GetReturnValue().Set(i);
|
||||||
|
case BindingData::FilePathIsFileReturnType::kIsNotFile:
|
||||||
|
diff --git a/src/node_file.h b/src/node_file.h
|
||||||
|
index bdad1ae25f4892cbbfd8cc30c4d8b4a6f600edbc..099488319f53bc7718313d6e30df2237cad6771d 100644
|
||||||
|
--- a/src/node_file.h
|
||||||
|
+++ b/src/node_file.h
|
||||||
|
@@ -101,7 +101,8 @@ class BindingData : public SnapshotableObject {
|
||||||
|
InternalFieldInfo* internal_field_info_ = nullptr;
|
||||||
|
|
||||||
|
static FilePathIsFileReturnType FilePathIsFile(Environment* env,
|
||||||
|
- const std::string& file_path);
|
||||||
|
+ const std::string& file_path,
|
||||||
|
+ v8::Local<v8::Function> is_file_function = v8::Local<v8::Function>());
|
||||||
|
};
|
||||||
|
|
||||||
|
// structure used to store state during a complex operation, e.g., mkdirp.
|
|
@ -1,170 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: VerteDinde <vertedinde@electronjs.org>
|
|
||||||
Date: Sat, 17 Feb 2024 22:17:13 -0800
|
|
||||||
Subject: fix: revert "src,lb: reducing C++ calls of esm legacy main resolve"
|
|
||||||
|
|
||||||
This switch to native legacyMainResolve doesn't take asar into account, and can
|
|
||||||
cause errors when a project using ESM and asar tries to load a dependency which
|
|
||||||
uses commonJS. This will need to be fixed forward, but revert for Electron 29's
|
|
||||||
stable release to avoid potentially breaking apps with a riskier fix.
|
|
||||||
|
|
||||||
This patch can be removed when node's
|
|
||||||
native implementation has been patched
|
|
||||||
to recognize asar files.
|
|
||||||
|
|
||||||
This reverts commit 9cf2e1f55b8446a7cde23699d00a3be73aa0c8f1.
|
|
||||||
|
|
||||||
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
|
|
||||||
index 2879e5cf541fb4d226cfd7cc0fe367ca448fb926..19df6fab3e118ac237f930b1e5d41c02c9050fa2 100644
|
|
||||||
--- a/lib/internal/modules/esm/resolve.js
|
|
||||||
+++ b/lib/internal/modules/esm/resolve.js
|
|
||||||
@@ -28,15 +28,15 @@ const { BuiltinModule } = require('internal/bootstrap/realm');
|
|
||||||
const fs = require('fs');
|
|
||||||
const { getOptionValue } = require('internal/options');
|
|
||||||
// Do not eagerly grab .manifest, it may be in TDZ
|
|
||||||
-const { sep, posix: { relative: relativePosixPath }, resolve } = require('path');
|
|
||||||
+const { sep, posix: { relative: relativePosixPath }, toNamespacedPath, resolve } = require('path');
|
|
||||||
const preserveSymlinks = getOptionValue('--preserve-symlinks');
|
|
||||||
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
|
|
||||||
const inputTypeFlag = getOptionValue('--input-type');
|
|
||||||
-const { URL, pathToFileURL, fileURLToPath, isURL, URLParse } = require('internal/url');
|
|
||||||
+const { URL, pathToFileURL, fileURLToPath, isURL, URLParse, toPathIfFileURL } = require('internal/url');
|
|
||||||
const { getCWDURL, setOwnProperty } = require('internal/util');
|
|
||||||
const { canParse: URLCanParse } = internalBinding('url');
|
|
||||||
-const { legacyMainResolve: FSLegacyMainResolve } = internalBinding('fs');
|
|
||||||
const {
|
|
||||||
+ ERR_ACCESS_DENIED,
|
|
||||||
ERR_INPUT_TYPE_NOT_ALLOWED,
|
|
||||||
ERR_INVALID_ARG_TYPE,
|
|
||||||
ERR_INVALID_MODULE_SPECIFIER,
|
|
||||||
@@ -53,6 +53,7 @@ const { Module: CJSModule } = require('internal/modules/cjs/loader');
|
|
||||||
const { getConditionsSet } = require('internal/modules/esm/utils');
|
|
||||||
const packageJsonReader = require('internal/modules/package_json_reader');
|
|
||||||
const internalFsBinding = internalBinding('fs');
|
|
||||||
+const permission = require('internal/process/permission');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {import('internal/modules/esm/package_config.js').PackageConfig} PackageConfig
|
|
||||||
@@ -183,6 +184,20 @@ const legacyMainResolveExtensionsIndexes = {
|
|
||||||
kResolvedByPackageAndNode: 9,
|
|
||||||
};
|
|
||||||
|
|
||||||
+/**
|
|
||||||
+ * @param {string | URL} url
|
|
||||||
+ * @returns {boolean}
|
|
||||||
+ */
|
|
||||||
+function fileExists(url) {
|
|
||||||
+ const { Buffer } = require('buffer');
|
|
||||||
+ const namespaced = toNamespacedPath(toPathIfFileURL(url));
|
|
||||||
+ if (permission.isEnabled() && !permission.has('fs.read', namespaced)) {
|
|
||||||
+ const resource = Buffer.isBuffer(namespaced) ? Buffer.toString(namespaced) : namespaced;
|
|
||||||
+ throw new ERR_ACCESS_DENIED('Access to this API has been restricted', 'FileSystemRead', resource);
|
|
||||||
+ }
|
|
||||||
+ return internalFsBinding.internalModuleStat(internalFsBinding, namespaced) === 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
/**
|
|
||||||
* Legacy CommonJS main resolution:
|
|
||||||
* 1. let M = pkg_url + (json main field)
|
|
||||||
@@ -199,18 +214,45 @@ function legacyMainResolve(packageJSONUrl, packageConfig, base) {
|
|
||||||
assert(isURL(packageJSONUrl));
|
|
||||||
const pkgPath = fileURLToPath(new URL('.', packageJSONUrl));
|
|
||||||
|
|
||||||
- const baseStringified = isURL(base) ? base.href : base;
|
|
||||||
-
|
|
||||||
- const resolvedOption = FSLegacyMainResolve(pkgPath, packageConfig.main, baseStringified);
|
|
||||||
-
|
|
||||||
- const maybeMain = resolvedOption <= legacyMainResolveExtensionsIndexes.kResolvedByMainIndexNode ?
|
|
||||||
- packageConfig.main || './' : '';
|
|
||||||
- const resolvedPath = resolve(pkgPath, maybeMain + legacyMainResolveExtensions[resolvedOption]);
|
|
||||||
- const resolvedUrl = pathToFileURL(resolvedPath);
|
|
||||||
-
|
|
||||||
- emitLegacyIndexDeprecation(resolvedUrl, resolvedPath, pkgPath, base, packageConfig.main);
|
|
||||||
+ 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;
|
|
||||||
+ } else if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
|
|
||||||
+ packageJSONUrl)));
|
|
||||||
+ else if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
|
|
||||||
+ packageJSONUrl)));
|
|
||||||
+ else if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
|
|
||||||
+ packageJSONUrl)));
|
|
||||||
+ else if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
|
|
||||||
+ packageJSONUrl)));
|
|
||||||
+ else if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
|
|
||||||
+ packageJSONUrl)));
|
|
||||||
+ else if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
|
|
||||||
+ packageJSONUrl)));
|
|
||||||
+ else guess = undefined;
|
|
||||||
+ if (guess) {
|
|
||||||
+ emitLegacyIndexDeprecation(guess, fileURLToPath(guess), pkgPath,
|
|
||||||
+ base, packageConfig.main);
|
|
||||||
+ return guess;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- return resolvedUrl;
|
|
||||||
+ // Fallthrough.
|
|
||||||
+ if (fileExists(guess = new URL('./index.js', packageJSONUrl)));
|
|
||||||
+ // So fs.
|
|
||||||
+ else if (fileExists(guess = new URL('./index.json', packageJSONUrl)));
|
|
||||||
+ else if (fileExists(guess = new URL('./index.node', packageJSONUrl)));
|
|
||||||
+ else guess = undefined;
|
|
||||||
+ if (guess) {
|
|
||||||
+ emitLegacyIndexDeprecation(guess, fileURLToPath(guess), pkgPath,
|
|
||||||
+ base, packageConfig.main);
|
|
||||||
+ return guess;
|
|
||||||
+ }
|
|
||||||
+ // Not found.
|
|
||||||
+ throw new ERR_MODULE_NOT_FOUND(pkgPath, fileURLToPath(base), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const encodedSepRegEx = /%2F|%5C/i;
|
|
||||||
diff --git a/test/es-module/test-cjs-legacyMainResolve.js b/test/es-module/test-cjs-legacyMainResolve.js
|
|
||||||
index edb567bce403f2d4df482c2549c6f7cec78c3588..4567ddc3715ac0d11facb0b567c5f5763699f4c9 100644
|
|
||||||
--- a/test/es-module/test-cjs-legacyMainResolve.js
|
|
||||||
+++ b/test/es-module/test-cjs-legacyMainResolve.js
|
|
||||||
@@ -129,7 +129,7 @@ describe('legacyMainResolve', () => {
|
|
||||||
);
|
|
||||||
assert.throws(
|
|
||||||
() => legacyMainResolve(packageJsonUrl, { main: null }, packageJsonUrl),
|
|
||||||
- { message: /index\.js/, code: 'ERR_MODULE_NOT_FOUND' },
|
|
||||||
+ { code: 'ERR_MODULE_NOT_FOUND' },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ describe('legacyMainResolve', () => {
|
|
||||||
const packageJsonUrl = pathToFileURL('/c/file%20with%20percents/package.json');
|
|
||||||
assert.throws(
|
|
||||||
() => legacyMainResolve(packageJsonUrl, { main: null }, packageJsonUrl),
|
|
||||||
- { message: /index\.js/, code: 'ERR_MODULE_NOT_FOUND' },
|
|
||||||
+ { code: 'ERR_MODULE_NOT_FOUND' },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ describe('legacyMainResolve', () => {
|
|
||||||
);
|
|
||||||
assert.throws(
|
|
||||||
() => legacyMainResolve(packageJsonUrl, { main: './index.node' }, packageJsonUrl),
|
|
||||||
- { message: /index\.node/, code: 'ERR_MODULE_NOT_FOUND' },
|
|
||||||
+ { code: 'ERR_MODULE_NOT_FOUND' },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -163,11 +163,11 @@ describe('legacyMainResolve', () => {
|
|
||||||
);
|
|
||||||
assert.throws(
|
|
||||||
() => legacyMainResolve(packageJsonUrl, { main: null }, undefined),
|
|
||||||
- { message: /"base" argument must be/, code: 'ERR_INVALID_ARG_TYPE' },
|
|
||||||
+ { message: 'The "path" argument must be of type string or an instance of URL. Received undefined', code: 'ERR_INVALID_ARG_TYPE' },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
- it('should interpret main as a path, not a URL', () => {
|
|
||||||
+ it.skip('should interpret main as a path, not a URL', () => {
|
|
||||||
const packageJsonUrl = fixtures.fileURL('/es-modules/legacy-main-resolver/package.json');
|
|
||||||
assert.deepStrictEqual(
|
|
||||||
legacyMainResolve(packageJsonUrl, { main: '../folder%25with percentage#/' }, packageJsonUrl),
|
|
|
@ -40,7 +40,7 @@ index 0f0cde7be431dcb80c5314b1a9da49886c436d1c..f6d2bd439cad8b9f91c9d9a6cdb302e6
|
||||||
}
|
}
|
||||||
HistogramBase* histogram;
|
HistogramBase* histogram;
|
||||||
diff --git a/src/node_file.cc b/src/node_file.cc
|
diff --git a/src/node_file.cc b/src/node_file.cc
|
||||||
index 1d22e19f16d5ad82466b0724971b2e4a685682f7..7531664c37833da9804d24c642a38a60c336c2c7 100644
|
index 9619d10710ffbbdc73fa7d59d1b797c8d0b3a956..f2a5c0939b60c582dbbecc07add1e903a9183b95 100644
|
||||||
--- a/src/node_file.cc
|
--- a/src/node_file.cc
|
||||||
+++ b/src/node_file.cc
|
+++ b/src/node_file.cc
|
||||||
@@ -1061,13 +1061,8 @@ static int32_t FastInternalModuleStat(
|
@@ -1061,13 +1061,8 @@ static int32_t FastInternalModuleStat(
|
||||||
|
|
|
@ -72,7 +72,6 @@
|
||||||
"parallel/test-snapshot-weak-reference",
|
"parallel/test-snapshot-weak-reference",
|
||||||
"parallel/test-snapshot-worker",
|
"parallel/test-snapshot-worker",
|
||||||
"parallel/test-strace-openat-openssl",
|
"parallel/test-strace-openat-openssl",
|
||||||
"parallel/test-find-package-json",
|
|
||||||
"parallel/test-tls-alpn-server-client",
|
"parallel/test-tls-alpn-server-client",
|
||||||
"parallel/test-tls-cli-min-version-1.0",
|
"parallel/test-tls-cli-min-version-1.0",
|
||||||
"parallel/test-tls-cli-max-version-1.2",
|
"parallel/test-tls-cli-max-version-1.2",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue