Skip to content

Commit ae70853

Browse files
committed
fix(pnp): esm - support unflagged JSON modules
1 parent 444eea5 commit ae70853

File tree

11 files changed

+130
-22
lines changed

11 files changed

+130
-22
lines changed

.pnp.cjs

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

.yarn/versions/736d10bb.yml

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

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ The following changes only affect people writing Yarn plugins:
6565
### Compatibility
6666

6767
- Updates the PnP compatibility layer for TypeScript 4.8.1-rc
68+
- The ESM loader now supports unflagged JSON modules.
6869

6970
## 3.2.2
7071

packages/acceptance-tests/pkg-tests-specs/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
"@yarnpkg/fslib": "workspace:^",
1717
"@yarnpkg/monorepo": "workspace:^",
1818
"@yarnpkg/parsers": "workspace:^",
19+
"@yarnpkg/pnp": "workspace:^",
1920
"lodash": "^4.17.15",
2021
"pkg-tests-core": "workspace:^",
2122
"semver": "^7.1.2",
22-
"tar": "^6.0.5"
23+
"tar": "^6.0.5",
24+
"tslib": "^2.4.0"
2325
},
2426
"engines": {
2527
"node": ">=14.15.0"

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

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {Filename, ppath, xfs} from '@yarnpkg/fslib';
2+
import * as loaderFlags from '@yarnpkg/pnp/sources/esm-loader/loaderFlags';
23

34
describe(`Plug'n'Play - ESM`, () => {
45
test(
@@ -195,8 +196,8 @@ describe(`Plug'n'Play - ESM`, () => {
195196
),
196197
);
197198

198-
test(
199-
`it should not resolve JSON files`,
199+
(loaderFlags.HAS_UNFLAGGED_JSON_MODULES === false ? test : test.skip)(
200+
`it should not resolve JSON modules without --experimental-json-modules`,
200201
makeTemporaryEnv(
201202
{
202203
type: `module`,
@@ -218,6 +219,55 @@ describe(`Plug'n'Play - ESM`, () => {
218219
),
219220
);
220221

222+
(loaderFlags.HAS_UNFLAGGED_JSON_MODULES ? test : test.skip)(
223+
`it should not resolve JSON modules without an import assertion`,
224+
makeTemporaryEnv(
225+
{
226+
type: `module`,
227+
},
228+
async ({path, run, source}) => {
229+
await expect(run(`install`)).resolves.toMatchObject({code: 0});
230+
231+
await xfs.writeFilePromise(
232+
ppath.join(path, `index.js` as Filename),
233+
`import './foo.json';`,
234+
);
235+
await xfs.writeFilePromise(ppath.join(path, `foo.json` as Filename), `{"name": "foo"}`);
236+
237+
await expect(run(`node`, `./index.js`)).rejects.toMatchObject({
238+
code: 1,
239+
stderr: expect.stringContaining(`ERR_IMPORT_ASSERTION_TYPE_MISSING`),
240+
});
241+
},
242+
),
243+
);
244+
245+
(loaderFlags.HAS_UNFLAGGED_JSON_MODULES ? test : test.skip)(
246+
`it should resolve JSON modules with an import assertion`,
247+
makeTemporaryEnv(
248+
{
249+
type: `module`,
250+
},
251+
async ({path, run, source}) => {
252+
await expect(run(`install`)).resolves.toMatchObject({code: 0});
253+
254+
await xfs.writeFilePromise(
255+
ppath.join(path, `index.js` as Filename),
256+
`
257+
import foo from './foo.json' assert { type: 'json' };
258+
console.log(foo.name);
259+
`,
260+
);
261+
await xfs.writeFilePromise(ppath.join(path, `foo.json` as Filename), `{"name": "foo"}`);
262+
263+
await expect(run(`node`, `./index.js`)).resolves.toMatchObject({
264+
code: 0,
265+
stdout: `foo\n`,
266+
});
267+
},
268+
),
269+
);
270+
221271
test(
222272
`it should respect exports`,
223273
makeTemporaryEnv(

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/load.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
import fs from 'fs';
2-
import {fileURLToPath} from 'url';
1+
import fs from 'fs';
2+
import {fileURLToPath} from 'url';
33

4-
import * as loaderUtils from '../loaderUtils';
4+
import {HAS_JSON_IMPORT_ASSERTION_REQUIREMENT} from '../loaderFlags';
5+
import * as loaderUtils from '../loaderUtils';
56

67
// The default `load` doesn't support reading from zip files
78
export async function load(
89
urlString: string,
9-
context: { format: string | null | undefined },
10+
context: {
11+
format: string | null | undefined;
12+
importAssertions?: {
13+
type?: 'json';
14+
};
15+
},
1016
nextLoad: typeof load,
1117
): Promise<{ format: string, source: string, shortCircuit: boolean }> {
1218
const url = loaderUtils.tryParseURL(urlString);
@@ -19,6 +25,12 @@ export async function load(
1925
if (!format)
2026
return nextLoad(urlString, context, nextLoad);
2127

28+
if (HAS_JSON_IMPORT_ASSERTION_REQUIREMENT && format === `json` && context.importAssertions?.type !== `json`) {
29+
const err = new TypeError(`[ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module "${urlString}" needs an import assertion of type "json"`) as TypeError & { code: string };
30+
err.code = `ERR_IMPORT_ASSERTION_TYPE_MISSING`;
31+
throw err;
32+
}
33+
2234
return {
2335
format,
2436
source: await fs.promises.readFile(filePath, `utf8`),

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,10 @@ import {getFormat as getFormatHook} from './hooks/getFormat';
22
import {getSource as getSourceHook} from './hooks/getSource';
33
import {load as loadHook} from './hooks/load';
44
import {resolve as resolveHook} from './hooks/resolve';
5+
import {HAS_CONSOLIDATED_HOOKS} from './loaderFlags';
56
import './fspatch';
67

7-
const [major, minor] = process.versions.node.split(`.`).map(value => parseInt(value, 10));
8-
9-
// The hooks were consolidated in https://github.com/nodejs/node/pull/37468
10-
const hasConsolidatedHooks = major > 16 || (major === 16 && minor >= 12);
11-
128
export const resolve = resolveHook;
13-
export const getFormat = hasConsolidatedHooks ? undefined : getFormatHook;
14-
export const getSource = hasConsolidatedHooks ? undefined : getSourceHook;
15-
export const load = hasConsolidatedHooks ? loadHook : undefined;
9+
export const getFormat = HAS_CONSOLIDATED_HOOKS ? undefined : getFormatHook;
10+
export const getSource = HAS_CONSOLIDATED_HOOKS ? undefined : getSourceHook;
11+
export const load = HAS_CONSOLIDATED_HOOKS ? loadHook : undefined;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const [major, minor] = process.versions.node.split(`.`).map(value => parseInt(value, 10));
2+
3+
// The hooks were consolidated in https://github.com/nodejs/node/pull/37468
4+
export const HAS_CONSOLIDATED_HOOKS = major > 16 || (major === 16 && minor >= 12);
5+
6+
// JSON modules were unflagged in https://github.com/nodejs/node/pull/41736
7+
export const HAS_UNFLAGGED_JSON_MODULES = major > 17 || (major === 17 && minor >= 5) || (major === 16 && minor >= 15);
8+
9+
// JSON modules requires import assertions after https://github.com/nodejs/node/pull/40250
10+
export const HAS_JSON_IMPORT_ASSERTION_REQUIREMENT = major > 17 || (major === 17 && minor >= 1) || (major === 16 && minor > 14);

packages/yarnpkg-pnp/sources/esm-loader/loaderUtils.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import {NativePath} from '@yarnpkg/fslib';
2-
import fs from 'fs';
3-
import path from 'path';
4-
import {URL} from 'url';
1+
import {NativePath} from '@yarnpkg/fslib';
2+
import fs from 'fs';
3+
import path from 'path';
4+
import {URL} from 'url';
55

6-
import * as nodeUtils from '../loader/nodeUtils';
6+
import * as nodeUtils from '../loader/nodeUtils';
7+
8+
import {HAS_UNFLAGGED_JSON_MODULES} from './loaderFlags';
79

810
export async function tryReadFile(path: NativePath): Promise<string | null> {
911
try {
@@ -48,6 +50,9 @@ export function getFileFormat(filepath: string): string | null {
4850
);
4951
}
5052
case `.json`: {
53+
if (HAS_UNFLAGGED_JSON_MODULES)
54+
return `json`;
55+
5156
// TODO: Enable if --experimental-json-modules is present
5257
// Waiting on https://github.com/nodejs/node/issues/36935
5358
throw new Error(

0 commit comments

Comments
 (0)