diff --git a/packages/next/package.json b/packages/next/package.json index 2069910dfb791..fa68e3db8f2a6 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -188,11 +188,11 @@ "@taskr/clear": "1.1.0", "@taskr/esnext": "1.1.0", "@types/amphtml-validator": "1.0.0", - "@types/babel__code-frame": "7.0.2", - "@types/babel__core": "7.1.12", - "@types/babel__generator": "7.6.2", - "@types/babel__template": "7.4.0", - "@types/babel__traverse": "7.11.0", + "@types/babel__code-frame": "7.0.6", + "@types/babel__core": "7.20.5", + "@types/babel__generator": "7.27.0", + "@types/babel__template": "7.4.4", + "@types/babel__traverse": "7.20.7", "@types/bytes": "3.1.1", "@types/ci-info": "2.0.0", "@types/compression": "0.0.36", diff --git a/packages/next/src/build/babel/loader/get-config.ts b/packages/next/src/build/babel/loader/get-config.ts index 6d5ce1b94d5bb..abaf184c3377f 100644 --- a/packages/next/src/build/babel/loader/get-config.ts +++ b/packages/next/src/build/babel/loader/get-config.ts @@ -2,14 +2,31 @@ import { readFileSync } from 'fs' import JSON5 from 'next/dist/compiled/json5' import { createConfigItem, loadOptions } from 'next/dist/compiled/babel/core' -import loadConfig from 'next/dist/compiled/babel/core-lib-config' +import loadFullConfig from 'next/dist/compiled/babel/core-lib-config' import type { NextBabelLoaderOptions, NextJsLoaderContext } from './types' -import { consumeIterator } from './util' +import { + consumeIterator, + type SourceMap, + type BabelLoaderTransformOptions, +} from './util' import * as Log from '../../output/log' import jsx from 'next/dist/compiled/babel/plugin-syntax-jsx' import { isReactCompilerRequired } from '../../swc' +/** + * An internal (non-exported) type used by babel. + */ +export type ResolvedBabelConfig = { + options: BabelLoaderTransformOptions + passes: BabelPluginPasses + externalDependencies: ReadonlyArray +} + +export type BabelPlugin = unknown +export type BabelPluginPassList = ReadonlyArray +export type BabelPluginPasses = ReadonlyArray + const nextDistPath = /(next[\\/]dist[\\/]shared[\\/]lib)|(next[\\/]dist[\\/]client)|(next[\\/]dist[\\/]pages)/ @@ -275,13 +292,13 @@ function checkCustomBabelConfigDeprecation( * This config should have no unresolved overrides, presets, etc. */ async function getFreshConfig( - this: NextJsLoaderContext, + ctx: NextJsLoaderContext, cacheCharacteristics: CharacteristicsGermaneToCaching, loaderOptions: NextBabelLoaderOptions, target: string, filename: string, - inputSourceMap?: object | null -) { + inputSourceMap?: SourceMap +): Promise { const hasReactCompiler = await (async () => { if ( loaderOptions.reactCompilerPlugins && @@ -314,18 +331,18 @@ async function getFreshConfig( let { isServer, pagesDir, srcDir, development } = loaderOptions - let options = { + let options: BabelLoaderTransformOptions = { babelrc: false, cloneInputAst: false, filename, - inputSourceMap: inputSourceMap || undefined, + inputSourceMap, // Ensure that Webpack will get a full absolute path in the sourcemap // so that it can properly map the module back to its internal cached // modules. sourceFileName: filename, - sourceMaps: this.sourceMap, - } as any + sourceMaps: ctx.sourceMap, + } const baseCaller = { name: 'next-babel-turbo-loader', @@ -334,7 +351,7 @@ async function getFreshConfig( // Provide plugins with insight into webpack target. // https://github.com/babel/babel-loader/issues/787 - target: target, + target, // Webpack 5 supports TLA behind a flag. We enable it by default // for Babel, and then webpack will throw an error if the experimental @@ -374,7 +391,7 @@ async function getFreshConfig( // but allow users to override if they want. options.sourceMaps = loaderOptions.sourceMaps === undefined - ? this.sourceMap + ? ctx.sourceMap : loaderOptions.sourceMaps options.plugins = [ @@ -424,12 +441,12 @@ async function getFreshConfig( if (!(reason instanceof Error)) { reason = new Error(reason) } - this.emitWarning(reason) + ctx.emitWarning(reason) }, }) const loadedOptions = loadOptions(options) - const config = consumeIterator(loadConfig(loadedOptions)) + const config = consumeIterator(loadFullConfig(loadedOptions)) return config } @@ -453,12 +470,11 @@ function getCacheKey(cacheCharacteristics: CharacteristicsGermaneToCaching) { return fileNameOrExt + flags } -type BabelConfig = any -const configCache: Map = new Map() +const configCache: Map = new Map() const configFiles: Set = new Set() export default async function getConfig( - this: NextJsLoaderContext, + ctx: NextJsLoaderContext, { source, target, @@ -470,9 +486,9 @@ export default async function getConfig( loaderOptions: NextBabelLoaderOptions target: string filename: string - inputSourceMap?: object | null + inputSourceMap?: SourceMap | undefined } -): Promise { +): Promise { const cacheCharacteristics = getCacheCharacteristics( loaderOptions, source, @@ -482,7 +498,7 @@ export default async function getConfig( if (loaderOptions.transformMode === 'default' && loaderOptions.configFile) { // Ensures webpack invalidates the cache for this loader when the config file changes - this.addDependency(loaderOptions.configFile) + ctx.addDependency(loaderOptions.configFile) } const cacheKey = getCacheKey(cacheCharacteristics) @@ -515,8 +531,8 @@ export default async function getConfig( ) } - const freshConfig = await getFreshConfig.call( - this, + const freshConfig = await getFreshConfig( + ctx, cacheCharacteristics, loaderOptions, target, diff --git a/packages/next/src/build/babel/loader/index.ts b/packages/next/src/build/babel/loader/index.ts index 99c42e991006a..68f8733cb7d83 100644 --- a/packages/next/src/build/babel/loader/index.ts +++ b/packages/next/src/build/babel/loader/index.ts @@ -1,25 +1,27 @@ import type { Span } from '../../../trace' import transform from './transform' import type { NextJsLoaderContext } from './types' +import type { SourceMap } from './util' +import type { webpack } from 'next/dist/compiled/webpack/webpack' async function nextBabelLoader( - this: NextJsLoaderContext, + ctx: NextJsLoaderContext, parentTrace: Span, inputSource: string, - inputSourceMap: object | null | undefined -) { - const filename = this.resourcePath + inputSourceMap: SourceMap | null | undefined +): Promise<[string, SourceMap | null | undefined]> { + const filename = ctx.resourcePath // Ensure `.d.ts` are not processed. if (filename.endsWith('.d.ts')) { return [inputSource, inputSourceMap] } - const target = this.target + const target = ctx.target const loaderOptions: any = parentTrace .traceChild('get-options') // @ts-ignore TODO: remove ignore once webpack 5 types are used - .traceFn(() => this.getOptions()) + .traceFn(() => ctx.getOptions()) if (loaderOptions.exclude && loaderOptions.exclude(filename)) { return [inputSource, inputSourceMap] @@ -29,8 +31,8 @@ async function nextBabelLoader( const { code: transformedSource, map: outputSourceMap } = await loaderSpanInner.traceAsyncFn( async () => - await transform.call( - this, + await transform( + ctx, inputSource, inputSourceMap, loaderOptions, @@ -43,25 +45,38 @@ async function nextBabelLoader( return [transformedSource, outputSourceMap] } -const nextBabelLoaderOuter = function nextBabelLoaderOuter( +function nextBabelLoaderOuter( this: NextJsLoaderContext, inputSource: string, - inputSourceMap: object | null | undefined + // webpack's source map format is compatible with babel, but the type signature doesn't match + inputSourceMap?: any ) { const callback = this.async() const loaderSpan = this.currentTraceSpan.traceChild('next-babel-turbo-loader') loaderSpan .traceAsyncFn(() => - nextBabelLoader.call(this, loaderSpan, inputSource, inputSourceMap) + nextBabelLoader(this, loaderSpan, inputSource, inputSourceMap) ) .then( - ([transformedSource, outputSourceMap]: any) => - callback?.(null, transformedSource, outputSourceMap || inputSourceMap), + ([transformedSource, outputSourceMap]) => + callback?.( + /* err */ null, + transformedSource, + outputSourceMap ?? inputSourceMap + ), (err) => { callback?.(err) } ) } +// check this type matches `webpack.LoaderDefinitionFunction`, but be careful +// not to publicly rely on the webpack type since the generated typescript +// declarations will be wrong. +const _nextBabelLoaderOuter: webpack.LoaderDefinitionFunction< + {}, + NextJsLoaderContext +> = nextBabelLoaderOuter + export default nextBabelLoaderOuter diff --git a/packages/next/src/build/babel/loader/transform.ts b/packages/next/src/build/babel/loader/transform.ts index 71cf68bd06872..91f1adb1b294c 100644 --- a/packages/next/src/build/babel/loader/transform.ts +++ b/packages/next/src/build/babel/loader/transform.ts @@ -3,7 +3,9 @@ */ import traverse from 'next/dist/compiled/babel/traverse' -import generate from 'next/dist/compiled/babel/generator' +import generate, { + type GeneratorResult, +} from 'next/dist/compiled/babel/generator' import normalizeFile from 'next/dist/compiled/babel/core-lib-normalize-file' import normalizeOpts from 'next/dist/compiled/babel/core-lib-normalize-opts' import loadBlockHoistPlugin from 'next/dist/compiled/babel/core-lib-block-hoist-plugin' @@ -13,6 +15,7 @@ import getConfig from './get-config' import { consumeIterator } from './util' import type { Span } from '../../../trace' import type { NextJsLoaderContext } from './types' +import type { SourceMap } from './util' function getTraversalParams(file: any, pluginPairs: any[]) { const passPairs = [] @@ -70,24 +73,25 @@ function transformAst(file: any, babelConfig: any, parentSpan: Span) { } export default async function transform( - this: NextJsLoaderContext, + ctx: NextJsLoaderContext, source: string, - inputSourceMap: object | null | undefined, + inputSourceMap: SourceMap | null | undefined, loaderOptions: any, filename: string, target: string, parentSpan: Span -) { +): Promise { const getConfigSpan = parentSpan.traceChild('babel-turbo-get-config') - const babelConfig = await getConfig.call(this, { + + const babelConfig = await getConfig(ctx, { source, loaderOptions, - inputSourceMap, + inputSourceMap: inputSourceMap ?? undefined, target, filename, }) if (!babelConfig) { - return { code: source, map: inputSourceMap } + return { code: source, map: inputSourceMap ?? null } } getConfigSpan.stop() diff --git a/packages/next/src/build/babel/loader/types.d.ts b/packages/next/src/build/babel/loader/types.d.ts index 812157a2f9795..ae10480d50f82 100644 --- a/packages/next/src/build/babel/loader/types.d.ts +++ b/packages/next/src/build/babel/loader/types.d.ts @@ -30,7 +30,7 @@ export type NextBabelLoaderOptionDefaultPresets = NextBabelLoaderBaseOptions & { transformMode: 'default' hasJsxRuntime: boolean hasReactRefresh: boolean - sourceMaps?: any[] + sourceMaps?: boolean | 'inline' | 'both' | null | undefined overrides: any configFile: string | undefined } diff --git a/packages/next/src/build/babel/loader/util.ts b/packages/next/src/build/babel/loader/util.ts index 02d15f3eebf04..5921e27bc908d 100644 --- a/packages/next/src/build/babel/loader/util.ts +++ b/packages/next/src/build/babel/loader/util.ts @@ -1,3 +1,5 @@ +import type { TransformOptions } from 'next/dist/compiled/babel/core' + export function consumeIterator(iter: Iterator) { while (true) { const { value, done } = iter.next() @@ -6,3 +8,19 @@ export function consumeIterator(iter: Iterator) { } } } + +/** + * Source map standard format as to revision 3. + * + * `TransformOptions` uses this type, but doesn't export it separately + */ +export type SourceMap = NonNullable + +/** + * An extension of the normal babel configuration, with extra `babel-loader`-specific fields that transforms can read. + * + * See: https://github.com/babel/babel-loader/blob/main/src/injectCaller.js + */ +export type BabelLoaderTransformOptions = TransformOptions & { + target?: string +} diff --git a/packages/next/src/build/babel/plugins/jsx-pragma.ts b/packages/next/src/build/babel/plugins/jsx-pragma.ts index 6e99011e2324c..2f8e1e74c6d31 100644 --- a/packages/next/src/build/babel/plugins/jsx-pragma.ts +++ b/packages/next/src/build/babel/plugins/jsx-pragma.ts @@ -70,12 +70,7 @@ export default function ({ ;[newPath] = path.unshiftContainer('body', mapping) } - for (const declar of newPath.get('declarations')) { - path.scope.registerBinding( - newPath.node.kind, - declar as NodePath - ) - } + path.scope.registerDeclaration(newPath) } if (!existingBinding) { diff --git a/packages/next/src/build/babel/plugins/react-loadable-plugin.ts b/packages/next/src/build/babel/plugins/react-loadable-plugin.ts index 8a5d2e5814db8..094a41f3056b3 100644 --- a/packages/next/src/build/babel/plugins/react-loadable-plugin.ts +++ b/packages/next/src/build/babel/plugins/react-loadable-plugin.ts @@ -62,7 +62,7 @@ export default function ({ let callExpression = refPath.parentPath if ( - callExpression.isMemberExpression() && + callExpression?.isMemberExpression() && callExpression.node.computed === false ) { const property = callExpression.get('property') @@ -74,14 +74,11 @@ export default function ({ } } - if (!callExpression.isCallExpression()) return + if (!callExpression?.isCallExpression()) return - const callExpression_ = - callExpression as NodePath - - let args = callExpression_.get('arguments') + let args = callExpression.get('arguments') if (args.length > 2) { - throw callExpression_.buildCodeFrameError( + throw callExpression.buildCodeFrameError( 'next/dynamic only accepts 2 arguments' ) } @@ -97,10 +94,10 @@ export default function ({ options = args[0] } else { if (!args[1]) { - callExpression_.node.arguments.push(t.objectExpression([])) + callExpression.node.arguments.push(t.objectExpression([])) } // This is needed as the code is modified above - args = callExpression_.get('arguments') + args = callExpression.get('arguments') loader = args[0] options = args[1] } diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index e626813cc39ae..28e1fcb32e294 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -814,23 +814,21 @@ function bindingToApi( if (reactCompilerOptions) { const ruleKeys = ['*.ts', '*.js', '*.jsx', '*.tsx'] if ( - Object.keys(nextConfig?.turbopack?.rules ?? []).some((key) => + Object.keys(nextConfig?.turbopack?.rules ?? {}).some((key) => ruleKeys.includes(key) ) ) { Log.warn( - `The React Compiler cannot be enabled automatically because 'turbopack.rules' contains a rule for '*.ts', '*.js', '*.jsx', and '*.tsx'. Remove this rule, or add 'babel-loader' and 'babel-plugin-react-compiler' to the Turbopack configuration manually.` + "The React Compiler cannot be enabled automatically because 'turbopack.rules' contains " + + "a rule for '*.ts', '*.js', '*.jsx', and '*.tsx'. Remove this rule, or add " + + "'babel-loader' and 'babel-plugin-react-compiler' to the Turbopack configuration " + + 'manually.' ) } else { - if (!nextConfig.turbopack) { - nextConfig.turbopack = {} - } - - if (!nextConfig.turbopack.rules) { - nextConfig.turbopack.rules = {} - } + nextConfig.turbopack ??= {} + nextConfig.turbopack.rules ??= {} - for (const key of ['*.ts', '*.js', '*.jsx', '*.tsx']) { + for (const key of ruleKeys) { nextConfig.turbopack.rules[key] = { browser: { foreign: false, @@ -839,8 +837,8 @@ function bindingToApi( originalNextConfig.experimental.reactCompiler, projectPath, nextConfig.dev, - false, - undefined + /* isServer */ false, + /* reactCompilerExclude */ undefined ), ], }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dee6f8db9cbc5..73e12a60ced7d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1115,20 +1115,20 @@ importers: specifier: 1.0.0 version: 1.0.0 '@types/babel__code-frame': - specifier: 7.0.2 - version: 7.0.2 + specifier: 7.0.6 + version: 7.0.6 '@types/babel__core': - specifier: 7.1.12 - version: 7.1.12 + specifier: 7.20.5 + version: 7.20.5 '@types/babel__generator': - specifier: 7.6.2 - version: 7.6.2 + specifier: 7.27.0 + version: 7.27.0 '@types/babel__template': - specifier: 7.4.0 - version: 7.4.0 + specifier: 7.4.4 + version: 7.4.4 '@types/babel__traverse': - specifier: 7.11.0 - version: 7.11.0 + specifier: 7.20.7 + version: 7.20.7 '@types/bytes': specifier: 3.1.1 version: 3.1.1 @@ -5475,29 +5475,20 @@ packages: '@types/aws-lambda@8.10.119': resolution: {integrity: sha512-Vqm22aZrCvCd6I5g1SvpW151jfqwTzEZ7XJ3yZ6xaZG31nUEOEyzzVImjRcsN8Wi/QyPxId/x8GTtgIbsy8kEw==} - '@types/babel__code-frame@7.0.2': - resolution: {integrity: sha512-imO+jT/yjOKOAS5GQZ8SDtwiIloAGGr6OaZDKB0V5JVaSfGZLat5K5/ZRtyKW6R60XHV3RHYPTFfhYb+wDKyKg==} - - '@types/babel__core@7.1.12': - resolution: {integrity: sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==} + '@types/babel__code-frame@7.0.6': + resolution: {integrity: sha512-Anitqkl3+KrzcW2k77lRlg/GfLZLWXBuNgbEcIOU6M92yw42vsd3xV/Z/yAHEj8m+KUjL6bWOVOFqX8PFPJ4LA==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@types/babel__generator@7.6.2': - resolution: {integrity: sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==} + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - '@types/babel__template@7.4.0': - resolution: {integrity: sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==} + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.11.0': - resolution: {integrity: sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==} - - '@types/babel__traverse@7.11.1': - resolution: {integrity: sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==} - - '@types/babel__traverse@7.20.6': - resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/babel__traverse@7.20.7': + resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} '@types/body-parser@1.17.1': resolution: {integrity: sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==} @@ -21422,42 +21413,26 @@ snapshots: '@types/aws-lambda@8.10.119': {} - '@types/babel__code-frame@7.0.2': {} - - '@types/babel__core@7.1.12': - dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 - '@types/babel__generator': 7.6.2 - '@types/babel__template': 7.4.0 - '@types/babel__traverse': 7.11.1 + '@types/babel__code-frame@7.0.6': {} '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.27.0 '@babel/types': 7.27.0 - '@types/babel__generator': 7.6.2 - '@types/babel__template': 7.4.0 - '@types/babel__traverse': 7.11.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.7 - '@types/babel__generator@7.6.2': + '@types/babel__generator@7.27.0': dependencies: '@babel/types': 7.27.0 - '@types/babel__template@7.4.0': + '@types/babel__template@7.4.4': dependencies: '@babel/parser': 7.27.0 '@babel/types': 7.27.0 - '@types/babel__traverse@7.11.0': - dependencies: - '@babel/types': 7.27.0 - - '@types/babel__traverse@7.11.1': - dependencies: - '@babel/types': 7.27.0 - - '@types/babel__traverse@7.20.6': + '@types/babel__traverse@7.20.7': dependencies: '@babel/types': 7.27.0 @@ -22911,7 +22886,7 @@ snapshots: '@babel/template': 7.27.0 '@babel/types': 7.27.0 '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.11.0 + '@types/babel__traverse': 7.20.7 babel-plugin-macros@3.0.1: dependencies: @@ -32408,7 +32383,7 @@ snapshots: '@babel/traverse': 7.27.0 '@babel/types': 7.27.0 '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.20.6 + '@types/babel__traverse': 7.20.7 '@types/doctrine': 0.0.9 '@types/resolve': 1.20.6 doctrine: 3.0.0