diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 9a0e0cbb8e95b..1f4fb64e6b07d 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -502,7 +502,6 @@ jobs: export NEXT_TEST_MODE=start export NEXT_TEST_WASM=true - export IS_WEBPACK_TEST=1 node run-tests.js \ test/production/pages-dir/production/test/index.test.ts \ test/e2e/streaming-ssr/index.test.ts @@ -712,7 +711,7 @@ jobs: secrets: inherit - test-dev: # TODO: rename to include webpack + test-dev: name: test dev needs: ['optimize-ci', 'changes', 'build-native', 'build-next'] if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }} @@ -729,7 +728,6 @@ jobs: uses: ./.github/workflows/build_reusable.yml with: afterBuild: | - export IS_WEBPACK_TEST=1 export NEXT_TEST_MODE=dev export NEXT_TEST_REACT_VERSION="${{ matrix.react }}" @@ -754,10 +752,8 @@ jobs: uses: ./.github/workflows/build_reusable.yml with: - # Should this be using turbopack? a variation? afterBuild: | export NEXT_TEST_MODE=dev - export IS_WEBPACK_TEST=1 node run-tests.js \ test/e2e/app-dir/app/index.test.ts \ @@ -784,7 +780,6 @@ jobs: with: nodeVersion: 20.9.0 afterBuild: | - export IS_WEBPACK_TEST=1 node run-tests.js \ --concurrency 4 \ test/production/pages-dir/production/test/index.test.ts \ @@ -813,7 +808,6 @@ jobs: with: afterBuild: | export NEXT_TEST_MODE=start - export IS_WEBPACK_TEST=1 node run-tests.js --type production \ test/e2e/app-dir/app/index.test.ts \ @@ -842,7 +836,6 @@ jobs: uses: ./.github/workflows/build_reusable.yml with: afterBuild: | - export IS_WEBPACK_TEST=1 export NEXT_TEST_MODE=start export NEXT_TEST_REACT_VERSION="${{ matrix.react }}" @@ -882,7 +875,6 @@ jobs: with: nodeVersion: 20.9.0 afterBuild: | - export IS_WEBPACK_TEST=1 export NEXT_TEST_REACT_VERSION="${{ matrix.react }}" node run-tests.js \ @@ -904,7 +896,6 @@ jobs: # these all run without concurrency because they're heavier export TEST_CONCURRENCY=1 - export IS_WEBPACK_TEST=1 BROWSER_NAME=firefox node run-tests.js \ test/production/pages-dir/production/test/index.test.ts @@ -932,7 +923,6 @@ jobs: afterBuild: | export __NEXT_EXPERIMENTAL_PPR=true export NEXT_EXTERNAL_TESTS_FILTERS="test/ppr-tests-manifest.json" - export IS_WEBPACK_TEST=1 node run-tests.js \ --timings \ @@ -955,7 +945,6 @@ jobs: export __NEXT_EXPERIMENTAL_PPR=true export NEXT_EXTERNAL_TESTS_FILTERS="test/ppr-tests-manifest.json" export NEXT_TEST_MODE=dev - export IS_WEBPACK_TEST=1 node run-tests.js \ --timings \ @@ -979,7 +968,6 @@ jobs: export __NEXT_EXPERIMENTAL_PPR=true export NEXT_EXTERNAL_TESTS_FILTERS="test/ppr-tests-manifest.json" export NEXT_TEST_MODE=start - export IS_WEBPACK_TEST=1 node run-tests.js \ --timings \ @@ -1002,7 +990,6 @@ jobs: export __NEXT_EXPERIMENTAL_PPR=true # for compatibility with the existing tests export __NEXT_EXPERIMENTAL_CACHE_COMPONENTS=true export NEXT_EXTERNAL_TESTS_FILTERS="test/experimental-tests-manifest.json" - export IS_WEBPACK_TEST=1 node run-tests.js \ --timings \ @@ -1027,7 +1014,6 @@ jobs: export NEXT_EXTERNAL_TESTS_FILTERS="test/experimental-tests-manifest.json" export NEXT_TEST_MODE=dev export __NEXT_EXPERIMENTAL_ISOLATED_DEV_BUILD=true - export IS_WEBPACK_TEST=1 node run-tests.js \ --timings \ @@ -1052,7 +1038,6 @@ jobs: export __NEXT_EXPERIMENTAL_CACHE_COMPONENTS=true export NEXT_EXTERNAL_TESTS_FILTERS="test/experimental-tests-manifest.json" export NEXT_TEST_MODE=start - export IS_WEBPACK_TEST=1 node run-tests.js \ --timings \ diff --git a/.github/workflows/pull_request_stats.yml b/.github/workflows/pull_request_stats.yml index 9fe1fc91701bc..3b3afeee5da3b 100644 --- a/.github/workflows/pull_request_stats.yml +++ b/.github/workflows/pull_request_stats.yml @@ -60,7 +60,3 @@ jobs: - uses: ./.github/actions/next-stats-action if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - env: - # This uses the webpack bundle analyzer and for consistent results we need to use webpack. - # Once there is an equivalent analyzer for turbopack, we can remove this. - IS_WEBPACK_TEST: 1 diff --git a/.github/workflows/test_e2e_deploy_release.yml b/.github/workflows/test_e2e_deploy_release.yml index 9c0d33eeefde9..631b0a2b43f46 100644 --- a/.github/workflows/test_e2e_deploy_release.yml +++ b/.github/workflows/test_e2e_deploy_release.yml @@ -78,15 +78,7 @@ jobs: matrix: group: [1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8] with: - afterBuild: | - npm i -g vercel@latest && \ - NEXT_E2E_TEST_TIMEOUT=240000 \ - NEXT_TEST_MODE=deploy \ - IS_WEBPACK_TEST=1 \ - NEXT_EXTERNAL_TESTS_FILTERS="test/deploy-tests-manifest.json" \ - NEXT_TEST_VERSION="${{ github.event.inputs.nextVersion || 'canary' }}" \ - VERCEL_CLI_VERSION="${{ github.event.inputs.vercelCliVersion || 'vercel@latest' }}" \ - node run-tests.js --timings -g ${{ matrix.group }} -c 2 --type e2e + afterBuild: npm i -g vercel@latest && NEXT_E2E_TEST_TIMEOUT=240000 NEXT_TEST_MODE=deploy NEXT_EXTERNAL_TESTS_FILTERS="test/deploy-tests-manifest.json" NEXT_TEST_VERSION="${{ github.event.inputs.nextVersion || 'canary' }}" VERCEL_CLI_VERSION="${{ github.event.inputs.vercelCliVersion || 'vercel@latest' }}" node run-tests.js --timings -g ${{ matrix.group }} -c 2 --type e2e skipNativeBuild: 'yes' skipNativeInstall: 'no' stepName: 'test-deploy-${{ matrix.group }}' diff --git a/bench/heavy-npm-deps/package.json b/bench/heavy-npm-deps/package.json index 8bd442a10f8a5..2428e307218e8 100644 --- a/bench/heavy-npm-deps/package.json +++ b/bench/heavy-npm-deps/package.json @@ -4,9 +4,9 @@ "private": true, "scripts": { "dev-turbopack": "next dev --turbopack", - "dev-webpack": "next dev --webpack", + "dev-webpack": "next dev", "build-turbopack": "next build --turbopack", - "build-webpack": "next build --webpack", + "build-webpack": "next build", "start-turbopack": "next start", "start-webpack": "next start", "build-application": "next build", diff --git a/bench/module-cost/package.json b/bench/module-cost/package.json index 96814e0e6e875..eb6e8a7478d92 100644 --- a/bench/module-cost/package.json +++ b/bench/module-cost/package.json @@ -3,10 +3,10 @@ "scripts": { "prepare-bench": "node scripts/prepare-bench.mjs", "benchmark": "node scripts/benchmark-runner.mjs", - "dev-webpack": "next dev --webpack", - "dev-turbopack": "next dev --turbopack", - "build-webpack": "next build --webpack", - "build-turbopack": "next build --turbopack", + "dev-webpack": "next dev", + "dev-turbopack": "next dev --turbo", + "build-webpack": "next build", + "build-turbopack": "next build --turbo", "start": "next start" }, "devDependencies": { diff --git a/package.json b/package.json index bcefe2d27693f..3a25302936fe9 100644 --- a/package.json +++ b/package.json @@ -17,49 +17,29 @@ "ci:publish": "tsx ./scripts/release/publish-npm.ts", "test-types": "tsc", "test-unit": "jest test/unit/ packages/next/ packages/font", - "test-dev-inner": "cross-env NEXT_TEST_MODE=dev pnpm testheadless", - "test-dev": "pnpm run test-dev-webpack", - "test-dev-webpack": "pnpm run with-webpack pnpm test-dev-inner", - "test-dev-experimental-inner": "pnpm run with-experimental pnpm test-dev-inner", - "test-dev-experimental": "pnpm run test-dev-experimental-webpack", - "test-dev-experimental-webpack": "pnpm run with-webpack pnpm test-dev-experimental-inner", - "test-dev-rspack": "pnpm run with-rspack pnpm run test-dev-inner", - "test-dev-experimental-rspack": "pnpm run with-rspack pnpm run test-dev-experimental-inner", - "test-dev-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm test-dev-inner", - "test-dev-experimental-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm run with-experimental pnpm test-dev-inner", - "test-start-inner": "cross-env NEXT_TEST_MODE=start pnpm testheadless", - "test-start": "pnpm run test-start-webpack", - "test-start-webpack": "pnpm run with-webpack pnpm run test-start-inner", - "test-start-experimental-inner": "pnpm run with-experimental pnpm test-start-inner", - "test-start-experimental": "pnpm run test-start-experimental-webpack", - "test-start-experimental-webpack": "pnpm run with-webpack pnpm run test-start-experimental-inner", - "test-start-rspack": "pnpm run with-rspack pnpm run test-start-inner", - "test-start-experimental-rspack": "pnpm run with-rspack pnpm run test-start-experimental-inner", - "test-start-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm test-start-inner", - "test-start-experimental-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm run with-experimental pnpm test-start-experimental-inner", - "test-deploy-inner": "cross-env NEXT_TEST_MODE=deploy pnpm testheadless", - "test-deploy": "pnpm run test-deploy-webpack", - "test-deploy-webpack": "pnpm run with-webpack pnpm test-deploy-inner", - "test-deploy-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm test-deploy-inner", - "testonly-dev-inner": "cross-env NEXT_TEST_MODE=dev pnpm testonly", - "testonly-dev": "pnpm run testonly-dev-webpack", - "testonly-dev-webpack": "pnpm run with-webpack pnpm run testonly-dev-inner", - "testonly-dev-rspack": "pnpm run with-rspack pnpm run testonly-dev-inner", - "testonly-dev-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm testonly-dev-inner", - "testonly-start-inner": "cross-env NEXT_TEST_MODE=start pnpm testonly", - "testonly-start": "pnpm run testonly-start-webpack", - "testonly-start-webpack": "pnpm run with-webpack pnpm run testonly-start-inner", - "testonly-start-rspack": "pnpm run with-rspack pnpm run testonly-start-inner", - "testonly-start-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm testonly-start-inner", - "testonly-deploy-inner": "cross-env NEXT_TEST_MODE=deploy pnpm testonly", - "testonly-deploy": "pnpm run testonly-deploy-webpack", - "testonly-deploy-webpack": "pnpm run with-webpack pnpm run testonly-deploy-inner", - "testonly-deploy-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm testonly-deploy-inner", - "test-inner": "pnpm testheadless", - "test": "pnpm test-webpack", - "test-webpack": "pnpm run with-webpack pnpm run test-inner", - "test-rspack": "pnpm run with-rspack pnpm run test-inner", - "test-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 TURBOPACK_BUILD=1 pnpm test-inner", + "test-dev": "cross-env NEXT_TEST_MODE=dev pnpm testheadless", + "test-dev-experimental": "cross-env NEXT_TEST_MODE=dev pnpm run with-experimental pnpm testheadless", + "test-dev-rspack": "pnpm run with-rspack pnpm run test-dev", + "test-dev-experimental-rspack": "pnpm run with-rspack pnpm run test-dev-experimental", + "test-dev-turbo": "cross-env NEXT_TEST_MODE=dev IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm testheadless", + "test-dev-experimental-turbo": "cross-env NEXT_TEST_MODE=dev IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm run with-experimental pnpm testheadless", + "test-start": "cross-env NEXT_TEST_MODE=start pnpm testheadless", + "test-start-experimental": "cross-env NEXT_TEST_MODE=start pnpm run with-experimental pnpm testheadless", + "test-start-rspack": "pnpm run with-rspack pnpm run test-start", + "test-start-experimental-rspack": "pnpm run with-rspack pnpm run test-start-experimental", + "test-start-turbo": "cross-env NEXT_TEST_MODE=start IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm testheadless", + "test-start-experimental-turbo": "cross-env NEXT_TEST_MODE=start IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm run with-experimental pnpm testheadless", + "test-deploy": "cross-env NEXT_TEST_MODE=deploy pnpm testheadless", + "testonly-dev": "cross-env NEXT_TEST_MODE=dev pnpm testonly", + "testonly-dev-rspack": "pnpm run with-rspack pnpm run testonly-dev", + "testonly-dev-turbo": "cross-env NEXT_TEST_MODE=dev IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm testonly", + "testonly-start": "cross-env NEXT_TEST_MODE=start pnpm testonly", + "testonly-start-rspack": "pnpm run with-rspack pnpm run testonly-start", + "testonly-start-turbo": "cross-env NEXT_TEST_MODE=start IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm testonly", + "testonly-deploy": "cross-env NEXT_TEST_MODE=deploy pnpm testonly", + "test": "pnpm testheadless", + "test-rspack": "pnpm run with-rspack pnpm run test", + "test-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 TURBOPACK_BUILD=1 pnpm testheadless", "testonly": "jest --runInBand", "testheadless": "cross-env HEADLESS=true pnpm testonly", "genstats": "cross-env LOCAL_STATS=true node .github/actions/next-stats-action/src/index.js", @@ -109,7 +89,6 @@ "build-storybook": "turbo run build-storybook", "test-storybook": "turbo run test-storybook", "with-rspack": "cross-env NEXT_RSPACK=1 NEXT_TEST_USE_RSPACK=1", - "with-webpack": "cross-env IS_WEBPACK_TEST=1", "with-experimental": "cross-env __NEXT_EXPERIMENTAL_CACHE_COMPONENTS=true __NEXT_EXPERIMENTAL_PPR=true" }, "devDependencies": { diff --git a/packages/next-rspack/index.js b/packages/next-rspack/index.js index adcc47df109ec..e0ce02d637d84 100644 --- a/packages/next-rspack/index.js +++ b/packages/next-rspack/index.js @@ -1,13 +1,5 @@ module.exports = function withRspack(config) { process.env.NEXT_RSPACK = 'true' process.env.RSPACK_CONFIG_VALIDATE = 'loose-silent' - if (process.env.TURBOPACK === 'auto') { - // If next has defaulted to turbopack, override it. - delete process.env.TURBOPACK - } else if (process.env.TURBOPACK) { - console.error('Cannot call withRspack and pass the --turbopack flag.') - console.error('Please configure only one bundler.') - process.exit(1) - } return config } diff --git a/packages/next/src/bin/next.ts b/packages/next/src/bin/next.ts index a947d1ad95aee..cfcb4b5b355b7 100755 --- a/packages/next/src/bin/next.ts +++ b/packages/next/src/bin/next.ts @@ -133,9 +133,8 @@ program .option('--no-mangling', 'Disables mangling.') .option('--profile', 'Enables production profiling for React.') .option('--experimental-app-only', 'Builds only App Router routes.') - .option('--turbo', 'Builds using Turbopack.') - .option('--turbopack', 'Builds using Turbopack.') - .option('--webpack', 'Builds using webpack.') + .option('--turbo', 'Starts development mode using Turbopack.') + .option('--turbopack', 'Starts development mode using Turbopack.') .addOption( new Option( '--experimental-build-mode [mode]', @@ -174,7 +173,6 @@ program ) .option('--turbo', 'Starts development mode using Turbopack.') .option('--turbopack', 'Starts development mode using Turbopack.') - .option('--webpack', 'Starts development mode using webpack.') .addOption( new Option( '-p, --port ', diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 5394a9b2bd593..9225b231b9d6f 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -83,7 +83,6 @@ import { UNDERSCORE_GLOBAL_ERROR_ROUTE_ENTRY, } from '../shared/lib/entry-constants' import { isDynamicRoute } from '../shared/lib/router/utils' -import { Bundler, finalizeBundlerFromConfig } from '../lib/bundler' import type { __ApiPreviewProps } from '../server/api-utils' import loadConfig from '../server/config' import type { BuildManifest } from '../server/get-page-files' @@ -902,7 +901,7 @@ export default async function build( runLint = true, noMangling = false, appDirOnly = false, - bundler = Bundler.Turbopack, + isTurbopack = false, experimentalBuildMode: 'default' | 'compile' | 'generate' | 'generate-env', traceUploadUrl: string | undefined ): Promise { @@ -915,6 +914,7 @@ export default async function build( try { const nextBuildSpan = trace('next-build', undefined, { buildMode: experimentalBuildMode, + isTurboBuild: String(isTurbopack), version: process.env.__NEXT_VERSION as string, }) @@ -949,10 +949,6 @@ export default async function build( ) loadedConfig = config - // Reading the config can modify environment variables that influence the bundler selection. - bundler = finalizeBundlerFromConfig(bundler) - nextBuildSpan.setAttribute('bundler', getBundlerForTelemetry(bundler)) - process.env.NEXT_DEPLOYMENT_ID = config.deploymentId || '' NextBuildContext.config = config @@ -975,7 +971,7 @@ export default async function build( NextBuildContext.buildId = buildId if (experimentalBuildMode === 'generate-env') { - if (bundler === Bundler.Turbopack) { + if (isTurbopack) { Log.warn('generate-env is not needed with turbopack') process.exit(0) } @@ -1402,7 +1398,7 @@ export default async function build( }) // Turbopack already handles conflicting app and page routes. - if (bundler !== Bundler.Turbopack) { + if (!isTurbopack) { const numConflictingAppPaths = conflictingAppPagePaths.length if (mappedAppPages && numConflictingAppPaths > 0) { Log.error( @@ -1685,7 +1681,7 @@ export default async function build( let shutdownPromise = Promise.resolve() if (!isGenerateMode) { - if (bundler === Bundler.Turbopack) { + if (isTurbopack) { const { duration: compilerDuration, shutdownPromise: p, @@ -1799,7 +1795,7 @@ export default async function build( telemetry.record( eventBuildCompleted(pagesPaths, { - bundler: getBundlerForTelemetry(bundler), + bundler: getBundlerForTelemetry(isTurbopack), durationInSeconds, totalAppPagesCount, }) @@ -1815,7 +1811,7 @@ export default async function build( telemetry.record( eventBuildCompleted(pagesPaths, { - bundler: getBundlerForTelemetry(bundler), + bundler: getBundlerForTelemetry(isTurbopack), durationInSeconds: compilerDuration, totalAppPagesCount, }) @@ -2433,10 +2429,7 @@ export default async function build( ) // If there's edge routes, append the edge instrumentation hook // Turbopack generates this chunk with a hashed name and references it in middleware-manifest. - if ( - bundler !== Bundler.Turbopack && - (edgeRuntimeAppCount || edgeRuntimePagesCount) - ) { + if (!isTurbopack && (edgeRuntimeAppCount || edgeRuntimePagesCount)) { instrumentationHookEntryFiles.push( path.join( SERVER_DIRECTORY, @@ -2489,7 +2482,7 @@ export default async function build( path.join(SERVER_DIRECTORY, FUNCTIONS_CONFIG_MANIFEST), path.join(SERVER_DIRECTORY, MIDDLEWARE_MANIFEST), path.join(SERVER_DIRECTORY, MIDDLEWARE_BUILD_MANIFEST + '.js'), - ...(bundler !== Bundler.Turbopack + ...(!isTurbopack ? [ path.join( SERVER_DIRECTORY, @@ -2524,7 +2517,7 @@ export default async function build( ), ] : []), - ...(pagesDir && bundler !== Bundler.Turbopack + ...(pagesDir && !isTurbopack ? [ DYNAMIC_CSS_MANIFEST + '.json', path.join(SERVER_DIRECTORY, DYNAMIC_CSS_MANIFEST + '.js'), @@ -2576,7 +2569,7 @@ export default async function build( ], } - if (bundler === Bundler.Turbopack) { + if (isTurbopack) { await writeManifest( path.join( distDir, @@ -2592,11 +2585,7 @@ export default async function build( await writeFunctionsConfigManifest(distDir, functionsConfigManifest) - if ( - bundler !== Bundler.Turbopack && - !isGenerateMode && - !buildTracesPromise - ) { + if (!isTurbopack && !isGenerateMode && !buildTracesPromise) { buildTracesPromise = nextBuildSpan .traceChild('collect-build-traces') .traceAsyncFn(() => { @@ -2737,7 +2726,7 @@ export default async function build( // we don't need to inline for turbopack build as // it will handle it's own caching separate of compile - if (isGenerateMode && bundler !== Bundler.Turbopack) { + if (isGenerateMode && !isTurbopack) { Log.info('Inlining static env ...') await nextBuildSpan @@ -4143,7 +4132,7 @@ export default async function build( if (telemetry) { telemetry.record( eventBuildFailed({ - bundler: getBundlerForTelemetry(bundler), + bundler: getBundlerForTelemetry(isTurbopack), errorCode: getErrorCodeForTelemetry(e), durationInSeconds: Math.floor((Date.now() - buildStartTime) / 1000), }) @@ -4164,7 +4153,7 @@ export default async function build( mode: 'build', projectDir: dir, distDir: loadedConfig.distDir, - isTurboSession: bundler === Bundler.Turbopack, + isTurboSession: isTurbopack, sync: true, }) } @@ -4178,17 +4167,16 @@ function errorFromUnsupportedSegmentConfig(): never { process.exit(1) } -function getBundlerForTelemetry(bundler: Bundler) { - switch (bundler) { - case Bundler.Turbopack: - return 'turbopack' - case Bundler.Rspack: - return 'rspack' - case Bundler.Webpack: - return 'webpack' - default: - throw new Error(`unknown bundler: ${bundler}`) +function getBundlerForTelemetry(isTurbopack: boolean) { + if (isTurbopack) { + return 'turbopack' } + + if (process.env.NEXT_RSPACK) { + return 'rspack' + } + + return 'webpack' } function getErrorCodeForTelemetry(err: unknown) { diff --git a/packages/next/src/cli/next-build.ts b/packages/next/src/cli/next-build.ts index bea9c4900e8e6..a2df58fdc4135 100755 --- a/packages/next/src/cli/next-build.ts +++ b/packages/next/src/cli/next-build.ts @@ -10,7 +10,6 @@ import isError from '../lib/is-error' import { getProjectDir } from '../lib/get-project-dir' import { enableMemoryDebuggingMode } from '../lib/memory/startup' import { disableMemoryDebuggingMode } from '../lib/memory/shutdown' -import { parseBundlerArgs } from '../lib/bundler' export type NextBuildOptions = { debug?: boolean @@ -20,7 +19,6 @@ export type NextBuildOptions = { mangling: boolean turbo?: boolean turbopack?: boolean - webpack?: boolean experimentalDebugMemoryUsage: boolean experimentalAppOnly?: boolean experimentalTurbo?: boolean @@ -84,7 +82,12 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => { printAndExit(`> No such directory exists as the project root: ${dir}`) } - const bundler = parseBundlerArgs(options) + const isTurbopack = Boolean( + options.turbo || options.turbopack || process.env.IS_TURBOPACK_TEST + ) + if (isTurbopack) { + process.env.TURBOPACK = '1' + } return build( dir, @@ -94,7 +97,7 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => { lint, !mangling, experimentalAppOnly, - bundler, + isTurbopack, experimentalBuildMode, traceUploadUrl ) diff --git a/packages/next/src/cli/next-dev.ts b/packages/next/src/cli/next-dev.ts index b4f1e0db71f6d..33e6e51bc6d88 100644 --- a/packages/next/src/cli/next-dev.ts +++ b/packages/next/src/cli/next-dev.ts @@ -38,17 +38,11 @@ import { once } from 'node:events' import { clearTimeout } from 'timers' import { flushAllTraces, trace } from '../trace' import { traceId } from '../trace/shared' -import { - Bundler, - finalizeBundlerFromConfig, - parseBundlerArgs, -} from '../lib/bundler' export type NextDevOptions = { disableSourceMaps: boolean turbo?: boolean turbopack?: boolean - webpack?: boolean port: number hostname?: string experimentalHttps?: boolean @@ -64,11 +58,11 @@ let dir: string let child: undefined | ChildProcess // The config in next-dev is only used to access config.distDir for telemetry and trace. let config: NextConfigComplete -let bundler: Bundler +let isTurboSession = false let traceUploadUrl: string let sessionStopHandled = false -const sessionStarted = Date.now() -const sessionSpan = trace('next-dev') +let sessionStarted = Date.now() +let sessionSpan = trace('next-dev') // How long should we wait for the child to cleanly exit after sending // SIGINT/SIGTERM to the child process before sending SIGKILL? @@ -129,13 +123,11 @@ const handleSessionStop = async (signal: NodeJS.Signals | number | null) => { new Telemetry({ distDir: path.join(dir, config.distDir), }) - // Reading the config can modify environment variables that influence the bundler selection. - bundler = finalizeBundlerFromConfig(bundler) telemetry.record( eventCliSessionStopped({ cliCommand: 'dev', - turboFlag: bundler === Bundler.Turbopack, + turboFlag: isTurboSession, durationMilliseconds: Date.now() - sessionStarted, pagesDir, appDir, @@ -154,7 +146,7 @@ const handleSessionStop = async (signal: NodeJS.Signals | number | null) => { mode: 'dev', projectDir: dir, distDir: config.distDir, - isTurboSession: bundler === Bundler.Turbopack, + isTurboSession, }) } @@ -176,7 +168,14 @@ const nextDev = async ( portSource: PortSource, directory?: string ) => { - bundler = parseBundlerArgs(options) + const isTurbopack = Boolean( + options.turbo || options.turbopack || process.env.IS_TURBOPACK_TEST + ) + if (isTurbopack) { + process.env.TURBOPACK = '1' + } + + isTurboSession = isTurbopack dir = getProjectDir(process.env.NEXT_PRIVATE_DEV_DIR || directory) @@ -289,9 +288,7 @@ const nextDev = async ( stdio: 'inherit', env: { ...defaultEnv, - ...(bundler === Bundler.Turbopack - ? { TURBOPACK: process.env.TURBOPACK } - : undefined), + ...(isTurbopack ? { TURBOPACK: '1' } : undefined), NEXT_PRIVATE_WORKER: '1', NEXT_PRIVATE_TRACE_ID: traceId, NODE_EXTRA_CA_CERTS: startServerOptions.selfSignedCertificate @@ -339,13 +336,12 @@ const nextDev = async ( (await loadConfig(PHASE_DEVELOPMENT_SERVER, dir, { silent: true, })) - bundler = finalizeBundlerFromConfig(bundler) uploadTrace({ traceUploadUrl, mode: 'dev', projectDir: dir, distDir: config.distDir, - isTurboSession: bundler === Bundler.Turbopack, + isTurboSession, sync: true, }) } diff --git a/packages/next/src/lib/bundler.ts b/packages/next/src/lib/bundler.ts deleted file mode 100644 index 38eee0866d2e6..0000000000000 --- a/packages/next/src/lib/bundler.ts +++ /dev/null @@ -1,99 +0,0 @@ -/// Utilties for configuring the bundler to use. -export enum Bundler { - Turbopack, - Webpack, - Rspack, -} -/** - * Parse the bundler arguments and potentially sets the `TURBOPACK` environment variable. - * - * NOTE: rspack is configured via next config which is chaotic so it is possible for this to be overridden later. - * - * @param options The options to parse. - * @returns The bundler that was configured - */ -export function parseBundlerArgs(options: { - turbo?: boolean - turbopack?: boolean - webpack?: boolean -}): Bundler { - const bundlerFlags = new Map() - const setBundlerFlag = (bundler: Bundler, flag: string) => { - bundlerFlags.set(bundler, (bundlerFlags.get(bundler) ?? []).concat(flag)) - } - // What turbo flag was set? We allow multiple to be set, which is silly but not ambiguous, just pick the most relevant one. - if (options.turbopack) { - setBundlerFlag(Bundler.Turbopack, '--turbopack') - } - if (options.turbo) { - setBundlerFlag(Bundler.Turbopack, '--turbo') - } else if (process.env.TURBOPACK) { - // We don't really want to support this but it is trivial and not really confusing. - // If we don't support it and someone sets it, we would have inconsistent behavior - // since some parts of next would read the return value of this function and other - // parts will read the env variable. - setBundlerFlag(Bundler.Turbopack, `TURBOPACK=${process.env.TURBOPACK}`) - } else if (process.env.IS_TURBOPACK_TEST) { - setBundlerFlag( - Bundler.Turbopack, - `IS_TURBOPACK_TEST=${process.env.IS_TURBOPACK_TEST}` - ) - } - if (options.webpack) { - setBundlerFlag(Bundler.Webpack, '--webpack') - } - - if (process.env.IS_WEBPACK_TEST) { - setBundlerFlag( - Bundler.Webpack, - `IS_WEBPACK_TEST=${process.env.IS_WEBPACK_TEST}` - ) - } - - // Mostly this is set via the NextConfig but it can also be set via the command line which is - // common for testing. - if (process.env.NEXT_RSPACK) { - setBundlerFlag(Bundler.Rspack, `NEXT_RSPACK=${process.env.NEXT_RSPACK}`) - } - if (process.env.NEXT_TEST_USE_RSPACK) { - setBundlerFlag( - Bundler.Rspack, - `NEXT_TEST_USE_RSPACK=${process.env.NEXT_TEST_USE_RSPACK}` - ) - } - - if (bundlerFlags.size > 1) { - console.error( - `Multiple bundler flags set: ${Array.from(bundlerFlags.values()).flat().join(', ')}. Configure exactly one bundler.` - ) - process.exit(1) - } - // The default is turbopack when nothing is configured. - if (bundlerFlags.size === 0) { - process.env.TURBOPACK = 'auto' - return Bundler.Turbopack - } - if (bundlerFlags.has(Bundler.Turbopack)) { - // Only conditionally assign to the environment variable, preserving already set values. - // If it was set to 'auto' because no flag was set and this function is called a second time we - // would upgrade to '1' but we don't really want that. - process.env.TURBOPACK ??= '1' - return Bundler.Turbopack - } - // Otherwise it is one of rspack or webpack. At this point there must be exactly one key in the map. - return bundlerFlags.keys().next().value! -} - -/** - * Finalize the bundler based on the config. - * - * Rspack is configured via next config by setting an environment variable (yay, side effects) - * so this should only be called after parsing the config. - */ -export function finalizeBundlerFromConfig(fromOptions: Bundler) { - // Reading the next config can set NEXT_RSPACK environment variables. - if (process.env.NEXT_RSPACK) { - return Bundler.Rspack - } - return fromOptions -} diff --git a/packages/next/src/lib/turbopack-warning.ts b/packages/next/src/lib/turbopack-warning.ts index 4858c0265b3dd..d737c3505a49a 100644 --- a/packages/next/src/lib/turbopack-warning.ts +++ b/packages/next/src/lib/turbopack-warning.ts @@ -1,4 +1,4 @@ -import type { NextConfigComplete } from '../server/config-shared' +import type { NextConfig } from '../server/config-shared' import loadConfig from '../server/config' import * as Log from '../build/output/log' import { @@ -43,9 +43,11 @@ const unsupportedTurbopackNextConfigOptions = [ ] // The following will need to be supported by `next build --turbopack` -const unsupportedProductionSpecificTurbopackNextConfigOptions: string[] = [] +const unsupportedProductionSpecificTurbopackNextConfigOptions: string[] = [ + // TODO: Support disabling sourcemaps, currently they're always enabled. + // 'productionBrowserSourceMaps', +] -/** */ export async function validateTurboNextConfig({ dir, isDev, @@ -65,8 +67,8 @@ export async function validateTurboNextConfig({ let hasWebpackConfig = false let hasTurboConfig = false - const unsupportedConfig: string[] = [] - let rawNextConfig: NextConfigComplete = {} as NextConfigComplete + let unsupportedConfig: string[] = [] + let rawNextConfig: NextConfig = {} const phase = isDev ? PHASE_DEVELOPMENT_SERVER : PHASE_PRODUCTION_BUILD try { @@ -74,7 +76,7 @@ export async function validateTurboNextConfig({ await loadConfig(phase, dir, { rawConfig: true, }) - ) + ) as NextConfig if (typeof rawNextConfig === 'function') { rawNextConfig = (rawNextConfig as any)(phase, { @@ -118,7 +120,7 @@ export async function validateTurboNextConfig({ const customKeys = flattenKeys(rawNextConfig) - const unsupportedKeys = isDev + let unsupportedKeys = isDev ? unsupportedTurbopackNextConfigOptions : [ ...unsupportedTurbopackNextConfigOptions, @@ -133,7 +135,7 @@ export async function validateTurboNextConfig({ hasTurboConfig = true } - const isUnsupported = + let isUnsupported = unsupportedKeys.some( (unsupportedKey) => // Either the key matches (or is a more specific subkey) of @@ -156,24 +158,13 @@ export async function validateTurboNextConfig({ Log.error('Unexpected error occurred while checking config', e) } - // If the build was defaulted to Turbopack, we want to warn about possibly ignored webpack - // configuration. Otherwise the user explicitly picked turbopack and thus we expect that - // they have configured it correctly. - if (process.env.TURBOPACK === 'auto' && hasWebpackConfig && !hasTurboConfig) { - const configFile = rawNextConfig.configFileName ?? 'your Next config file' - Log.error( - `ERROR: This build is using Turbopack, with a \`webpack\` config and no \`turbopack\` config. This may be a mistake. - - As of Next.js 16 turbopack is enabled by default and custom webpack configurations may need to be migrated to Turbopack. - - NOTE: your \`webpack\` config may have been added by a configuration plugin. - - To configure Turbopack, see https://nextjs.org/docs/app/api-reference/next-config-js/turbopack - - TIP: Many applications work fine under Turbopack with no configuration, if that is the case for you, you can silence this error by passing the \`--turbopack\` or \`--webpack\` flag explicitly or simply setting an empty turbopack config in ${configFile} (e.g. \`turbopack: {}\`).` + if (hasWebpackConfig && !hasTurboConfig) { + Log.warn( + `Webpack is configured while Turbopack is not, which may cause problems.` + ) + Log.warn( + `See instructions if you need to configure Turbopack:\n https://nextjs.org/docs/app/api-reference/next-config-js/turbopack\n` ) - - process.exit(1) } if (unsupportedConfig.length) { diff --git a/packages/next/src/server/next.ts b/packages/next/src/server/next.ts index 2ca7d7b9bffb9..88de1cd32a45d 100644 --- a/packages/next/src/server/next.ts +++ b/packages/next/src/server/next.ts @@ -540,8 +540,7 @@ function createServer( options && (options.turbo || options.turbopack || process.env.IS_TURBOPACK_TEST) ) { - // Configure TURBOPACK if it isn't already set - process.env.TURBOPACK ??= '1' + process.env.TURBOPACK = '1' } // The package is used as a TypeScript plugin. if ( diff --git a/scripts/test-new-tests.mjs b/scripts/test-new-tests.mjs index 881ae9dc3b21a..f62b101ea0f9e 100644 --- a/scripts/test-new-tests.mjs +++ b/scripts/test-new-tests.mjs @@ -153,7 +153,6 @@ async function main() { NEXT_EXTERNAL_TESTS_FILTERS, NEXT_TEST_MODE: testMode, NEXT_TEST_VERSION: nextTestVersion, - IS_WEBPACK_TEST: '1', }, }) } @@ -171,7 +170,6 @@ async function main() { NEXT_TEST_VERSION: nextTestVersion, IS_TURBOPACK_TEST: '1', TURBOPACK_BUILD: testMode === 'start' ? '1' : undefined, - TURBOPACK_DEV: testMode === 'dev' ? '1' : undefined, }, }) } diff --git a/test/development/next-config-ts/turbo/index.test.ts b/test/development/next-config-ts/turbo/index.test.ts index 348568ae507ed..f43c75d6176fd 100644 --- a/test/development/next-config-ts/turbo/index.test.ts +++ b/test/development/next-config-ts/turbo/index.test.ts @@ -1,15 +1,13 @@ import { nextTestSetup } from 'e2e-utils' -;(process.env.IS_TURBOPACK_TEST ? describe : describe.skip)( - 'next-config-ts - turbopack', - () => { - const { next } = nextTestSetup({ - files: __dirname, - // explicitly ensure that turbopack is used - startCommand: 'pnpm next dev --turbopack', - }) - it('should work with Turbopack', async () => { - const $ = await next.render$('/') - expect($('p').text()).toBe('foo') - }) - } -) + +describe('next-config-ts - turbopack', () => { + const { next } = nextTestSetup({ + files: __dirname, + // explicitly ensure that turbopack is used + startCommand: 'pnpm next dev --turbo', + }) + it('should work with Turbopack', async () => { + const $ = await next.render$('/') + expect($('p').text()).toBe('foo') + }) +}) diff --git a/test/e2e/config-turbopack/index.test.ts b/test/e2e/config-turbopack/index.test.ts index 64089fd3d0129..36dacfd63a972 100644 --- a/test/e2e/config-turbopack/index.test.ts +++ b/test/e2e/config-turbopack/index.test.ts @@ -1,142 +1,96 @@ /* eslint-disable jest/no-standalone-expect */ import { nextTestSetup } from 'e2e-utils' -const WARNING_MESSAGE = - 'ERROR: This build is using Turbopack, with a `webpack` config and no `turbopack` config. This may be a mistake' +const WARNING_MESSAGE = 'Webpack is configured while Turbopack is not' const itif = (condition: boolean) => (condition ? it : it.skip) -const page = { - 'app/page.js': ` -export default function Page() { - return

