Skip to content

Commit 8f3ecff

Browse files
authored
Merge branch '14-2-1' into ijjk/backport-fix-res-clone
2 parents 0364e9d + 73f6b7d commit 8f3ecff

File tree

31 files changed

+444
-22
lines changed

31 files changed

+444
-22
lines changed

packages/next/src/build/webpack-config.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,8 @@ export default async function getBaseWebpackConfig(
11921192
'next-metadata-route-loader',
11931193
'modularize-import-loader',
11941194
'next-barrel-loader',
1195+
'next-server-binary-loader',
1196+
'next-error-browser-binary-loader',
11951197
].reduce((alias, loader) => {
11961198
// using multiple aliases to replace `resolveLoader.modules`
11971199
alias[loader] = path.join(__dirname, 'webpack', 'loaders', loader)
@@ -1276,6 +1278,17 @@ export default async function getBaseWebpackConfig(
12761278
or: WEBPACK_LAYERS.GROUP.nonClientServerTarget,
12771279
},
12781280
},
1281+
{
1282+
test: /[\\/].*?\.node$/,
1283+
loader: isNodeServer
1284+
? 'next-server-binary-loader'
1285+
: 'next-error-browser-binary-loader',
1286+
// On server side bundling, only apply to app router, do not apply to pages router;
1287+
// On client side or edge runtime bundling, always error.
1288+
...(isNodeServer && {
1289+
issuerLayer: isWebpackAppLayer,
1290+
}),
1291+
},
12791292
...(hasAppDir
12801293
? [
12811294
{
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { webpack } from 'next/dist/compiled/webpack/webpack'
2+
3+
export default function nextErrorBrowserBinaryLoader(
4+
this: webpack.LoaderContext<any>
5+
) {
6+
const { resourcePath, rootContext } = this
7+
const relativePath = resourcePath.slice(rootContext.length + 1)
8+
throw new Error(
9+
`Node.js binary module ./${relativePath} is not supported in the browser. Please only use the module on server side`
10+
)
11+
}

packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,27 @@ function errorOnBadHandler(resourcePath: string) {
1414
`
1515
}
1616

17+
/* re-export the userland route configs */
18+
async function createReExportsCode(
19+
resourcePath: string,
20+
loaderContext: webpack.LoaderContext<any>
21+
) {
22+
const exportNames = await getLoaderModuleNamedExports(
23+
resourcePath,
24+
loaderContext
25+
)
26+
// Re-export configs but avoid conflicted exports
27+
const reExportNames = exportNames.filter(
28+
(name) => name !== 'default' && name !== 'generateSitemaps'
29+
)
30+
31+
return reExportNames.length > 0
32+
? `export { ${reExportNames.join(', ')} } from ${JSON.stringify(
33+
resourcePath
34+
)}\n`
35+
: ''
36+
}
37+
1738
const cacheHeader = {
1839
none: 'no-cache, no-store',
1940
longCache: 'public, immutable, no-transform, max-age=31536000',
@@ -83,7 +104,10 @@ export const dynamic = 'force-static'
83104
return code
84105
}
85106

86-
function getDynamicTextRouteCode(resourcePath: string) {
107+
async function getDynamicTextRouteCode(
108+
resourcePath: string,
109+
loaderContext: webpack.LoaderContext<any>
110+
) {
87111
return `\
88112
/* dynamic asset route */
89113
import { NextResponse } from 'next/server'
@@ -94,6 +118,7 @@ const contentType = ${JSON.stringify(getContentType(resourcePath))}
94118
const fileType = ${JSON.stringify(getFilenameAndExtension(resourcePath).name)}
95119
96120
${errorOnBadHandler(resourcePath)}
121+
${await createReExportsCode(resourcePath, loaderContext)}
97122
98123
export async function GET() {
99124
const data = await handler()
@@ -110,7 +135,10 @@ export async function GET() {
110135
}
111136

112137
// <metadata-image>/[id]/route.js
113-
function getDynamicImageRouteCode(resourcePath: string) {
138+
async function getDynamicImageRouteCode(
139+
resourcePath: string,
140+
loaderContext: webpack.LoaderContext<any>
141+
) {
114142
return `\
115143
/* dynamic image route */
116144
import { NextResponse } from 'next/server'
@@ -122,6 +150,7 @@ const handler = imageModule.default
122150
const generateImageMetadata = imageModule.generateImageMetadata
123151
124152
${errorOnBadHandler(resourcePath)}
153+
${await createReExportsCode(resourcePath, loaderContext)}
125154
126155
export async function GET(_, ctx) {
127156
const { __metadata_id__, ...params } = ctx.params || {}
@@ -160,10 +189,6 @@ async function getDynamicSiteMapRouteCode(
160189
resourcePath,
161190
loaderContext
162191
)
163-
// Re-export configs but avoid conflicted exports
164-
const reExportNames = exportNames.filter(
165-
(name) => name !== 'default' && name !== 'generateSitemaps'
166-
)
167192

168193
const hasGenerateSiteMaps = exportNames.includes('generateSitemaps')
169194
if (
@@ -197,15 +222,7 @@ const contentType = ${JSON.stringify(getContentType(resourcePath))}
197222
const fileType = ${JSON.stringify(getFilenameAndExtension(resourcePath).name)}
198223
199224
${errorOnBadHandler(resourcePath)}
200-
201-
${'' /* re-export the userland route configs */}
202-
${
203-
reExportNames.length > 0
204-
? `export { ${reExportNames.join(', ')} } from ${JSON.stringify(
205-
resourcePath
206-
)}\n`
207-
: ''
208-
}
225+
${await createReExportsCode(resourcePath, loaderContext)}
209226
210227
export async function GET(_, ctx) {
211228
const { __metadata_id__, ...params } = ctx.params || {}
@@ -266,11 +283,11 @@ const nextMetadataRouterLoader: webpack.LoaderDefinitionFunction<MetadataRouteLo
266283
let code = ''
267284
if (isDynamic === '1') {
268285
if (fileBaseName === 'robots' || fileBaseName === 'manifest') {
269-
code = getDynamicTextRouteCode(filePath)
286+
code = await getDynamicTextRouteCode(filePath, this)
270287
} else if (fileBaseName === 'sitemap') {
271288
code = await getDynamicSiteMapRouteCode(filePath, page, this)
272289
} else {
273-
code = getDynamicImageRouteCode(filePath)
290+
code = await getDynamicImageRouteCode(filePath, this)
274291
}
275292
} else {
276293
code = await getStaticAssetRouteCode(filePath, fileBaseName)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { webpack } from 'next/dist/compiled/webpack/webpack'
2+
import path from 'path'
3+
4+
export default function nextErrorBrowserBinaryLoader(
5+
this: webpack.LoaderContext<any>
6+
) {
7+
let relativePath = path.relative(this.rootContext, this.resourcePath)
8+
if (!relativePath.startsWith('.')) {
9+
relativePath = './' + relativePath
10+
}
11+
return `module.exports = __non_webpack_require__(${JSON.stringify(
12+
relativePath
13+
)})`
14+
}

packages/next/src/server/app-render/app-render.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,23 @@ async function generateFlight(
348348
}
349349
)
350350

351-
return new FlightRenderResult(flightReadableStream)
351+
const resultOptions: RenderResultOptions = {
352+
metadata: {},
353+
}
354+
355+
if (
356+
ctx.staticGenerationStore.pendingRevalidates ||
357+
ctx.staticGenerationStore.revalidatedTags
358+
) {
359+
resultOptions.waitUntil = Promise.all([
360+
ctx.staticGenerationStore.incrementalCache?.revalidateTag(
361+
ctx.staticGenerationStore.revalidatedTags || []
362+
),
363+
...Object.values(ctx.staticGenerationStore.pendingRevalidates || {}),
364+
])
365+
}
366+
367+
return new FlightRenderResult(flightReadableStream, resultOptions)
352368
}
353369

354370
type RenderToStreamResult = {
@@ -1349,7 +1365,10 @@ async function renderToHTMLOrFlightImpl(
13491365
})
13501366

13511367
// If we have pending revalidates, wait until they are all resolved.
1352-
if (staticGenerationStore.pendingRevalidates) {
1368+
if (
1369+
staticGenerationStore.pendingRevalidates ||
1370+
staticGenerationStore.revalidatedTags
1371+
) {
13531372
options.waitUntil = Promise.all([
13541373
staticGenerationStore.incrementalCache?.revalidateTag(
13551374
staticGenerationStore.revalidatedTags || []
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { RSC_CONTENT_TYPE_HEADER } from '../../client/components/app-router-headers'
2-
import RenderResult from '../render-result'
2+
import RenderResult, { type RenderResultOptions } from '../render-result'
33

44
/**
55
* Flight Response is always set to RSC_CONTENT_TYPE_HEADER to ensure it does not get interpreted as HTML.
66
*/
77
export class FlightRenderResult extends RenderResult {
8-
constructor(response: string | ReadableStream<Uint8Array>) {
9-
super(response, { contentType: RSC_CONTENT_TYPE_HEADER, metadata: {} })
8+
constructor(
9+
response: string | ReadableStream<Uint8Array>,
10+
options?: RenderResultOptions
11+
) {
12+
super(response, {
13+
contentType: RSC_CONTENT_TYPE_HEADER,
14+
waitUntil: options?.waitUntil,
15+
metadata: options?.metadata ?? {},
16+
})
1017
}
1118
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ReactNode } from 'react'
2+
export default function Root({ children }: { children: ReactNode }) {
3+
return (
4+
<html>
5+
<body>{children}</body>
6+
</html>
7+
)
8+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use client'
2+
3+
import { foo } from 'foo-browser-import-binary'
4+
5+
export default function Page() {
6+
return <p>{foo()}</p>
7+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
import {
3+
hasRedbox,
4+
getRedboxDescription,
5+
getRedboxSource,
6+
} from 'next-test-utils'
7+
;(process.env.TURBOPACK ? describe.skip : describe)(
8+
'externalize-node-binary-browser-error',
9+
() => {
10+
const { next } = nextTestSetup({
11+
files: __dirname,
12+
})
13+
14+
it('should error when import node binary on browser side', async () => {
15+
const browser = await next.browser('/')
16+
await hasRedbox(browser)
17+
const redbox = {
18+
description: await getRedboxDescription(browser),
19+
source: await getRedboxSource(browser),
20+
}
21+
22+
expect(redbox.description).toBe('Failed to compile')
23+
expect(redbox.source).toMatchInlineSnapshot(`
24+
"./node_modules/foo-browser-import-binary/binary.node
25+
Error: Node.js binary module ./node_modules/foo-browser-import-binary/binary.node is not supported in the browser. Please only use the module on server side"
26+
`)
27+
})
28+
}
29+
)

test/development/app-dir/externalize-node-binary-browser-error/node_modules/foo-browser-import-binary/binary.node

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)