Skip to content

Commit 3a975d7

Browse files
authored
feat(next): turbopack build support, fix incorrect handling of external packages (#14475)
Fixes #14338 and vercel/next.js#85110 (comment). This PR should fix support for using turbopack to build your Next.js project (which is now the default in Next.js 16, unless you pass `next build --webpack`). Previously, we were directly externalizing dependencies like `libsql` as they cause an error when being bundled. However, as those dependencies were not installed at the top-level by the user, the resulting `require('libsql')` calls could fail in production. Turbopack threw the following warning: ```bash Packages that should be external need to be installed in the project directory, so they can be resolved from the output files.\nTry to install it into the project directory by running ```` However, we ignored this warning, as we cannot force users to manually install dependencies of our dependencies. In Next.js 16, when using Turbopack build, this is no longer a warning but an error, which is why attempting to use turbopack build right now will not work. This PR properly fixes this externalization issue, by externalizing the `entry points` of those problematic packages (our db adapters). This means we no longer need to hide the warning.
1 parent fc2becf commit 3a975d7

File tree

2 files changed

+31
-63
lines changed

2 files changed

+31
-63
lines changed

packages/next/src/withPayload.js

Lines changed: 21 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,6 @@ export const withPayload = (nextConfig = {}, options = {}) => {
1515
env.NEXT_PUBLIC_ENABLE_ROUTER_CACHE_REFRESH = 'true'
1616
}
1717

18-
if (process.env.PAYLOAD_PATCH_TURBOPACK_WARNINGS !== 'false') {
19-
const turbopackWarningText =
20-
'Packages that should be external need to be installed in the project directory, so they can be resolved from the output files.\nTry to install it into the project directory by running'
21-
22-
const consoleWarn = console.warn
23-
console.warn = (...args) => {
24-
// Force to disable serverExternalPackages warnings: https://github.com/vercel/next.js/issues/68805
25-
if (
26-
(typeof args[1] === 'string' && args[1].includes(turbopackWarningText)) ||
27-
(typeof args[0] === 'string' && args[0].includes(turbopackWarningText))
28-
) {
29-
return
30-
}
31-
32-
consoleWarn(...args)
33-
}
34-
}
35-
3618
const poweredByHeader = {
3719
key: 'X-Powered-By',
3820
value: 'Next.js, Payload',
@@ -88,25 +70,32 @@ export const withPayload = (nextConfig = {}, options = {}) => {
8870
},
8971
serverExternalPackages: [
9072
...(nextConfig.serverExternalPackages || []),
91-
'drizzle-kit',
92-
'drizzle-kit/api',
93-
'pino',
94-
'libsql',
95-
'pino-pretty',
96-
'graphql',
97-
// Do not bundle server-only packages during dev to improve compile speed
73+
// These packages always need to be external, both during dev and production. This is because they install dependencies
74+
// that will error when trying to bundle them (e.g. drizzle-kit, libsql, esbuild etc.).
75+
// We cannot externalize those problem-packages directly. We can only externalize packages that are manually installed
76+
// by the end user. Otherwise, the require('externalPackage') calls generated by the bundler would fail during runtime,
77+
// as you cannot import dependencies of dependencies in a lot of package managers like pnpm. We'd have to force users
78+
// to install the dependencies directly.
79+
// Thus, we externalize the "entry-point" = the package that is installed by the end user, which would be our db adapters.
80+
//
81+
//
82+
// External because it installs mongoose (part of default serverExternalPackages: https://github.com/vercel/next.js/blob/canary/packages/next/src/lib/server-external-packages.json => would throw warning if we don't exclude the entry-point package):
83+
'@payloadcms/db-mongodb',
84+
// External because they install dependencies like drizzle, libsql, esbuild etc.:
85+
'@payloadcms/db-postgres',
86+
'@payloadcms/db-sqlite',
87+
'@payloadcms/db-vercel-postgres',
88+
'@payloadcms/drizzle',
89+
// External because they install @aws-sdk/client-s3:
90+
'@payloadcms/payload-cloud',
91+
// TODO: We need to externalize @payloadcms/storage-s3 as well, once Next.js has the ability to exclude @payloadcms/storage-s3/client from being externalized.
92+
// Do not bundle additional server-only packages during dev to improve compilation speed
9893
...(process.env.NODE_ENV === 'development' && options.devBundleServerPackages === false
9994
? [
10095
'payload',
101-
'@payloadcms/db-mongodb',
102-
'@payloadcms/db-postgres',
103-
'@payloadcms/db-sqlite',
104-
'@payloadcms/db-vercel-postgres',
105-
'@payloadcms/drizzle',
10696
'@payloadcms/email-nodemailer',
10797
'@payloadcms/email-resend',
10898
'@payloadcms/graphql',
109-
'@payloadcms/payload-cloud',
11099
'@payloadcms/plugin-redirects',
111100
// TODO: Add the following packages, excluding their /client subpath exports, once Next.js supports it
112101
//'@payloadcms/plugin-cloud-storage',
@@ -129,45 +118,14 @@ export const withPayload = (nextConfig = {}, options = {}) => {
129118

130119
return {
131120
...incomingWebpackConfig,
132-
externals: [
133-
...(incomingWebpackConfig?.externals || []),
134-
'drizzle-kit',
135-
'drizzle-kit/api',
136-
'sharp',
137-
'libsql',
138-
'require-in-the-middle',
139-
],
140-
ignoreWarnings: [
141-
...(incomingWebpackConfig?.ignoreWarnings || []),
142-
{ module: /node_modules\/mongodb\/lib\/utils\.js/ },
143-
{ file: /node_modules\/mongodb\/lib\/utils\.js/ },
144-
{ module: /node_modules\/mongodb\/lib\/bson\.js/ },
145-
{ file: /node_modules\/mongodb\/lib\/bson\.js/ },
146-
],
121+
externals: [...(incomingWebpackConfig?.externals || []), 'require-in-the-middle'],
147122
plugins: [
148123
...(incomingWebpackConfig?.plugins || []),
149124
// Fix cloudflare:sockets error: https://github.com/vercel/next.js/discussions/50177
150125
new webpackOptions.webpack.IgnorePlugin({
151126
resourceRegExp: /^pg-native$|^cloudflare:sockets$/,
152127
}),
153128
],
154-
resolve: {
155-
...(incomingWebpackConfig?.resolve || {}),
156-
alias: {
157-
...(incomingWebpackConfig?.resolve?.alias || {}),
158-
},
159-
fallback: {
160-
...(incomingWebpackConfig?.resolve?.fallback || {}),
161-
'@aws-sdk/credential-providers': false,
162-
'@mongodb-js/zstd': false,
163-
aws4: false,
164-
kerberos: false,
165-
'mongodb-client-encryption': false,
166-
snappy: false,
167-
'supports-color': false,
168-
'yocto-queue': false,
169-
},
170-
},
171129
}
172130
},
173131
}

test/plugin-multi-tenant/e2e.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,17 +577,22 @@ test.describe('Multi Tenant', () => {
577577
page,
578578
serverURL,
579579
})
580+
await wait(1000)
580581

581582
await goToListDoc({
582583
cellClass: '.cell-name',
583584
page,
584585
textToMatch: 'Blue Dog',
585586
urlUtil: tenantsURL,
586587
})
588+
await wait(1000)
587589

588590
await expect(page.locator('#field-name')).toBeVisible()
589591
await page.locator('#field-name').fill('Red Dog')
592+
await wait(1000)
593+
590594
await saveDocAndAssert(page)
595+
await wait(1000)
591596

592597
await page.goto(tenantsURL.list)
593598
// Wait for backend tenant cache to update after save operation
@@ -599,17 +604,22 @@ test.describe('Multi Tenant', () => {
599604
return (await getTenantOptions({ page })).sort()
600605
})
601606
.toEqual(['Red Dog', 'Steel Cat', 'Public Tenant', 'Anchor Bar'].sort())
607+
await wait(1000)
602608

603609
await goToListDoc({
604610
cellClass: '.cell-name',
605611
page,
606612
textToMatch: 'Red Dog',
607613
urlUtil: tenantsURL,
608614
})
615+
await wait(1000)
609616

610617
// Change the tenant back to the original name
611618
await page.locator('#field-name').fill('Blue Dog')
619+
await wait(1000)
620+
612621
await saveDocAndAssert(page)
622+
await wait(1000)
613623

614624
await page.goto(tenantsURL.list)
615625
// Wait for backend tenant cache to update after save operation

0 commit comments

Comments
 (0)