hello world

-} -`, -} - -;(process.env.IS_TURBOPACK_TEST ? describe : describe.skip)( - 'config-turbopack', - () => { - describe('when turbopack is auto selected', () => { - describe('when webpack is configured but Turbopack is not', () => { - const { next, isNextDev, isNextStart } = nextTestSetup({ - skipStart: Boolean(process.env.NEXT_TEST_MODE === 'start'), - turbo: false, - env: { - TURBOPACK: 'auto', - }, - files: { - ...page, - 'next.config.js': ` +describe('config-turbopack', () => { + describe('when webpack is configured but Turbopack is not', () => { + const { next, isTurbopack } = nextTestSetup({ + files: { + 'app/page.js': ` + export default function Page() { + return

hello world

+ } + `, + 'next.config.js': ` module.exports = { webpack: (config) => { return config }, } `, - }, - }) - - itif(isNextDev)('warns', async () => { - if (next) - try { - await next.render('/') - } catch (e) { - // we expect an error but this is the only way to get the server to crash - } + }, + }) - expect(next.cliOutput).toContain(WARNING_MESSAGE) - }) - itif(isNextStart)('errors', async () => { - const { exitCode, cliOutput } = await next.build() - expect(exitCode).toBe(1) - expect(cliOutput).toContain(WARNING_MESSAGE) - }) - }) - // no warn cases work when auto selected too - noWarnCases() + itif(isTurbopack)('warns', async () => { + if (next) await next.render('/') + expect(next.cliOutput).toContain(WARNING_MESSAGE) }) + }) - describe('when turbopack is explicitly configured', () => { - describe('when webpack is configured but Turbopack is not', () => { - const { next } = nextTestSetup({ - files: { - ...page, - 'next.config.js': ` - module.exports = { - webpack: (config) => { - return config - }, + describe('when webpack is configured and config.turbopack is set', () => { + const { next, isTurbopack } = nextTestSetup({ + files: { + 'app/page.js': ` + export default function Page() { + return

hello world

+ } + `, + 'next.config.js': ` + module.exports = { + turbopack: { + rules: { + '*.foo': { + loaders: ['foo-loader'] + } } - `, - }, - }) + }, + webpack: (config) => { + return config + }, + } + `, + }, + }) - it('does not warn', async () => { - if (next) await next.render('/') - expect(next.cliOutput).not.toContain(WARNING_MESSAGE) - }) - }) - noWarnCases() + itif(isTurbopack)('does not warn', async () => { + if (next) await next.render('/') + expect(next.cliOutput).not.toContain(WARNING_MESSAGE) }) - /// These other cases don't warn because --turbopack is explicitly selected - function noWarnCases(env?: Record) { - describe('when webpack is configured and config.turbopack is set', () => { - const { next } = nextTestSetup({ - env, - files: { - ...page, - 'next.config.js': ` - module.exports = { - turbopack: { + }) + + describe('when webpack is configured and config.experimental.turbo is set', () => { + const { next, isTurbopack } = nextTestSetup({ + files: { + 'app/page.js': ` + export default function Page() { + return

hello world

+ } + `, + 'next.config.js': ` + module.exports = { + experimental: { + turbo: { rules: { '*.foo': { loaders: ['foo-loader'] } } - }, - webpack: (config) => { - return config - }, - } - `, - }, - }) - - it('does not warn', async () => { - if (next) await next.render('/') - expect(next.cliOutput).not.toContain(WARNING_MESSAGE) - }) - }) - - describe('when webpack is configured and config.experimental.turbo is set', () => { - const { next } = nextTestSetup({ - files: { - ...page, - 'next.config.js': ` - module.exports = { - experimental: { - turbo: { - rules: { - '*.foo': { - loaders: ['foo-loader'] - } - } - } - }, - webpack: (config) => { - return config - }, - } - `, - }, - }) + } + }, + webpack: (config) => { + return config + }, + } + `, + }, + }) - it('does not warn', async () => { - if (next) await next.render('/') - expect(next.cliOutput).not.toContain(WARNING_MESSAGE) - }) - }) - } - } -) + itif(isTurbopack)('does not warn', async () => { + if (next) await next.render('/') + expect(next.cliOutput).not.toContain(WARNING_MESSAGE) + }) + }) +})