From 8a5c59b7f1538e7c039a84f5fc914d602f91ee7d Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 29 Jun 2022 17:47:44 -0500 Subject: [PATCH] Ensure @swc/helpers do not rely on hoisting (#38174) This ensures we properly alias and internalize `@swc/helpers` so that we don't rely on package managers hoisting this dependency for the build to work properly. This also disables the external helpers with `jest` as this can also require hoisting to work and doesn't provide as much of an optimization. ## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` Fixes: [slack thread](https://vercel.slack.com/archives/C0289CGVAR2/p1656437059151439) --- packages/next/build/swc/options.js | 2 +- packages/next/build/webpack-config.ts | 10 +++++ .../index.test.ts | 38 +++++++++++++++++++ test/lib/create-next-install.js | 2 +- 4 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 test/e2e/handle-non-hoisted-swc-helpers/index.test.ts diff --git a/packages/next/build/swc/options.js b/packages/next/build/swc/options.js index 40e1d37304..218692a908 100644 --- a/packages/next/build/swc/options.js +++ b/packages/next/build/swc/options.js @@ -56,7 +56,7 @@ function getBaseSWCOptions({ paths, } : {}), - externalHelpers: !process.versions.pnp, + externalHelpers: !process.versions.pnp && !jest, parser: parserConfig, experimental: { keepImportAssertions: true, diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 0d31d1cd32..9dc43df0d9 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -678,6 +678,10 @@ export default async function getBaseWebpackConfig( } : {}), + '@swc/helpers': path.dirname( + require.resolve('@swc/helpers/package.json') + ), + setimmediate: 'next/dist/compiled/setimmediate', }, ...(isClient || isEdgeServer @@ -816,6 +820,12 @@ export default async function getBaseWebpackConfig( } } + // @swc/helpers should not be external as it would + // require hoisting the package which we can't rely on + if (request.includes('@swc/helpers')) { + return + } + // When in esm externals mode, and using import, we resolve with // ESM resolving options. const isEsmRequested = dependencyType === 'esm' diff --git a/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts b/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts new file mode 100644 index 0000000000..3fdef2e151 --- /dev/null +++ b/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts @@ -0,0 +1,38 @@ +import { createNext } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { renderViaHTTP } from 'next-test-utils' + +describe('handle-non-hoisted-swc-helpers', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/index.js': ` + export default function Page() { + return

hello world

+ } + + export function getServerSideProps() { + const helper = require('@swc/helpers/lib/_object_spread.js') + console.log(helper) + return { + props: { + now: Date.now() + } + } + } + `, + }, + installCommand: + 'npm install; mkdir -p node_modules/next/node_modules/@swc; mv node_modules/@swc/helpers node_modules/next/node_modules/@swc/', + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it('should work', async () => { + const html = await renderViaHTTP(next.url, '/') + expect(html).toContain('hello world') + }) +}) diff --git a/test/lib/create-next-install.js b/test/lib/create-next-install.js index 5ef5965914..c8d776f4d6 100644 --- a/test/lib/create-next-install.js +++ b/test/lib/create-next-install.js @@ -55,12 +55,12 @@ async function createNextInstall( if (!(packageJson && packageJson.nextPrivateSkipLocalDeps)) { const pkgPaths = await linkPackages(tmpRepoDir) combinedDependencies = { + next: pkgPaths.get('next'), ...Object.keys(dependencies).reduce((prev, pkg) => { const pkgPath = pkgPaths.get(pkg) prev[pkg] = pkgPath || dependencies[pkg] return prev }, {}), - next: pkgPaths.get('next'), } }