Skip to content

Commit f97880c

Browse files
lizthegreymerceyz
andcommitted
fix(pnp): esm - handle parentURL without a file: protocol (#5362)
**What's the problem this PR addresses?** When chaining Yarn PNP ESM loader with [import-in-the-middle](https://github.com/DataDog/import-in-the-middle) loader, INVALID_URL_SCHEME is thrown because `fileURLToPath()` is run on a parent URL of `node:util?iitm=true` generated by the IITM loader. ``` 2023-03-30T21:40:58.280Z 3364766 U TypeError [ERR_INVALID_URL_SCHEME]: The URL must be of scheme file 2023-03-30T21:40:58.280Z 3364766 U at new NodeError (node:internal/errors:399:5) 2023-03-30T21:40:58.280Z 3364766 U at fileURLToPath (node:internal/url:1212:11) 2023-03-30T21:40:58.280Z 3364766 U at resolve$1 (file:///home/lizf/eve-roster/.pnp.loader.mjs:1993:77) 2023-03-30T21:40:58.280Z 3364766 U at nextResolve (node:internal/modules/esm/hooks:654:28) 2023-03-30T21:40:58.280Z 3364766 U at Hooks.resolve (node:internal/modules/esm/hooks:309:30) 2023-03-30T21:40:58.280Z 3364766 U at ESMLoader.resolve (node:internal/modules/esm/loader:312:26) 2023-03-30T21:40:58.280Z 3364766 U at ESMLoader.getModuleJob (node:internal/modules/esm/loader:172:38) 2023-03-30T21:40:58.281Z 3364766 U at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40) 2023-03-30T21:40:58.281Z 3364766 U at link (node:internal/modules/esm/module_job:75:36) { 2023-03-30T21:40:58.281Z 3364766 U code: 'ERR_INVALID_URL_SCHEME' 2023-03-30T21:40:58.281Z 3364766 U } 2023-03-30T21:40:58.281Z 3364766 U 2023-03-30T21:40:58.281Z 3364766 U Node.js v19.8.1 ``` **How did you fix it?** Ensure fileURLToPath() is only run on file URLs by explicitly checking the URL protocol; if it is not `file`, then default to CWD. **Checklist** - [x] I have read the [Contributing Guide](https://yarnpkg.com/advanced/contributing). - [x] I have set the packages that need to be released for my changes to be effective. - [x] I will check that all automated PR checks pass before the PR gets reviewed. --------- Co-authored-by: merceyz <[email protected]>
1 parent 80302c3 commit f97880c

File tree

4 files changed

+88
-4
lines changed

4 files changed

+88
-4
lines changed

.yarn/versions/1c86bcd3.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
releases:
2+
"@yarnpkg/cli": patch
3+
"@yarnpkg/plugin-pnp": patch
4+
"@yarnpkg/pnp": patch
5+
6+
declined:
7+
- "@yarnpkg/plugin-compat"
8+
- "@yarnpkg/plugin-constraints"
9+
- "@yarnpkg/plugin-dlx"
10+
- "@yarnpkg/plugin-essentials"
11+
- "@yarnpkg/plugin-init"
12+
- "@yarnpkg/plugin-interactive-tools"
13+
- "@yarnpkg/plugin-nm"
14+
- "@yarnpkg/plugin-npm-cli"
15+
- "@yarnpkg/plugin-pack"
16+
- "@yarnpkg/plugin-patch"
17+
- "@yarnpkg/plugin-pnpm"
18+
- "@yarnpkg/plugin-stage"
19+
- "@yarnpkg/plugin-typescript"
20+
- "@yarnpkg/plugin-version"
21+
- "@yarnpkg/plugin-workspace-tools"
22+
- "@yarnpkg/builder"
23+
- "@yarnpkg/core"
24+
- "@yarnpkg/doctor"
25+
- "@yarnpkg/nm"
26+
- "@yarnpkg/pnpify"
27+
- "@yarnpkg/sdks"

packages/acceptance-tests/pkg-tests-specs/sources/pnp-esm.test.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {Filename, PortablePath, ppath, xfs} from '@yarnpkg/fslib';
2-
import * as loaderFlags from '@yarnpkg/pnp/sources/esm-loader/loaderFlags';
1+
import {Filename, PortablePath, npath, ppath, xfs} from '@yarnpkg/fslib';
2+
import * as loaderFlags from '@yarnpkg/pnp/sources/esm-loader/loaderFlags';
3+
import {pathToFileURL} from 'url';
34

45
describe(`Plug'n'Play - ESM`, () => {
56
test(
@@ -970,5 +971,61 @@ describe(`Plug'n'Play - ESM`, () => {
970971
},
971972
),
972973
);
974+
975+
(loaderFlags.HAS_CONSOLIDATED_HOOKS ? test : test.skip)(
976+
`it should allow importing files regardless of parent URL`,
977+
makeTemporaryEnv(
978+
{
979+
type: `module`,
980+
},
981+
async ({path, run, source}) => {
982+
await expect(run(`install`)).resolves.toMatchObject({code: 0});
983+
984+
await xfs.writeFilePromise(
985+
ppath.join(path, `loader.js` as Filename),
986+
`
987+
export function resolve(specifier, context, next) {
988+
if (specifier !== 'custom:foo') {
989+
return next(specifier, context);
990+
}
991+
992+
return {
993+
shortCircuit: true,
994+
url: 'custom:foo',
995+
};
996+
}
997+
998+
export function load(url, context, next) {
999+
if (url !== 'custom:foo') {
1000+
return next(url, context);
1001+
}
1002+
1003+
return {
1004+
format: 'module',
1005+
source: "import { foo } from '${pathToFileURL(npath.fromPortablePath(ppath.join(path, `foo.js` as Filename)))}'\\nconsole.log(foo);",
1006+
shortCircuit: true,
1007+
};
1008+
}
1009+
`,
1010+
);
1011+
1012+
await xfs.writeFilePromise(
1013+
ppath.join(path, `foo.js` as Filename),
1014+
`export const foo = 42;`,
1015+
);
1016+
1017+
await xfs.writeFilePromise(
1018+
ppath.join(path, `index.js` as Filename),
1019+
`import 'custom:foo'`,
1020+
);
1021+
1022+
await expect(run(`node`, `--loader`, `./loader.js`, `./index.js`)).resolves.toMatchObject({
1023+
code: 0,
1024+
stdout: `42\n`,
1025+
stderr: ``,
1026+
});
1027+
},
1028+
),
1029+
);
9731030
});
9741031
});

packages/yarnpkg-pnp/sources/esm-loader/built-loader.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/yarnpkg-pnp/sources/esm-loader/hooks/resolve.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export async function resolve(
6767

6868
const {parentURL, conditions = []} = context;
6969

70-
const issuer = parentURL ? fileURLToPath(parentURL) : process.cwd();
70+
const issuer = parentURL && loaderUtils.tryParseURL(parentURL)?.protocol === `file:` ? fileURLToPath(parentURL) : process.cwd();
7171

7272
// Get the pnpapi of either the issuer or the specifier.
7373
// The latter is required when the specifier is an absolute path to a

0 commit comments

Comments
 (0)