Add browser polyfils for Node.js modules (webpack 5 backwards compat) (#16022)

This adds the following Node.js core polyfills only when the import is used:

- `path`
- `stream`
- `vm`
- `crypto`
- `buffer`

Fixes #15948

We'll have a separate issue about adding warnings for the usage of these modules in the browser, some polyfills like crypto are quite heavy and generally not needed for most applications (included accidentally through node_modules).
This commit is contained in:
Tim Neutkens 2020-08-10 03:26:21 +02:00 committed by GitHub
parent 394e24073d
commit 843d58425b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 189 additions and 7 deletions

View file

@ -356,7 +356,16 @@ export default async function getBaseWebpackConfig(
'next/router': 'next/dist/client/router.js',
'next/config': 'next/dist/next-server/lib/runtime-config.js',
'next/dynamic': 'next/dist/next-server/lib/dynamic.js',
next: NEXT_PROJECT_ROOT,
...(isServer
? {}
: {
stream: 'stream-browserify',
path: 'path-browserify',
crypto: 'crypto-browserify',
buffer: 'buffer',
vm: 'vm-browserify',
next: NEXT_PROJECT_ROOT,
}),
[PAGES_DIR_ALIAS]: pagesDir,
[DOT_NEXT_ALIAS]: distDir,
...getOptimizedAliases(isServer),

View file

@ -87,6 +87,7 @@
"buffer": "5.6.0",
"cacache": "13.0.1",
"chokidar": "2.1.8",
"crypto-browserify": "3.12.0",
"css-loader": "3.5.3",
"cssnano-simple": "1.0.6",
"find-cache-dir": "3.3.1",
@ -97,6 +98,7 @@
"native-url": "0.3.4",
"neo-async": "2.6.1",
"node-html-parser": "^1.2.19",
"path-browserify": "1.0.1",
"pnp-webpack-plugin": "1.6.4",
"postcss": "7.0.32",
"process": "0.11.10",
@ -107,9 +109,11 @@
"resolve-url-loader": "3.1.1",
"sass-loader": "8.0.2",
"schema-utils": "2.6.6",
"stream-browserify": "3.0.0",
"style-loader": "1.2.1",
"styled-jsx": "3.3.0",
"use-subscription": "1.4.1",
"vm-browserify": "1.1.2",
"watchpack": "2.0.0-beta.13",
"web-vitals": "0.2.1",
"webpack": "4.44.1",

View file

@ -0,0 +1,48 @@
import { Writable } from 'stream'
import path from 'path'
import crypto from 'crypto'
import { Buffer } from 'buffer'
import vm from 'vm'
import { useEffect, useState } from 'react'
export default function NodeBrowserPolyfillPage() {
const [state, setState] = useState({})
useEffect(() => {
let closedStream = false
const writable = new Writable({
write(_chunk, _encoding, callback) {
callback()
},
})
writable.on('finish', () => {
closedStream = true
})
writable.end()
setState({
path: path.join('/hello/world', 'test.txt'),
hash: crypto.createHash('sha256').update('hello world').digest('hex'),
buffer: Buffer.from('hello world').toString('utf8'),
vm: vm.runInNewContext('a + 5', { a: 100 }),
stream: closedStream,
})
}, [])
useEffect(() => {
if (state.vm) {
window.didRender = true
}
}, [state])
return (
<>
<div
id="node-browser-polyfills"
dangerouslySetInnerHTML={{ __html: JSON.stringify(state) }}
></div>
</>
)
}

View file

@ -2,6 +2,7 @@
import { join } from 'path'
import { renderViaHTTP, findPort, launchApp, killApp } from 'next-test-utils'
import webdriver from 'next-webdriver'
// test suits
import hmr from './hmr'
@ -33,6 +34,24 @@ describe('Basic Features', () => {
})
afterAll(() => killApp(context.server))
it('should polyfill Node.js modules', async () => {
const browser = await webdriver(context.appPort, '/node-browser-polyfills')
await browser.waitForCondition('window.didRender')
const data = await browser
.waitForElementByCss('#node-browser-polyfills')
.text()
const parsedData = JSON.parse(data)
expect(parsedData.vm).toBe(105)
expect(parsedData.hash).toBe(
'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'
)
expect(parsedData.path).toBe('/hello/world/test.txt')
expect(parsedData.buffer).toBe('hello world')
expect(parsedData.stream).toBe(true)
})
dynamic(context, (p, q) => renderViaHTTP(context.appPort, p, q))
hmr(context, (p, q) => renderViaHTTP(context.appPort, p, q))
errorRecovery(context, (p, q) => renderViaHTTP(context.appPort, p, q))

View file

@ -0,0 +1,48 @@
import { Writable } from 'stream'
import path from 'path'
import crypto from 'crypto'
import { Buffer } from 'buffer'
import vm from 'vm'
import { useEffect, useState } from 'react'
export default function NodeBrowserPolyfillPage() {
const [state, setState] = useState({})
useEffect(() => {
let closedStream = false
const writable = new Writable({
write(_chunk, _encoding, callback) {
callback()
},
})
writable.on('finish', () => {
closedStream = true
})
writable.end()
setState({
path: path.join('/hello/world', 'test.txt'),
hash: crypto.createHash('sha256').update('hello world').digest('hex'),
buffer: Buffer.from('hello world').toString('utf8'),
vm: vm.runInNewContext('a + 5', { a: 100 }),
stream: closedStream,
})
}, [])
useEffect(() => {
if (state.vm) {
window.didRender = true
}
}, [state])
return (
<>
<div
id="node-browser-polyfills"
dangerouslySetInnerHTML={{ __html: JSON.stringify(state) }}
></div>
</>
)
}

View file

@ -70,6 +70,24 @@ describe('Production Usage', () => {
})
}
it('should polyfill Node.js modules', async () => {
const browser = await webdriver(appPort, '/node-browser-polyfills')
await browser.waitForCondition('window.didRender')
const data = await browser
.waitForElementByCss('#node-browser-polyfills')
.text()
const parsedData = JSON.parse(data)
expect(parsedData.vm).toBe(105)
expect(parsedData.hash).toBe(
'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'
)
expect(parsedData.path).toBe('/hello/world/test.txt')
expect(parsedData.buffer).toBe('hello world')
expect(parsedData.stream).toBe(true)
})
it('should allow etag header support', async () => {
const url = `http://localhost:${appPort}/`
const etag = (await fetch(url)).headers.get('ETag')

View file

@ -4121,7 +4121,7 @@ browserify-zlib@^0.2.0:
dependencies:
pako "~1.0.5"
browserslist@4.13.0, browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6, browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.13.0, browserslist@^4.3.6, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.8.5:
browserslist@4.13.0, browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.13.0, browserslist@^4.3.6, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.8.5:
version "4.13.0"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.13.0.tgz#42556cba011e1b0a2775b611cba6a8eca18e940d"
integrity sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==
@ -4131,6 +4131,14 @@ browserslist@4.13.0, browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7
escalade "^3.0.1"
node-releases "^1.1.58"
browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
version "1.7.7"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
integrity sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=
dependencies:
caniuse-db "^1.0.30000639"
electron-to-chromium "^1.2.7"
browserstack-local@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/browserstack-local/-/browserstack-local-1.4.0.tgz#d979cac056f57b9af159b3bcd7fdc09b4354537c"
@ -4460,11 +4468,21 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634:
version "1.0.30001023"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001023.tgz#f856f71af16a5a44e81f1fcefc1673912a43da72"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001019, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001093:
caniuse-db@^1.0.30000639:
version "1.0.30001112"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001112.tgz#89cb249402af2ebf2c23a5585d1bd78cb5e38732"
integrity sha512-Xrn3lVEIsvAAUmFVHKKComfyCRM59NfuV3EwCXqs2XLgOxAqYgrfEs0vDk+Dk7KctlAMh6CH/CSd1srJt503ZA==
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001019, caniuse-lite@^1.0.30001020:
version "1.0.30001066"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz#0a8a58a10108f2b9bf38e7b65c237b12fd9c5f04"
integrity sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw==
caniuse-lite@^1.0.30001093:
version "1.0.30001112"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001112.tgz#0fffc3b934ff56ff0548c37bc9dad7d882bcf672"
integrity sha512-J05RTQlqsatidif/38aN3PGULCLrg8OYQOlJUKbeYVzC2mGZkZLIztwRlB3MtrfLmawUmjFlNJvy/uhwniIe1Q==
capitalize@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/capitalize/-/capitalize-1.0.0.tgz#dc802c580aee101929020d2ca14b4ca8a0ae44be"
@ -5376,7 +5394,7 @@ cross-spawn@^7.0.0:
shebang-command "^2.0.0"
which "^2.0.1"
crypto-browserify@^3.11.0:
crypto-browserify@3.12.0, crypto-browserify@^3.11.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
@ -6190,6 +6208,11 @@ ejs@^2.6.1:
version "2.7.4"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba"
electron-to-chromium@^1.2.7:
version "1.3.526"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.526.tgz#0e004899edf75afc172cce1b8189aac5dca646aa"
integrity sha512-HiroW5ZbGwgT8kCnoEO8qnGjoTPzJxduvV/Vv/wH63eo2N6Zj3xT5fmmaSPAPUM05iN9/5fIEkIg3owTtV6QZg==
electron-to-chromium@^1.3.488:
version "1.3.501"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.501.tgz#faa17a2cb0105ee30d5e1ca87eae7d8e85dd3175"
@ -8108,7 +8131,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
@ -11695,6 +11718,11 @@ path-browserify@0.0.1:
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
path-browserify@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
path-case@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5"
@ -13337,7 +13365,7 @@ read@1, read@~1.0.1:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@^3.4.0, readable-stream@^3.6.0:
readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@ -14717,6 +14745,14 @@ stealthy-require@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
stream-browserify@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f"
integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==
dependencies:
inherits "~2.0.4"
readable-stream "^3.5.0"
stream-browserify@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
@ -15970,7 +16006,7 @@ vlq@^0.2.1, vlq@^0.2.2:
version "0.2.3"
resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26"
vm-browserify@^1.0.1:
vm-browserify@1.1.2, vm-browserify@^1.0.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==