mirror of https://github.com/nodejs/node.git
module: fix async resolution error within the sync `findPackageJSON`
PR-URL: https://github.com/nodejs/node/pull/56382 Reviewed-By: Jordan Harband <ljharb@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>pull/56419/head
parent
4afb0e235a
commit
19d58c5181
|
@ -81,13 +81,17 @@ added: v23.2.0
|
|||
* `base` {string|URL} The absolute location (`file:` URL string or FS path) of the
|
||||
containing module. For CJS, use `__filename` (not `__dirname`!); for ESM, use
|
||||
`import.meta.url`. You do not need to pass it if `specifier` is an `absolute specifier`.
|
||||
* Returns: {string|undefined} A path if the `package.json` is found. When `startLocation`
|
||||
* Returns: {string|undefined} A path if the `package.json` is found. When `specifier`
|
||||
is a package, the package's root `package.json`; when a relative or unresolved, the closest
|
||||
`package.json` to the `startLocation`.
|
||||
`package.json` to the `specifier`.
|
||||
|
||||
> **Caveat**: Do not use this to try to determine module format. There are many things effecting
|
||||
> **Caveat**: Do not use this to try to determine module format. There are many things affecting
|
||||
> that determination; the `type` field of package.json is the _least_ definitive (ex file extension
|
||||
> superceeds it, and a loader hook superceeds that).
|
||||
> supersedes it, and a loader hook supersedes that).
|
||||
|
||||
> **Caveat**: This currently leverages only the built-in default resolver; if
|
||||
> [`resolve` customization hooks][resolve hook] are registered, they will not affect the resolution.
|
||||
> This may change in the future.
|
||||
|
||||
```text
|
||||
/path/to/project
|
||||
|
|
|
@ -267,8 +267,8 @@ function getPackageJSONURL(specifier, base) {
|
|||
throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base), null);
|
||||
}
|
||||
|
||||
const pjsonImportAttributes = { __proto__: null, type: 'json' };
|
||||
let cascadedLoader;
|
||||
/** @type {import('./esm/resolve.js').defaultResolve} */
|
||||
let defaultResolve;
|
||||
/**
|
||||
* @param {URL['href'] | string | URL} specifier The location for which to get the "root" package.json
|
||||
* @param {URL['href'] | string | URL} [base] The location of the current module (ex file://tmp/foo.js).
|
||||
|
@ -296,10 +296,15 @@ function findPackageJSON(specifier, base = 'data:') {
|
|||
}
|
||||
|
||||
let resolvedTarget;
|
||||
cascadedLoader ??= require('internal/modules/esm/loader').getOrInitializeCascadedLoader();
|
||||
defaultResolve ??= require('internal/modules/esm/resolve').defaultResolve;
|
||||
|
||||
try {
|
||||
resolvedTarget = cascadedLoader.resolve(specifier, `${parentURL}`, pjsonImportAttributes).url;
|
||||
// TODO(@JakobJingleheimer): Detect whether findPackageJSON is being used within a loader
|
||||
// (possibly piggyback on `allowImportMetaResolve`)
|
||||
// - When inside, use the default resolve
|
||||
// - (I think it's impossible to use the chain because of re-entry & a deadlock from atomics).
|
||||
// - When outside, use cascadedLoader.resolveSync (not implemented yet, but the pieces exist).
|
||||
resolvedTarget = defaultResolve(specifier, { parentURL: `${parentURL}` }).url;
|
||||
} catch (err) {
|
||||
if (err.code === 'ERR_UNSUPPORTED_DIR_IMPORT') {
|
||||
resolvedTarget = err.url;
|
||||
|
|
|
@ -149,4 +149,44 @@ describe('findPackageJSON', () => { // Throws when no arguments are provided
|
|||
});
|
||||
}));
|
||||
});
|
||||
|
||||
it('should work within a loader', async () => {
|
||||
const specifierBase = './packages/root-types-field';
|
||||
const target = fixtures.fileURL(specifierBase, 'index.js');
|
||||
const foundPjsonPath = path.toNamespacedPath(fixtures.path(specifierBase, 'package.json'));
|
||||
const { code, stderr, stdout } = await common.spawnPromisified(process.execPath, [
|
||||
'--no-warnings',
|
||||
'--loader',
|
||||
[
|
||||
'data:text/javascript,',
|
||||
'import fs from "node:fs";',
|
||||
'import module from "node:module";',
|
||||
encodeURIComponent(`fs.writeSync(1, module.findPackageJSON(${JSON.stringify(target)}));`),
|
||||
'export const resolve = async (s, c, n) => n(s);',
|
||||
].join(''),
|
||||
'--eval',
|
||||
'import "node:os";', // Can be anything that triggers the resolve hook chain
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.ok(stdout.includes(foundPjsonPath), stdout);
|
||||
assert.strictEqual(code, 0);
|
||||
});
|
||||
|
||||
it('should work with an async resolve hook registered', async () => {
|
||||
const specifierBase = './packages/root-types-field';
|
||||
const target = fixtures.fileURL(specifierBase, 'index.js');
|
||||
const foundPjsonPath = path.toNamespacedPath(fixtures.path(specifierBase, 'package.json'));
|
||||
const { code, stderr, stdout } = await common.spawnPromisified(process.execPath, [
|
||||
'--no-warnings',
|
||||
'--loader',
|
||||
'data:text/javascript,export const resolve = async (s, c, n) => n(s);',
|
||||
'--print',
|
||||
`require("node:module").findPackageJSON(${JSON.stringify(target)})`,
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.ok(stdout.includes(foundPjsonPath), stdout);
|
||||
assert.strictEqual(code, 0);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue