Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/next/src/build/analysis/get-page-static-info.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { NextConfig } from '../../server/config-shared'
import type { RouteHas } from '../../lib/load-custom-routes'

import { promises as fs } from 'fs'
import { readFileSync } from 'fs'
import { relative } from 'path'
import { LRUCache } from '../../server/lib/lru-cache'
import {
Expand Down Expand Up @@ -427,9 +427,9 @@ function validateMiddlewareProxyExports({
}
}

async function tryToReadFile(filePath: string, shouldThrow: boolean) {
function tryToReadFile(filePath: string, shouldThrow: boolean) {
try {
return await fs.readFile(filePath, {
return readFileSync(filePath, {
encoding: 'utf8',
})
} catch (error: any) {
Expand Down Expand Up @@ -615,7 +615,7 @@ export async function getAppPageStaticInfo({
isDev,
page,
}: GetPageStaticInfoParams): Promise<AppPageStaticInfo> {
const content = await tryToReadFile(pageFilePath, !isDev)
const content = tryToReadFile(pageFilePath, !isDev)
if (!content || !PARSE_PATTERN.test(content)) {
return {
type: PAGE_TYPES.APP,
Expand Down Expand Up @@ -711,7 +711,7 @@ export async function getPagesPageStaticInfo({
isDev,
page,
}: GetPageStaticInfoParams): Promise<PagesPageStaticInfo> {
const content = await tryToReadFile(pageFilePath, !isDev)
const content = tryToReadFile(pageFilePath, !isDev)
if (!content || !PARSE_PATTERN.test(content)) {
return {
type: PAGE_TYPES.PAGES,
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/build/create-compiler-aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type { NextConfigComplete } from '../server/config-shared'
import { defaultOverrides } from '../server/require-hook'
import { hasExternalOtelApiPackage } from './webpack-config'
import { NEXT_PROJECT_ROOT } from './next-dir-paths'
import { shouldUseReactServerCondition } from './utils'
import { shouldUseReactServerCondition } from './webpack-layer'

interface CompilerAliases {
[alias: string]: string | string[]
Expand Down
27 changes: 27 additions & 0 deletions packages/next/src/build/get-supported-browsers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import browserslist from 'next/dist/compiled/browserslist'
import { MODERN_BROWSERSLIST_TARGET } from '../shared/lib/constants'

export function getSupportedBrowsers(
dir: string,
isDevelopment: boolean
): string[] {
let browsers: any
try {
const browsersListConfig = browserslist.loadConfig({
path: dir,
env: isDevelopment ? 'development' : 'production',
})
// Running `browserslist` resolves `extends` and other config features into a list of browsers
if (browsersListConfig && browsersListConfig.length > 0) {
browsers = browserslist(browsersListConfig)
}
} catch {}

// When user has browserslist use that target
if (browsers && browsers.length > 0) {
return browsers
}

// Uses modern browsers as the default.
return MODERN_BROWSERSLIST_TARGET
}
3 changes: 2 additions & 1 deletion packages/next/src/build/handle-externals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
NODE_ESM_RESOLVE_OPTIONS,
NODE_RESOLVE_OPTIONS,
} from './webpack-config'
import { isWebpackBundledLayer, shouldUseReactServerCondition } from './utils'
import { isWebpackBundledLayer } from './utils'
import { shouldUseReactServerCondition } from './webpack-layer'
import { normalizePathSep } from '../shared/lib/page-path/normalize-path-sep'
const reactPackagesRegex = /^(react|react-dom|react-server-dom-webpack)($|\/)/

Expand Down
5 changes: 3 additions & 2 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ import { HTML_LIMITED_BOT_UA_RE_STRING } from '../shared/lib/router/utils/is-bot
import type { UseCacheTrackerKey } from './webpack/plugins/telemetry-plugin/use-cache-tracker-utils'

import { turbopackBuild } from './turbopack-build'
import { isFileSystemCacheEnabledForBuild } from '../shared/lib/turbopack/utils'
import { inlineStaticEnv } from '../lib/inline-static-env'
import { populateStaticEnv } from '../lib/static-env'
import { durationToString, hrtimeDurationToString } from './duration-to-string'
Expand Down Expand Up @@ -2696,7 +2695,9 @@ export default async function build(
},
{
featureName: 'turbopackFileSystemCache',
invocationCount: isFileSystemCacheEnabledForBuild(config) ? 1 : 0,
invocationCount: config.experimental?.turbopackFileSystemCacheForBuild
? 1
: 0,
},
]
telemetry.record(
Expand Down
86 changes: 86 additions & 0 deletions packages/next/src/build/print-build-errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { formatIssue, isRelevantWarning } from '../shared/lib/turbopack/utils'
import type { TurbopackResult } from './swc/types'

/**
* Processes and reports build issues from Turbopack entrypoints.
*
* @param entrypoints - The result object containing build issues to process.
* @param isDev - A flag indicating if the build is running in development mode.
* @return This function does not return a value but logs or throws errors based on the issues.
* @throws {Error} If a fatal issue is encountered, this function throws an error. In development mode, we only throw on
* 'fatal' and 'bug' issues. In production mode, we also throw on 'error' issues.
*/
export function printBuildErrors(
entrypoints: TurbopackResult,
isDev: boolean
): void {
// Issues that we want to stop the server from executing
const topLevelFatalIssues = []
// Issues that are true errors, but we believe we can keep running and allow the user to address the issue
const topLevelErrors = []
// Issues that are warnings but should not affect the running of the build
const topLevelWarnings = []

// Track seen formatted error messages to avoid duplicates
const seenFatalIssues = new Set<string>()
const seenErrors = new Set<string>()
const seenWarnings = new Set<string>()

for (const issue of entrypoints.issues) {
// We only want to completely shut down the server
if (issue.severity === 'fatal' || issue.severity === 'bug') {
const formatted = formatIssue(issue)
if (!seenFatalIssues.has(formatted)) {
seenFatalIssues.add(formatted)
topLevelFatalIssues.push(formatted)
}
} else if (isRelevantWarning(issue)) {
const formatted = formatIssue(issue)
if (!seenWarnings.has(formatted)) {
seenWarnings.add(formatted)
topLevelWarnings.push(formatted)
}
} else if (issue.severity === 'error') {
const formatted = formatIssue(issue)
if (isDev) {
// We want to treat errors as recoverable in development
// so that we can show the errors in the site and allow users
// to respond to the errors when necessary. In production builds
// though we want to error out and stop the build process.
if (!seenErrors.has(formatted)) {
seenErrors.add(formatted)
topLevelErrors.push(formatted)
}
} else {
if (!seenFatalIssues.has(formatted)) {
seenFatalIssues.add(formatted)
topLevelFatalIssues.push(formatted)
}
}
}
}
// TODO: print in order by source location so issues from the same file are displayed together and then add a summary at the end about the number of warnings/errors
if (topLevelWarnings.length > 0) {
console.warn(
`Turbopack build encountered ${
topLevelWarnings.length
} warnings:\n${topLevelWarnings.join('\n')}`
)
}

if (topLevelErrors.length > 0) {
console.error(
`Turbopack build encountered ${
topLevelErrors.length
} errors:\n${topLevelErrors.join('\n')}`
)
}

if (topLevelFatalIssues.length > 0) {
throw new Error(
`Turbopack build failed with ${
topLevelFatalIssues.length
} errors:\n${topLevelFatalIssues.join('\n')}`
)
}
}
49 changes: 29 additions & 20 deletions packages/next/src/build/swc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ import { pathToFileURL } from 'url'
import { arch, platform } from 'os'
import { platformArchTriples } from 'next/dist/compiled/@napi-rs/triples'
import * as Log from '../output/log'
import { getParserOptions } from './options'
import { eventSwcLoadFailure } from '../../telemetry/events/swc-load-failure'
import { patchIncorrectLockfile } from '../../lib/patch-incorrect-lockfile'
import { downloadNativeNextSwc, downloadWasmSwc } from '../../lib/download-swc'
import type {
NextConfigComplete,
TurbopackLoaderBuiltinCondition,
Expand All @@ -15,7 +11,6 @@ import type {
TurbopackRuleConfigCollection,
TurbopackRuleConfigItem,
} from '../../server/config-shared'
import { isDeepStrictEqual } from 'util'
import { type DefineEnvOptions, getDefineEnv } from '../define-env'
import type {
NapiPartialProjectOptions,
Expand All @@ -42,8 +37,6 @@ import type {
UpdateMessage,
WrittenEndpoint,
} from './types'
import { throwTurbopackInternalError } from '../../shared/lib/turbopack/internal-error'

export enum HmrTarget {
Client = 'client',
Server = 'server',
Expand Down Expand Up @@ -235,9 +228,11 @@ export async function loadBindings(
if (!lockfilePatchPromise.cur) {
// always run lockfile check once so that it gets patched
// even if it doesn't fail to load locally
lockfilePatchPromise.cur = patchIncorrectLockfile(process.cwd()).catch(
console.error
lockfilePatchPromise.cur = (
require('../../lib/patch-incorrect-lockfile') as typeof import('../../lib/patch-incorrect-lockfile')
)
.patchIncorrectLockfile(process.cwd())
.catch(console.error)
}

let attempts: any[] = []
Expand Down Expand Up @@ -321,7 +316,9 @@ async function tryLoadNativeWithFallback(attempts: Array<string>) {
)

if (!downloadNativeBindingsPromise) {
downloadNativeBindingsPromise = downloadNativeNextSwc(
downloadNativeBindingsPromise = (
require('../../lib/download-swc') as typeof import('../../lib/download-swc')
).downloadNativeNextSwc(
nextVersion,
nativeBindingsDirectory,
triples.map((triple: any) => triple.platformArchABI)
Expand All @@ -344,8 +341,9 @@ async function tryLoadWasmWithFallback(
): Promise<Binding | undefined> {
try {
let bindings = await loadWasm('')
// @ts-expect-error TODO: this event has a wrong type.
eventSwcLoadFailure({
;(
require('../../telemetry/events/swc-load-failure') as typeof import('../../telemetry/events/swc-load-failure')
).eventSwcLoadFailure({
wasm: 'enabled',
nativeBindingsErrorCode: lastNativeBindingsLoadErrorCode,
})
Expand All @@ -364,12 +362,15 @@ async function tryLoadWasmWithFallback(
'wasm'
)
if (!downloadWasmPromise) {
downloadWasmPromise = downloadWasmSwc(nextVersion, wasmDirectory)
downloadWasmPromise = (
require('../../lib/download-swc') as typeof import('../../lib/download-swc')
).downloadWasmSwc(nextVersion, wasmDirectory)
}
await downloadWasmPromise
let bindings = await loadWasm(wasmDirectory)
// @ts-expect-error TODO: this event has a wrong type.
eventSwcLoadFailure({
;(
require('../../telemetry/events/swc-load-failure') as typeof import('../../telemetry/events/swc-load-failure')
).eventSwcLoadFailure({
wasm: 'fallback',
nativeBindingsErrorCode: lastNativeBindingsLoadErrorCode,
})
Expand Down Expand Up @@ -417,8 +418,9 @@ async function logLoadFailure(attempts: any, triedWasm = false) {
Log.warn(attempt)
}

// @ts-expect-error TODO: this event has a wrong type.
await eventSwcLoadFailure({
await (
require('../../telemetry/events/swc-load-failure') as typeof import('../../telemetry/events/swc-load-failure')
).eventSwcLoadFailure({
wasm: triedWasm ? 'failed' : undefined,
nativeBindingsErrorCode: lastNativeBindingsLoadErrorCode,
})
Expand Down Expand Up @@ -1124,7 +1126,10 @@ function bindingToApi(
function checkLoaderItem(loaderItem: TurbopackLoaderItem, glob: string) {
if (
typeof loaderItem !== 'string' &&
!isDeepStrictEqual(loaderItem, JSON.parse(JSON.stringify(loaderItem)))
!(require('util') as typeof import('util')).isDeepStrictEqual(
loaderItem,
JSON.parse(JSON.stringify(loaderItem))
)
) {
throw new Error(
`loader ${loaderItem.loader} for match "${glob}" does not have serializable options. ` +
Expand Down Expand Up @@ -1228,7 +1233,9 @@ function bindingToApi(
await rustifyProjectOptions(options),
turboEngineOptions || {},
{
throwTurbopackInternalError,
throwTurbopackInternalError: (
require('../../shared/lib/turbopack/internal-error') as typeof import('../../shared/lib/turbopack/internal-error')
).throwTurbopackInternalError,
onBeforeDeferredEntries: callbacks?.onBeforeDeferredEntries,
}
)
Expand Down Expand Up @@ -1704,7 +1711,9 @@ export function isReactCompilerRequired(filename: string): Promise<boolean> {

export async function parse(src: string, options: any): Promise<any> {
const bindings = getBindingsSync()
const parserOptions = getParserOptions(options)
const parserOptions = (
require('./options') as typeof import('./options')
).getParserOptions(options)
const parsed = await bindings.parse(src, parserOptions)
return JSON.parse(parsed)
}
Expand Down
5 changes: 4 additions & 1 deletion packages/next/src/build/swc/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import type {
StyledComponentsConfig,
} from '../../server/config-shared'
import type { ResolvedBaseUrl } from '../load-jsconfig'
import { shouldUseReactServerCondition, isWebpackAppPagesLayer } from '../utils'
import {
shouldUseReactServerCondition,
isWebpackAppPagesLayer,
} from '../webpack-layer'
import { escapeStringRegexp } from '../../shared/lib/escape-regexp'

const nextDirname = path.dirname(require.resolve('next/package.json'))
Expand Down
6 changes: 3 additions & 3 deletions packages/next/src/build/turbopack-analyze/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import type { __ApiPreviewProps } from '../../server/api-utils'

import path from 'path'
import { validateTurboNextConfig } from '../../lib/turbopack-warning'
import { isFileSystemCacheEnabledForBuild } from '../../shared/lib/turbopack/utils'
import { createDefineEnv, loadBindings } from '../swc'
import { isCI } from '../../server/ci-info'
import { backgroundLogCompilationEvents } from '../../shared/lib/turbopack/compilation-events'
import { getSupportedBrowsers } from '../utils'
import { getSupportedBrowsers } from '../get-supported-browsers'
import { normalizePath } from '../../lib/normalize-path'
import { PHASE_PRODUCTION_BUILD } from '../../shared/lib/constants'

Expand Down Expand Up @@ -48,7 +47,8 @@ export async function turbopackAnalyze(

const supportedBrowsers = getSupportedBrowsers(dir, dev)

const persistentCaching = isFileSystemCacheEnabledForBuild(config)
const persistentCaching =
config.experimental?.turbopackFileSystemCacheForBuild || false
const rootPath = config.turbopack?.root || config.outputFileTracingRoot || dir
const project = await bindings.turbo.createProject(
{
Expand Down
7 changes: 4 additions & 3 deletions packages/next/src/build/turbopack-build/impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import { saveCpuProfile } from '../../server/lib/cpu-profile'
import path from 'path'
import { validateTurboNextConfig } from '../../lib/turbopack-warning'
import { isFileSystemCacheEnabledForBuild } from '../../shared/lib/turbopack/utils'
import { NextBuildContext } from '../build-context'
import { createDefineEnv, getBindingsSync } from '../swc'
import { installBindings } from '../swc/install-bindings'
Expand All @@ -19,7 +18,8 @@ import { Telemetry } from '../../telemetry/storage'
import { setGlobal } from '../../trace'
import { isCI } from '../../server/ci-info'
import { backgroundLogCompilationEvents } from '../../shared/lib/turbopack/compilation-events'
import { getSupportedBrowsers, printBuildErrors } from '../utils'
import { getSupportedBrowsers } from '../get-supported-browsers'
import { printBuildErrors } from '../print-build-errors'
import { normalizePath } from '../../lib/normalize-path'
import type {
ProjectOptions,
Expand Down Expand Up @@ -68,7 +68,8 @@ export async function turbopackBuild(): Promise<{
const hasDeferredEntries =
(config.experimental.deferredEntries?.length ?? 0) > 0

const persistentCaching = isFileSystemCacheEnabledForBuild(config)
const persistentCaching =
config.experimental?.turbopackFileSystemCacheForBuild || false
const rootPath = config.turbopack?.root || config.outputFileTracingRoot || dir

// Shared options for createProject calls
Expand Down
Loading
Loading