diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 581cec0af01e..e873787ae58b 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1577,61 +1577,74 @@ export default async function getBaseWebpackConfig( }, { resolve: { - // Full list of old polyfills is accessible here: - // https://github.com/webpack/webpack/blob/2a0536cf510768111a3a6dceeb14cb79b9f59273/lib/ModuleNotFoundError.js#L13-L42 - fallback: { - assert: require.resolve('next/dist/compiled/assert'), - buffer: require.resolve('next/dist/compiled/buffer/'), - constants: require.resolve( - 'next/dist/compiled/constants-browserify' - ), - crypto: require.resolve( - 'next/dist/compiled/crypto-browserify' - ), - domain: require.resolve( - 'next/dist/compiled/domain-browser' - ), - http: require.resolve('next/dist/compiled/stream-http'), - https: require.resolve( - 'next/dist/compiled/https-browserify' - ), - os: require.resolve('next/dist/compiled/os-browserify'), - path: require.resolve( - 'next/dist/compiled/path-browserify' - ), - punycode: require.resolve( - 'next/dist/compiled/punycode' - ), - process: require.resolve('./polyfills/process'), - // Handled in separate alias - querystring: require.resolve( - 'next/dist/compiled/querystring-es3' - ), - stream: require.resolve( - 'next/dist/compiled/stream-browserify' - ), - string_decoder: require.resolve( - 'next/dist/compiled/string_decoder' - ), - sys: require.resolve('next/dist/compiled/util/'), - timers: require.resolve( - 'next/dist/compiled/timers-browserify' - ), - tty: require.resolve( - 'next/dist/compiled/tty-browserify' - ), - // Handled in separate alias - // url: require.resolve('url/'), - util: require.resolve('next/dist/compiled/util/'), - vm: require.resolve('next/dist/compiled/vm-browserify'), - zlib: require.resolve( - 'next/dist/compiled/browserify-zlib' - ), - events: require.resolve('next/dist/compiled/events/'), - setImmediate: require.resolve( - 'next/dist/compiled/setimmediate' - ), - }, + fallback: + config.experimental.fallbackNodePolyfills === false + ? {} + : { + assert: require.resolve( + 'next/dist/compiled/assert' + ), + buffer: require.resolve( + 'next/dist/compiled/buffer/' + ), + constants: require.resolve( + 'next/dist/compiled/constants-browserify' + ), + crypto: require.resolve( + 'next/dist/compiled/crypto-browserify' + ), + domain: require.resolve( + 'next/dist/compiled/domain-browser' + ), + http: require.resolve( + 'next/dist/compiled/stream-http' + ), + https: require.resolve( + 'next/dist/compiled/https-browserify' + ), + os: require.resolve( + 'next/dist/compiled/os-browserify' + ), + path: require.resolve( + 'next/dist/compiled/path-browserify' + ), + punycode: require.resolve( + 'next/dist/compiled/punycode' + ), + process: require.resolve('./polyfills/process'), + // Handled in separate alias + querystring: require.resolve( + 'next/dist/compiled/querystring-es3' + ), + stream: require.resolve( + 'next/dist/compiled/stream-browserify' + ), + string_decoder: require.resolve( + 'next/dist/compiled/string_decoder' + ), + sys: require.resolve('next/dist/compiled/util/'), + timers: require.resolve( + 'next/dist/compiled/timers-browserify' + ), + tty: require.resolve( + 'next/dist/compiled/tty-browserify' + ), + // Handled in separate alias + // url: require.resolve('url/'), + util: require.resolve('next/dist/compiled/util/'), + vm: require.resolve( + 'next/dist/compiled/vm-browserify' + ), + zlib: require.resolve( + 'next/dist/compiled/browserify-zlib' + ), + events: require.resolve( + 'next/dist/compiled/events/' + ), + setImmediate: require.resolve( + 'next/dist/compiled/setimmediate' + ), + }, }, }, ], diff --git a/packages/next/server/config-schema.ts b/packages/next/server/config-schema.ts index db74156d29e3..56def7e1fdef 100644 --- a/packages/next/server/config-schema.ts +++ b/packages/next/server/config-schema.ts @@ -258,6 +258,9 @@ const configSchema = { externalDir: { type: 'boolean', }, + fallbackNodePolyfills: { + type: 'boolean', + }, forceSwcTransforms: { type: 'boolean', }, diff --git a/packages/next/server/config-shared.ts b/packages/next/server/config-shared.ts index 0977434043f7..fe2238449320 100644 --- a/packages/next/server/config-shared.ts +++ b/packages/next/server/config-shared.ts @@ -145,6 +145,12 @@ export interface ExperimentalConfig { } swcPlugins?: Array<[string, Record]> largePageDataBytes?: number + /** + * If set to `false`, webpack won't fall back to polyfill Node.js modules in the browser + * Full list of old polyfills is accessible here: + * [webpack/webpack#ModuleNotoundError.js#L13-L42](https://github.com/webpack/webpack/blob/2a0536cf510768111a3a6dceeb14cb79b9f59273/lib/ModuleNotFoundError.js#L13-L42) + */ + fallbackNodePolyfills?: false } export type ExportPathMap = { diff --git a/test/production/disable-fallback-polyfills/index.test.ts b/test/production/disable-fallback-polyfills/index.test.ts new file mode 100644 index 000000000000..b22fd400cb09 --- /dev/null +++ b/test/production/disable-fallback-polyfills/index.test.ts @@ -0,0 +1,48 @@ +import { createNext } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' + +describe('Disable fallback polyfills', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/index.js': ` + import { useEffect } from 'react' + import crypto from 'crypto' + + export default function Page() { + useEffect(() => { + crypto; + }, []) + return

hello world

+ } + `, + }, + dependencies: {}, + }) + await next.stop() + }) + afterAll(() => next.destroy()) + + it('Fallback polyfills added by default', async () => { + const firstLoadJSSize = Number( + next.cliOutput.match(/○ \/\s{38}(\d*) kB\s{10}(?\d*) kB/) + .groups.firstLoad + ) + expect(firstLoadJSSize).not.toBeLessThan(200) + }) + + it('Build should fail, when fallback polyfilling is disabled', async () => { + await next.patchFile( + 'next.config.js', + `module.exports = { + experimental: { + fallbackNodePolyfills: false + } + }` + ) + + await expect(next.start()).rejects.toThrow(/next build failed/) + }) +})