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)
This commit is contained in:
JJ Kasper 2022-06-29 17:47:44 -05:00 committed by GitHub
parent 66b83f4506
commit 8a5c59b7f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 2 deletions

View file

@ -56,7 +56,7 @@ function getBaseSWCOptions({
paths,
}
: {}),
externalHelpers: !process.versions.pnp,
externalHelpers: !process.versions.pnp && !jest,
parser: parserConfig,
experimental: {
keepImportAssertions: true,

View file

@ -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'

View file

@ -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 <p>hello world</p>
}
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')
})
})

View file

@ -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'),
}
}