From 70a8f614fbfdfbf135af4b6893451ece5a4d914c Mon Sep 17 00:00:00 2001 From: Kyle Herock Date: Wed, 1 Dec 2021 17:43:51 -0500 Subject: [PATCH] fix(pnp): allow ESM loader to load arbitrary file extensions as CJS --- .../pkg-tests-specs/sources/pnp-esm.test.ts | 44 ++++++++++++++++++- .../sources/esm-loader/built-loader.js | 2 +- .../sources/esm-loader/loaderUtils.ts | 24 ++++++---- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/packages/acceptance-tests/pkg-tests-specs/sources/pnp-esm.test.ts b/packages/acceptance-tests/pkg-tests-specs/sources/pnp-esm.test.ts index fd1a2893cb0e..7c830f0950b6 100644 --- a/packages/acceptance-tests/pkg-tests-specs/sources/pnp-esm.test.ts +++ b/packages/acceptance-tests/pkg-tests-specs/sources/pnp-esm.test.ts @@ -194,7 +194,25 @@ describe(`Plug'n'Play - ESM`, () => { ); test( - `it should not allow unknown extensions`, + `it should load commonjs with an unknown extension`, + makeTemporaryEnv( + { + }, + async ({path, run, source}) => { + await xfs.writeFilePromise(ppath.join(path, `index.ts` as Filename), `console.log(typeof require === 'undefined')`); + + await expect(run(`install`)).resolves.toMatchObject({code: 0}); + + await expect(run(`node`, `./index.ts`)).resolves.toMatchObject({ + code: 0, + stdout: `false\n`, + }); + }, + ), + ); + + test( + `it should not allow unknown extensions with {type: "module"}`, makeTemporaryEnv( { type: `module`, @@ -214,7 +232,7 @@ describe(`Plug'n'Play - ESM`, () => { // Tests https://github.com/nodejs/node/issues/33226 test( - `it should not load extensionless commonjs files as ESM`, + `it should load extensionless commonjs files`, makeTemporaryEnv( { }, { @@ -233,6 +251,28 @@ describe(`Plug'n'Play - ESM`, () => { ), ); + test( + `it should not allow extensionless files with {"type": "module"}`, + makeTemporaryEnv( + { + type: `module`, + }, + { + pnpEnableEsmLoader: true, + }, + async ({path, run, source}) => { + await xfs.writeFilePromise(ppath.join(path, `index` as Filename), ``); + + await expect(run(`install`)).resolves.toMatchObject({code: 0}); + + await expect(run(`node`, `./index`)).rejects.toMatchObject({ + code: 1, + stderr: expect.stringContaining(`Unknown file extension`), + }); + }, + ), + ); + test( `it should support ESM binaries`, makeTemporaryEnv( diff --git a/packages/yarnpkg-pnp/sources/esm-loader/built-loader.js b/packages/yarnpkg-pnp/sources/esm-loader/built-loader.js index 24cc134d6bc0..baf8a098da05 100644 --- a/packages/yarnpkg-pnp/sources/esm-loader/built-loader.js +++ b/packages/yarnpkg-pnp/sources/esm-loader/built-loader.js @@ -2,7 +2,7 @@ let hook; module.exports = () => { if (typeof hook === `undefined`) - hook = require('zlib').brotliDecompressSync(Buffer.from('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', 'base64')).toString(); + hook = require('zlib').brotliDecompressSync(Buffer.from('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', 'base64')).toString(); return hook; }; diff --git a/packages/yarnpkg-pnp/sources/esm-loader/loaderUtils.ts b/packages/yarnpkg-pnp/sources/esm-loader/loaderUtils.ts index f409bd939aee..47d519eeff48 100644 --- a/packages/yarnpkg-pnp/sources/esm-loader/loaderUtils.ts +++ b/packages/yarnpkg-pnp/sources/esm-loader/loaderUtils.ts @@ -48,16 +48,24 @@ export function getFileFormat(filepath: string): string | null { `Unknown file extension ".json" for ${filepath}`, ); } - // Matching files without extensions deviates from Node's default - // behaviour but is a fix for https://github.com/nodejs/node/issues/33226 - case ``: case `.js`: { const pkg = nodeUtils.readPackageScope(filepath); - if (pkg) { - return pkg.data.type ?? `commonjs`; - } + // assume CJS for files outside of a package boundary + if (!pkg) + return `commonjs`; + return pkg.data.type ?? `commonjs`; + } + // Matching files beyond those handled above deviates from Node's default + // --experimental-loader behavior but is required to work around + // https://github.com/nodejs/node/issues/33226 + default: { + const pkg = nodeUtils.readPackageScope(filepath); + if (!pkg) + return `commonjs`; + // prevent extensions beyond .mjs or .js from loading as ESM + if (pkg.data.type === `module`) + return null; + return pkg.data.type ?? `commonjs`; } } - - return null; }