Skip to content

Commit e1da07e

Browse files
huozhiijjk
andauthored
Externalize node binary modules for app router (#70646)
backport #70330 Fixes #69912 --------- Co-authored-by: JJ Kasper <jj@jjsweb.site>
1 parent 0ffea65 commit e1da07e

15 files changed

Lines changed: 139 additions & 0 deletions

File tree

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+
}
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+
}
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.

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

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

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

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
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+
}

0 commit comments

Comments
 (0)