next.js/packages/next/src/lib/bundler.ts
bundler.ts102 lines3.5 KB
/// 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<Bundler, string[]>()
  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(', ')}.

Edit your command or your package.json script to configure only 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
}
Quest for Codev2.0.0
/
SIGN IN