diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
index 799678988275..9fef923b17a4 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -1013,134 +1013,6 @@ jobs:
stepName: 'test-cache-components-prod-${{ matrix.group }}'
secrets: inherit
- test-node-streams-cache-components-dev:
- name: test node streams cache components dev
- needs:
- [
- 'optimize-ci',
- 'changes',
- 'build-native',
- 'build-next',
- 'fetch-test-timings',
- ]
- if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
-
- strategy:
- fail-fast: false
- matrix:
- group: [1/6, 2/6, 3/6, 4/6, 5/6, 6/6]
- uses: ./.github/workflows/build_reusable.yml
- with:
- afterBuild: |
- export __NEXT_USE_NODE_STREAMS=true
- export __NEXT_CACHE_COMPONENTS=true
- export __NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS=true
- export __NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER=true
- export NEXT_EXTERNAL_TESTS_FILTERS="test/cache-components-tests-manifest.json"
- export NEXT_TEST_MODE=dev
- export IS_TURBOPACK_TEST=1
- export TURBOPACK_DEV=1
-
- node run-tests.js \
- --timings \
- --require-timings \
- -g ${{ matrix.group }} \
- --type development
- testTimingsArtifact: 'test-timings'
- stepName: 'test-node-streams-cache-components-dev-${{ matrix.group }}'
- secrets: inherit
-
- test-node-streams-cache-components-prod:
- name: test node streams cache components prod
- needs:
- [
- 'optimize-ci',
- 'changes',
- 'build-native',
- 'build-next',
- 'fetch-test-timings',
- ]
- if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
-
- strategy:
- fail-fast: false
- matrix:
- group: [1/7, 2/7, 3/7, 4/7, 5/7, 6/7, 7/7]
- uses: ./.github/workflows/build_reusable.yml
- with:
- afterBuild: |
- export __NEXT_USE_NODE_STREAMS=true
- export __NEXT_CACHE_COMPONENTS=true
- export __NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS=true
- export __NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER=true
- export NEXT_EXTERNAL_TESTS_FILTERS="test/cache-components-tests-manifest.json"
- export NEXT_TEST_MODE=start
- export IS_TURBOPACK_TEST=1
- export TURBOPACK_BUILD=1
-
- node run-tests.js \
- --timings \
- --require-timings \
- -g ${{ matrix.group }} \
- --type production
- testTimingsArtifact: 'test-timings'
- stepName: 'test-node-streams-cache-components-prod-${{ matrix.group }}'
- secrets: inherit
-
- test-node-streams-dev:
- name: test node streams dev
- needs: ['optimize-ci', 'changes', 'build-native', 'build-next']
- if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
-
- strategy:
- fail-fast: false
- matrix:
- group: [1/6, 2/6, 3/6, 4/6, 5/6, 6/6]
- uses: ./.github/workflows/build_reusable.yml
- with:
- afterBuild: |
- export __NEXT_USE_NODE_STREAMS=true
- export NEXT_EXTERNAL_TESTS_FILTERS="test/cache-components-tests-manifest.json,test/use-node-streams-tests-manifest.json"
- export NEXT_TEST_MODE=dev
- export IS_TURBOPACK_TEST=1
- export TURBOPACK_DEV=1
- export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
- export RUST_BACKTRACE=1
-
- node run-tests.js \
- --timings \
- -g ${{ matrix.group }} \
- --type development
- stepName: 'test-node-streams-dev-${{ matrix.group }}'
- secrets: inherit
-
- test-node-streams-prod:
- name: test node streams prod
- needs: ['optimize-ci', 'changes', 'build-native', 'build-next']
- if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
-
- strategy:
- fail-fast: false
- matrix:
- group: [1/7, 2/7, 3/7, 4/7, 5/7, 6/7, 7/7]
- uses: ./.github/workflows/build_reusable.yml
- with:
- afterBuild: |
- export __NEXT_USE_NODE_STREAMS=true
- export NEXT_EXTERNAL_TESTS_FILTERS="test/cache-components-tests-manifest.json,test/use-node-streams-tests-manifest.json"
- export NEXT_TEST_MODE=start
- export IS_TURBOPACK_TEST=1
- export TURBOPACK_BUILD=1
- export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
- export RUST_BACKTRACE=1
-
- node run-tests.js \
- --timings \
- -g ${{ matrix.group }} \
- --type production
- stepName: 'test-node-streams-prod-${{ matrix.group }}'
- secrets: inherit
-
tests-pass:
needs:
[
@@ -1157,10 +1029,6 @@ jobs:
'test-firefox-safari',
'test-cache-components-dev',
'test-cache-components-prod',
- 'test-node-streams-cache-components-dev',
- 'test-node-streams-cache-components-prod',
- 'test-node-streams-dev',
- 'test-node-streams-prod',
'test-cargo-unit',
'rust-check',
'rustdoc-check',
diff --git a/lerna.json b/lerna.json
index 9acef58d795f..98302bb874e3 100644
--- a/lerna.json
+++ b/lerna.json
@@ -15,5 +15,5 @@
"registry": "https://registry.npmjs.org/"
}
},
- "version": "16.3.0-canary.37"
+ "version": "16.3.0-canary.38"
}
\ No newline at end of file
diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json
index 0552ff3a1317..c31434c22d2a 100644
--- a/packages/create-next-app/package.json
+++ b/packages/create-next-app/package.json
@@ -1,6 +1,6 @@
{
"name": "create-next-app",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"keywords": [
"react",
"next",
diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json
index 5ae58b51f2dc..492fe300daaa 100644
--- a/packages/eslint-config-next/package.json
+++ b/packages/eslint-config-next/package.json
@@ -1,6 +1,6 @@
{
"name": "eslint-config-next",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"description": "ESLint configuration used by Next.js.",
"license": "MIT",
"repository": {
@@ -12,7 +12,7 @@
"dist"
],
"dependencies": {
- "@next/eslint-plugin-next": "16.3.0-canary.37",
+ "@next/eslint-plugin-next": "16.3.0-canary.38",
"eslint-import-resolver-node": "^0.3.6",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.32.0",
diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json
index 071fa49c464c..225325e6d68d 100644
--- a/packages/eslint-plugin-internal/package.json
+++ b/packages/eslint-plugin-internal/package.json
@@ -1,7 +1,7 @@
{
"name": "@next/eslint-plugin-internal",
"private": true,
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"description": "ESLint plugin for working on Next.js.",
"exports": {
".": "./src/eslint-plugin-internal.js"
diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json
index bb64794693b0..a63a8afaa447 100644
--- a/packages/eslint-plugin-next/package.json
+++ b/packages/eslint-plugin-next/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/eslint-plugin-next",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"description": "ESLint plugin for Next.js.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
diff --git a/packages/font/package.json b/packages/font/package.json
index 7e5e92b07790..d2e8ac784459 100644
--- a/packages/font/package.json
+++ b/packages/font/package.json
@@ -1,7 +1,7 @@
{
"name": "@next/font",
"private": true,
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"repository": {
"url": "vercel/next.js",
"directory": "packages/font"
diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json
index d100769667b2..a985d1222498 100644
--- a/packages/next-bundle-analyzer/package.json
+++ b/packages/next-bundle-analyzer/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/bundle-analyzer",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"main": "index.js",
"types": "index.d.ts",
"license": "MIT",
diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json
index fba401d9fa72..f875fac75015 100644
--- a/packages/next-codemod/package.json
+++ b/packages/next-codemod/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/codemod",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"license": "MIT",
"repository": {
"type": "git",
diff --git a/packages/next-env/index.ts b/packages/next-env/index.ts
index 392670c7817e..5b34e68be666 100644
--- a/packages/next-env/index.ts
+++ b/packages/next-env/index.ts
@@ -18,7 +18,17 @@ let cachedLoadedEnvFiles: LoadedEnvFiles = []
let previousLoadedEnvFiles: LoadedEnvFiles = []
export function updateInitialEnv(newEnv: Env) {
- Object.assign(initialEnv || {}, newEnv)
+ if (!initialEnv) {
+ return
+ }
+
+ for (const [key, value] of Object.entries(newEnv)) {
+ if (value === undefined) {
+ delete initialEnv[key]
+ } else {
+ initialEnv[key] = value
+ }
+ }
}
type Log = {
diff --git a/packages/next-env/package.json b/packages/next-env/package.json
index 91f7abeb9ed2..faa73740af0f 100644
--- a/packages/next-env/package.json
+++ b/packages/next-env/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/env",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"keywords": [
"react",
"next",
diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json
index b133ae0ceda5..68afc5d7341a 100644
--- a/packages/next-mdx/package.json
+++ b/packages/next-mdx/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/mdx",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"main": "index.js",
"license": "MIT",
"repository": {
diff --git a/packages/next-playwright/package.json b/packages/next-playwright/package.json
index fe0bbd9562f7..c8e23bf0f234 100644
--- a/packages/next-playwright/package.json
+++ b/packages/next-playwright/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/playwright",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-playwright"
diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json
index 6ec1529c33a4..06c2fd30ae63 100644
--- a/packages/next-plugin-storybook/package.json
+++ b/packages/next-plugin-storybook/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/plugin-storybook",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-plugin-storybook"
diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json
index 68e02644914d..8596f6300312 100644
--- a/packages/next-polyfill-module/package.json
+++ b/packages/next-polyfill-module/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-module",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)",
"main": "dist/polyfill-module.js",
"license": "MIT",
diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json
index f2f21d15b13c..752e7b5128eb 100644
--- a/packages/next-polyfill-nomodule/package.json
+++ b/packages/next-polyfill-nomodule/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-nomodule",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"description": "A polyfill for non-dead, nomodule browsers.",
"main": "dist/polyfill-nomodule.js",
"license": "MIT",
diff --git a/packages/next-routing/package.json b/packages/next-routing/package.json
index 5ab5910f388d..b5110dd01451 100644
--- a/packages/next-routing/package.json
+++ b/packages/next-routing/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/routing",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"keywords": [
"react",
"next",
diff --git a/packages/next-rspack/package.json b/packages/next-rspack/package.json
index 91a5a17105fe..f81659571ee0 100644
--- a/packages/next-rspack/package.json
+++ b/packages/next-rspack/package.json
@@ -1,6 +1,6 @@
{
"name": "next-rspack",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-rspack"
diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json
index b7073cd5208c..e1fc35d0380c 100644
--- a/packages/next-swc/package.json
+++ b/packages/next-swc/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/swc",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"private": true,
"files": [
"native/"
diff --git a/packages/next/package.json b/packages/next/package.json
index 0c06e3f121de..495b02117b35 100644
--- a/packages/next/package.json
+++ b/packages/next/package.json
@@ -1,6 +1,6 @@
{
"name": "next",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"description": "The React Framework",
"main": "./dist/server/next.js",
"license": "MIT",
@@ -101,7 +101,7 @@
]
},
"dependencies": {
- "@next/env": "16.3.0-canary.37",
+ "@next/env": "16.3.0-canary.38",
"@swc/helpers": "0.5.15",
"baseline-browser-mapping": "^2.9.19",
"caniuse-lite": "^1.0.30001579",
@@ -165,11 +165,11 @@
"@modelcontextprotocol/sdk": "1.18.1",
"@mswjs/interceptors": "0.23.0",
"@napi-rs/triples": "1.2.0",
- "@next/font": "16.3.0-canary.37",
- "@next/polyfill-module": "16.3.0-canary.37",
- "@next/polyfill-nomodule": "16.3.0-canary.37",
- "@next/react-refresh-utils": "16.3.0-canary.37",
- "@next/swc": "16.3.0-canary.37",
+ "@next/font": "16.3.0-canary.38",
+ "@next/polyfill-module": "16.3.0-canary.38",
+ "@next/polyfill-nomodule": "16.3.0-canary.38",
+ "@next/react-refresh-utils": "16.3.0-canary.38",
+ "@next/swc": "16.3.0-canary.38",
"@opentelemetry/api": "1.6.0",
"@playwright/test": "1.58.2",
"@rspack/core": "1.6.7",
diff --git a/packages/next/src/server/app-render/stream-ops.node.ts b/packages/next/src/server/app-render/stream-ops.node.ts
index 5e4e80b26253..90dc34d9aadb 100644
--- a/packages/next/src/server/app-render/stream-ops.node.ts
+++ b/packages/next/src/server/app-render/stream-ops.node.ts
@@ -614,9 +614,6 @@ export async function renderToNodeFizzStream(
onHeaders: streamOptions?.onHeaders,
onShellReady() {
streamOptions?.onShellReady?.()
- if (!deferPipe) {
- pipeable.pipe(pt)
- }
shellReady.resolve()
},
onShellError(error: unknown) {
@@ -636,6 +633,11 @@ export async function renderToNodeFizzStream(
await shellReady.promise
+ if (!deferPipe) {
+ await waitAtLeastOneReactRenderTask()
+ pipeable.pipe(pt)
+ }
+
return {
stream: pt,
allReady: allReady.promise,
diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts
index 8da5f855611e..077f90d8eca5 100644
--- a/packages/next/src/server/config-shared.ts
+++ b/packages/next/src/server/config-shared.ts
@@ -36,7 +36,7 @@ export type NextConfigComplete = Required> & {
prefetchInlining?: PrefetchInliningConfig
// Normalized by config.ts: defaulted to 90% of staticPageGenerationTimeout
useCacheTimeout: number
- // Normalized by config.ts `finalizeConfig`: defaulted to `'manual-warning'`
+ // Normalized by config.ts `finalizeConfig`: defaulted to `'warning'`
instantInsights: { validationLevel: ValidationLevel }
}
// The root directory of the distDir. In development mode, this is the parent directory of `distDir`
@@ -1080,10 +1080,10 @@ export interface ExperimentalConfig {
/**
* Controls the validation behavior of Instant Insights
*
- * - `'warning'`: Validates all navigations for Instant UI in development
- * - `'manual-warning'`: Validates navigations for Instant UI in development when configured with `unstable_instant` in Pages and Layouts
+ * - `'warning'` (default): Validates all navigations for Instant UI in development
+ * - `'manual-warning'`: Validates navigations for Instant UI in development only when configured with `unstable_instant` in Pages and Layouts
* - `'experimental-error'`: Validates all navigations for Instant in development and build. Use with caution.
- * - `'experimental-manual-error'`: Validates navigations for Instant UI in developement and build when configured with `unstable_instant` in Pages and Layouts. Use with caution.
+ * - `'experimental-manual-error'`: Validates navigations for Instant UI in development and build when configured with `unstable_instant` in Pages and Layouts. Use with caution.
*/
validationLevel?: ValidationLevel
}
@@ -2040,6 +2040,7 @@ export const defaultConfig = Object.freeze({
gestureTransition: false,
inlineCss: false,
useCache: undefined,
+ useNodeStreams: true,
slowModuleDetection: undefined,
globalNotFound: false,
browserDebugInfoInTerminal: 'warn',
diff --git a/packages/next/src/server/config.ts b/packages/next/src/server/config.ts
index c61f17984c65..3d1e14ffb0a5 100644
--- a/packages/next/src/server/config.ts
+++ b/packages/next/src/server/config.ts
@@ -1618,11 +1618,30 @@ function assignDefaultsAndValidate(
function finalizeConfig(config: NextConfigComplete): NextConfigComplete {
config.experimental.instantInsights = {
validationLevel:
- config.experimental.instantInsights?.validationLevel ?? 'manual-warning',
+ config.experimental.instantInsights?.validationLevel ?? 'warning',
}
+ syncUseNodeStreamsEnv(config)
return config
}
+function syncUseNodeStreamsEnv(config: NextConfig): void {
+ // This must use resolved config: user configs are inspected before defaults
+ // are merged, while runtime bundles must select the default implementation.
+ const useNodeStreams = config.experimental?.useNodeStreams
+ ? 'true'
+ : undefined
+
+ if (useNodeStreams) {
+ process.env.__NEXT_USE_NODE_STREAMS = useNodeStreams
+ } else {
+ delete process.env.__NEXT_USE_NODE_STREAMS
+ }
+
+ // Dev env reloads restore process.env from this snapshot. Preserve the
+ // resolved runtime selection so a reload cannot mix stream implementations.
+ updateInitialEnv({ __NEXT_USE_NODE_STREAMS: useNodeStreams })
+}
+
async function applyModifyConfig(
config: NextConfigComplete,
phase: PHASE_TYPE,
@@ -1750,6 +1769,7 @@ export default async function loadConfig(
return cachedResult.rawConfig
}
+ syncUseNodeStreamsEnv(cachedResult.config)
return cachedResult.config
} else {
// Reset next.config errors before loading config
@@ -1777,6 +1797,8 @@ export default async function loadConfig(
process.env.__NEXT_PRIVATE_STANDALONE_CONFIG
)
+ syncUseNodeStreamsEnv(standaloneConfig)
+
// Cache the standalone config
configCache.set(cacheKey, {
config: standaloneConfig,
@@ -2229,16 +2251,6 @@ function enforceExperimentalFeatures(
config.experimental.useNodeStreams = true
}
- // Keep runtime bundle selection env in sync with the resolved config.
- // Explicit user config (e.g. useNodeStreams: false) should win over an
- // inherited shell env var to avoid selecting nodestream runtime bundles
- // while define-env compiled user bundles with node streams disabled.
- if (config.experimental.useNodeStreams) {
- process.env.__NEXT_USE_NODE_STREAMS = 'true'
- } else {
- delete process.env.__NEXT_USE_NODE_STREAMS
- }
-
// TODO: Remove this once strictRouteTypes is the default.
if (
process.env.__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES === 'true' &&
diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json
index 9a7664759eeb..423e598566e3 100644
--- a/packages/react-refresh-utils/package.json
+++ b/packages/react-refresh-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/react-refresh-utils",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"description": "An experimental package providing utilities for React Refresh.",
"repository": {
"url": "vercel/next.js",
diff --git a/packages/third-parties/package.json b/packages/third-parties/package.json
index 16793b4f18ce..9f486741dc3e 100644
--- a/packages/third-parties/package.json
+++ b/packages/third-parties/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/third-parties",
- "version": "16.3.0-canary.37",
+ "version": "16.3.0-canary.38",
"repository": {
"url": "vercel/next.js",
"directory": "packages/third-parties"
@@ -27,7 +27,7 @@
"third-party-capital": "1.0.20"
},
"devDependencies": {
- "next": "16.3.0-canary.37",
+ "next": "16.3.0-canary.38",
"outdent": "0.8.0",
"prettier": "2.5.1",
"typescript": "6.0.2"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 636661a0b069..ef3d6815d204 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -986,7 +986,7 @@ importers:
packages/eslint-config-next:
dependencies:
'@next/eslint-plugin-next':
- specifier: 16.3.0-canary.37
+ specifier: 16.3.0-canary.38
version: link:../eslint-plugin-next
eslint:
specifier: '>=9.0.0'
@@ -1063,7 +1063,7 @@ importers:
packages/next:
dependencies:
'@next/env':
- specifier: 16.3.0-canary.37
+ specifier: 16.3.0-canary.38
version: link:../next-env
'@swc/helpers':
specifier: 0.5.15
@@ -1184,19 +1184,19 @@ importers:
specifier: 1.2.0
version: 1.2.0
'@next/font':
- specifier: 16.3.0-canary.37
+ specifier: 16.3.0-canary.38
version: link:../font
'@next/polyfill-module':
- specifier: 16.3.0-canary.37
+ specifier: 16.3.0-canary.38
version: link:../next-polyfill-module
'@next/polyfill-nomodule':
- specifier: 16.3.0-canary.37
+ specifier: 16.3.0-canary.38
version: link:../next-polyfill-nomodule
'@next/react-refresh-utils':
- specifier: 16.3.0-canary.37
+ specifier: 16.3.0-canary.38
version: link:../react-refresh-utils
'@next/swc':
- specifier: 16.3.0-canary.37
+ specifier: 16.3.0-canary.38
version: link:../next-swc
'@opentelemetry/api':
specifier: 1.6.0
@@ -1930,7 +1930,7 @@ importers:
version: 1.0.20
devDependencies:
next:
- specifier: 16.3.0-canary.37
+ specifier: 16.3.0-canary.38
version: link:../next
outdent:
specifier: 0.8.0
diff --git a/test/development/app-dir/cache-components-dev-errors/next.config.js b/test/development/app-dir/cache-components-dev-errors/next.config.js
index e64bae22d658..94fcd455df28 100644
--- a/test/development/app-dir/cache-components-dev-errors/next.config.js
+++ b/test/development/app-dir/cache-components-dev-errors/next.config.js
@@ -3,6 +3,11 @@
*/
const nextConfig = {
cacheComponents: true,
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
}
module.exports = nextConfig
diff --git a/test/development/app-dir/error-overlay/error-ignored-frames/next.config.js b/test/development/app-dir/error-overlay/error-ignored-frames/next.config.js
index 807126e4cf0b..3f1322dc956a 100644
--- a/test/development/app-dir/error-overlay/error-ignored-frames/next.config.js
+++ b/test/development/app-dir/error-overlay/error-ignored-frames/next.config.js
@@ -1,6 +1,12 @@
/**
* @type {import('next').NextConfig}
*/
-const nextConfig = {}
+const nextConfig = {
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
+}
module.exports = nextConfig
diff --git a/test/development/app-dir/hmr-iframe/next.config.js b/test/development/app-dir/hmr-iframe/next.config.js
new file mode 100644
index 000000000000..3f1322dc956a
--- /dev/null
+++ b/test/development/app-dir/hmr-iframe/next.config.js
@@ -0,0 +1,12 @@
+/**
+ * @type {import('next').NextConfig}
+ */
+const nextConfig = {
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
+}
+
+module.exports = nextConfig
diff --git a/test/development/app-dir/owner-stack-invalid-element-type/next.config.js b/test/development/app-dir/owner-stack-invalid-element-type/next.config.js
index 807126e4cf0b..3f1322dc956a 100644
--- a/test/development/app-dir/owner-stack-invalid-element-type/next.config.js
+++ b/test/development/app-dir/owner-stack-invalid-element-type/next.config.js
@@ -1,6 +1,12 @@
/**
* @type {import('next').NextConfig}
*/
-const nextConfig = {}
+const nextConfig = {
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
+}
module.exports = nextConfig
diff --git a/test/development/app-dir/owner-stack/next.config.js b/test/development/app-dir/owner-stack/next.config.js
index 807126e4cf0b..3f1322dc956a 100644
--- a/test/development/app-dir/owner-stack/next.config.js
+++ b/test/development/app-dir/owner-stack/next.config.js
@@ -1,6 +1,12 @@
/**
* @type {import('next').NextConfig}
*/
-const nextConfig = {}
+const nextConfig = {
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
+}
module.exports = nextConfig
diff --git a/test/e2e/app-dir/cache-components-errors/fixtures/default/next.config.js b/test/e2e/app-dir/cache-components-errors/fixtures/default/next.config.js
index e64bae22d658..94fcd455df28 100644
--- a/test/e2e/app-dir/cache-components-errors/fixtures/default/next.config.js
+++ b/test/e2e/app-dir/cache-components-errors/fixtures/default/next.config.js
@@ -3,6 +3,11 @@
*/
const nextConfig = {
cacheComponents: true,
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
}
module.exports = nextConfig
diff --git a/test/e2e/app-dir/cache-components-errors/fixtures/http-access-fallback-prerender/next.config.js b/test/e2e/app-dir/cache-components-errors/fixtures/http-access-fallback-prerender/next.config.js
index 34aa2b5aed26..e561e049a1eb 100644
--- a/test/e2e/app-dir/cache-components-errors/fixtures/http-access-fallback-prerender/next.config.js
+++ b/test/e2e/app-dir/cache-components-errors/fixtures/http-access-fallback-prerender/next.config.js
@@ -5,6 +5,9 @@ const nextConfig = {
cacheComponents: true,
experimental: {
authInterrupts: true,
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
},
}
diff --git a/test/e2e/app-dir/cache-components/next.config.js b/test/e2e/app-dir/cache-components/next.config.js
index 86f364bdd422..e83f1cf6305a 100644
--- a/test/e2e/app-dir/cache-components/next.config.js
+++ b/test/e2e/app-dir/cache-components/next.config.js
@@ -5,6 +5,11 @@ const nextConfig = {
cacheComponents: true,
adapterPath:
process.env.NEXT_ADAPTER_PATH ?? require.resolve('./my-adapter.mjs'),
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
}
module.exports = nextConfig
diff --git a/test/e2e/app-dir/instant-validation-level-default/app/bare/page.tsx b/test/e2e/app-dir/instant-validation-level-default/app/bare/page.tsx
new file mode 100644
index 000000000000..307c49df3a08
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation-level-default/app/bare/page.tsx
@@ -0,0 +1,16 @@
+// Bare page (no `unstable_instant` config). Under the framework default
+// (`'warning'`), implicit validation fires on this page in dev. The runtime
+// data accessed at the top of the page is the "Suspense too high for instant
+// navigation" violation that instant validation specifically flags. The root
+// layout's Suspense satisfies static-shell validation, so the only error in
+// dev is the instant one.
+import { connection } from 'next/server'
+
+export default async function Page() {
+ await connection()
+ return (
+
+ bare page (no unstable_instant), runtime data at the top.
+
+ )
+}
diff --git a/test/e2e/app-dir/instant-validation-level-default/app/explicit-false/page.tsx b/test/e2e/app-dir/instant-validation-level-default/app/explicit-false/page.tsx
new file mode 100644
index 000000000000..27e70e2c2631
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation-level-default/app/explicit-false/page.tsx
@@ -0,0 +1,16 @@
+// Page explicitly opts out of instant validation. Under the framework
+// default (`'warning'`), this segment-level override suppresses the
+// implicit validation that would otherwise fire on a bare page, so no
+// redbox appears.
+import { connection } from 'next/server'
+
+export const unstable_instant = false
+
+export default async function Page() {
+ await connection()
+ return (
+
+ explicit-false page (segment opts out of validation).
+
+ )
+}
diff --git a/test/e2e/app-dir/instant-validation-level-default/app/layout.tsx b/test/e2e/app-dir/instant-validation-level-default/app/layout.tsx
new file mode 100644
index 000000000000..a32d71748e1c
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation-level-default/app/layout.tsx
@@ -0,0 +1,23 @@
+import { Suspense, type ReactNode } from 'react'
+
+// Validation level is not set in next.config.ts, so the framework default
+// applies. The default is 'warning' — implicit validation fires on bare
+// page/default segments in dev only (build is unaffected unless a segment
+// explicitly escalates with `level: 'experimental-error'`).
+//
+// Children are wrapped in Suspense so that pages with runtime data
+// accessed at the top of the page don't fail static-shell validation
+// (the Suspense fallback renders into the static shell). Instant
+// validation flags "Suspense too high for instant navigation" as an
+// instant-specific violation when it runs.
+export const unstable_instant = false
+
+export default function RootLayout({ children }: { children: ReactNode }) {
+ return (
+
+
+ loading…
}>{children}
+
+
+ )
+}
diff --git a/test/e2e/app-dir/instant-validation-level-default/instant-validation-level-default.test.ts b/test/e2e/app-dir/instant-validation-level-default/instant-validation-level-default.test.ts
new file mode 100644
index 000000000000..461bf9067e0d
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation-level-default/instant-validation-level-default.test.ts
@@ -0,0 +1,93 @@
+import { nextTestSetup } from 'e2e-utils'
+import { expectBuildValidationSkipped } from 'e2e-utils/instant-validation'
+import { waitForNoErrorToast } from '../../../lib/next-test-utils'
+
+// This fixture intentionally omits `experimental.instantInsights` from
+// next.config.ts. It pins the framework default for `validationLevel` —
+// the framework should resolve the default to `'warning'`, which means
+// implicit validation fires on bare pages in dev. If the framework default
+// ever changes, this test should fail, alerting whoever changes it.
+//
+// For exhaustive coverage of explicit levels and per-segment overrides,
+// see the sibling `instant-validation-level-{warning,manual-warning,error,
+// manual-error}` fixtures.
+describe('instant validation - default level', () => {
+ const { next, skipped, isNextDev, isNextStart, isTurbopack } = nextTestSetup({
+ files: __dirname,
+ skipStart: true,
+ skipDeployment: true,
+ env: {
+ NEXT_TEST_LOG_VALIDATION: '1',
+ },
+ })
+ if (skipped) return
+
+ if (isNextStart && !isTurbopack) {
+ it.skip('TODO: snapshot tests for webpack', () => {})
+ return
+ }
+
+ if (isNextStart) {
+ beforeAll(async () => {
+ await next.build({ args: ['--experimental-build-mode', 'compile'] })
+ })
+ afterEach(async () => {
+ await next.stop()
+ })
+ } else {
+ beforeAll(async () => {
+ await next.start()
+ })
+ }
+
+ const prerender = async (pathname: string) => {
+ return await next.build({
+ args: [
+ '--experimental-build-mode',
+ 'generate',
+ '--debug-build-paths',
+ `app${pathname}/page.tsx`,
+ ],
+ })
+ }
+
+ if (isNextDev) {
+ describe('dev', () => {
+ it('bare page: framework default matches `warning`, implicit validation fires', async () => {
+ const browser = await next.browser('/bare')
+ await expect(browser).toDisplayCollapsedRedbox(`
+ {
+ "code": "E1264",
+ "description": "Next.js encountered uncached data during a navigation.",
+ "environmentLabel": "Server",
+ "label": "Instant",
+ "source": "app/bare/page.tsx (10:19) @ Page
+ > 10 | await connection()
+ | ^",
+ "stack": [
+ "Page app/bare/page.tsx (10:19)",
+ ],
+ }
+ `)
+ })
+
+ it('explicit-false page: per-segment opt-out still works under default', async () => {
+ const browser = await next.browser('/explicit-false')
+ await browser.elementByCss('main')
+ await waitForNoErrorToast(browser, { waitInMs: 500 })
+ })
+ })
+ } else {
+ describe('build', () => {
+ it('bare page: framework default is dev-only, build skips validation', async () => {
+ const result = await prerender('/bare')
+ expectBuildValidationSkipped(result)
+ })
+
+ it('explicit-false page: per-segment opt-out keeps build clean', async () => {
+ const result = await prerender('/explicit-false')
+ expectBuildValidationSkipped(result)
+ })
+ })
+ }
+})
diff --git a/test/e2e/app-dir/instant-validation-level-default/next.config.ts b/test/e2e/app-dir/instant-validation-level-default/next.config.ts
new file mode 100644
index 000000000000..9fd0c7a083d4
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation-level-default/next.config.ts
@@ -0,0 +1,10 @@
+import type { NextConfig } from 'next'
+
+const nextConfig: NextConfig = {
+ cacheComponents: true,
+ typescript: {
+ ignoreBuildErrors: true,
+ },
+}
+
+export default nextConfig
diff --git a/test/e2e/app-dir/loader-file-named-export-custom-loader-error/next.config.js b/test/e2e/app-dir/loader-file-named-export-custom-loader-error/next.config.js
index 77961243627b..b625c77b5dca 100644
--- a/test/e2e/app-dir/loader-file-named-export-custom-loader-error/next.config.js
+++ b/test/e2e/app-dir/loader-file-named-export-custom-loader-error/next.config.js
@@ -5,6 +5,11 @@ const nextConfig = {
images: {
loaderFile: '/dummy-loader.ts',
},
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
}
module.exports = nextConfig
diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/next.config.js b/test/e2e/app-dir/non-root-project-monorepo/apps/web/next.config.js
index 807126e4cf0b..3f1322dc956a 100644
--- a/test/e2e/app-dir/non-root-project-monorepo/apps/web/next.config.js
+++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/next.config.js
@@ -1,6 +1,12 @@
/**
* @type {import('next').NextConfig}
*/
-const nextConfig = {}
+const nextConfig = {
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
+}
module.exports = nextConfig
diff --git a/test/e2e/app-dir/router-autoscroll/next.config.js b/test/e2e/app-dir/router-autoscroll/next.config.js
index 36e8d82ea1af..ad60a12885cd 100644
--- a/test/e2e/app-dir/router-autoscroll/next.config.js
+++ b/test/e2e/app-dir/router-autoscroll/next.config.js
@@ -1,6 +1,12 @@
/**
* @type {import('next').NextConfig}
*/
-const config = {}
+const config = {
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
+}
module.exports = config
diff --git a/test/e2e/app-dir/server-source-maps/fixtures/default/next.config.js b/test/e2e/app-dir/server-source-maps/fixtures/default/next.config.js
index 8a61ee02d1f4..19c0893004fa 100644
--- a/test/e2e/app-dir/server-source-maps/fixtures/default/next.config.js
+++ b/test/e2e/app-dir/server-source-maps/fixtures/default/next.config.js
@@ -6,6 +6,9 @@ const nextConfig = {
experimental: {
cpus: 1,
serverSourceMaps: true,
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
},
serverExternalPackages: ['external-pkg'],
}
diff --git a/test/e2e/app-dir/server-source-maps/fixtures/edge/next.config.js b/test/e2e/app-dir/server-source-maps/fixtures/edge/next.config.js
index 86773e2ae9a6..0341e872150f 100644
--- a/test/e2e/app-dir/server-source-maps/fixtures/edge/next.config.js
+++ b/test/e2e/app-dir/server-source-maps/fixtures/edge/next.config.js
@@ -5,6 +5,9 @@ const nextConfig = {
experimental: {
cpus: 1,
serverSourceMaps: true,
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
},
}
diff --git a/test/e2e/legacy-link-behavior/next.config.js b/test/e2e/legacy-link-behavior/next.config.js
index e64bae22d658..94fcd455df28 100644
--- a/test/e2e/legacy-link-behavior/next.config.js
+++ b/test/e2e/legacy-link-behavior/next.config.js
@@ -3,6 +3,11 @@
*/
const nextConfig = {
cacheComponents: true,
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
}
module.exports = nextConfig
diff --git a/test/e2e/next-image-new/app-dir-localpatterns/next.config.js b/test/e2e/next-image-new/app-dir-localpatterns/next.config.js
index 10c28b1a185c..635050bf29c1 100644
--- a/test/e2e/next-image-new/app-dir-localpatterns/next.config.js
+++ b/test/e2e/next-image-new/app-dir-localpatterns/next.config.js
@@ -7,4 +7,9 @@ module.exports = {
},
],
},
+ experimental: {
+ instantInsights: {
+ validationLevel: 'manual-warning',
+ },
+ },
}
diff --git a/test/production/debug-build-path/debug-build-paths.test.ts b/test/production/debug-build-path/debug-build-paths.test.ts
index ac382337ebd2..a70ab6827bb3 100644
--- a/test/production/debug-build-path/debug-build-paths.test.ts
+++ b/test/production/debug-build-path/debug-build-paths.test.ts
@@ -1,11 +1,33 @@
import path from 'path'
import { nextTestSetup } from 'e2e-utils'
+function getTreeView(cliOutput: string): string {
+ let foundStart = false
+ const lines: string[] = []
+
+ for (const line of cliOutput.split('\n')) {
+ foundStart ||= line.startsWith('Route ')
+
+ if (foundStart) {
+ lines.push(line)
+ }
+
+ if (line.startsWith('└')) {
+ foundStart = false
+ }
+ }
+
+ return lines.join('\n').trim()
+}
+
describe('debug-build-paths', () => {
describe('default fixture', () => {
const { next } = nextTestSetup({
files: path.join(__dirname, 'fixtures/default'),
skipStart: true,
+ env: {
+ __NEXT_PRIVATE_DETERMINISTIC_BUILD_OUTPUT: '1',
+ },
})
describe('explicit path formats', () => {
@@ -17,12 +39,11 @@ describe('debug-build-paths', () => {
expect(buildResult.cliOutput).toBeDefined()
// Should only build the specified page
- expect(buildResult.cliOutput).toContain('Route (pages)')
- expect(buildResult.cliOutput).toContain('○ /foo')
- // Should not build other pages
- expect(buildResult.cliOutput).not.toContain('○ /bar')
- // Should not build app routes
- expect(buildResult.cliOutput).not.toContain('Route (app)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (pages)
+ ┌ ○ /404
+ └ ○ /foo"
+ `)
})
it('should build multiple pages routes', async () => {
@@ -33,11 +54,12 @@ describe('debug-build-paths', () => {
expect(buildResult.cliOutput).toBeDefined()
// Should build both specified pages
- expect(buildResult.cliOutput).toContain('Route (pages)')
- expect(buildResult.cliOutput).toContain('○ /foo')
- expect(buildResult.cliOutput).toContain('○ /bar')
- // Should not build app routes
- expect(buildResult.cliOutput).not.toContain('Route (app)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (pages)
+ ┌ ○ /404
+ ├ ○ /bar
+ └ ○ /foo"
+ `)
})
it('should build dynamic route with literal [slug] path', async () => {
@@ -50,14 +72,11 @@ describe('debug-build-paths', () => {
expect(buildResult.cliOutput).toBeDefined()
// Should build only the blog/[slug] route
- expect(buildResult.cliOutput).toContain('Route (app)')
- expect(buildResult.cliOutput).toContain('/blog/[slug]')
- // Should not build other app routes
- expect(buildResult.cliOutput).not.toMatch(/○ \/\n/)
- expect(buildResult.cliOutput).not.toContain('○ /about')
- expect(buildResult.cliOutput).not.toContain('○ /dashboard')
- // Should not build pages routes
- expect(buildResult.cliOutput).not.toContain('Route (pages)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ └ ƒ /blog/[slug]"
+ `)
})
})
@@ -69,17 +88,14 @@ describe('debug-build-paths', () => {
expect(buildResult.exitCode).toBe(0)
expect(buildResult.cliOutput).toBeDefined()
- // Should build pages matching the glob
- expect(buildResult.cliOutput).toContain('Route (pages)')
- expect(buildResult.cliOutput).toContain('○ /foo')
- expect(buildResult.cliOutput).toContain('○ /bar')
-
- // Should build the specified app route
- expect(buildResult.cliOutput).toContain('Route (app)')
- expect(buildResult.cliOutput).toContain('○ /')
- // Should not build other app routes
- expect(buildResult.cliOutput).not.toContain('○ /about')
- expect(buildResult.cliOutput).not.toContain('○ /dashboard')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /
+ └ ○ /_not-found
+ Route (pages)
+ ┌ ○ /bar
+ └ ○ /foo"
+ `)
})
it('should match nested routes with app/blog/**/page.tsx pattern', async () => {
@@ -90,14 +106,12 @@ describe('debug-build-paths', () => {
expect(buildResult.cliOutput).toBeDefined()
// Should build the blog route
- expect(buildResult.cliOutput).toContain('Route (app)')
- expect(buildResult.cliOutput).toContain('/blog/[slug]')
- // Should not build other app routes (check for exact route, not substring)
- expect(buildResult.cliOutput).not.toMatch(/○ \/\n/)
- expect(buildResult.cliOutput).not.toContain('○ /about')
- expect(buildResult.cliOutput).not.toContain('○ /dashboard')
- // Should not build pages routes
- expect(buildResult.cliOutput).not.toContain('Route (pages)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ ├ ƒ /blog/[slug]
+ └ ƒ /blog/[slug]/comments"
+ `)
})
it('should match dynamic routes with glob before brackets like app/**/[slug]/page.tsx', async () => {
@@ -108,14 +122,11 @@ describe('debug-build-paths', () => {
expect(buildResult.cliOutput).toBeDefined()
// Should build the blog/[slug] route
- expect(buildResult.cliOutput).toContain('Route (app)')
- expect(buildResult.cliOutput).toContain('/blog/[slug]')
- // Should not build other app routes
- expect(buildResult.cliOutput).not.toMatch(/○ \/\n/)
- expect(buildResult.cliOutput).not.toContain('○ /about')
- expect(buildResult.cliOutput).not.toContain('○ /dashboard')
- // Should not build pages routes
- expect(buildResult.cliOutput).not.toContain('Route (pages)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ └ ƒ /blog/[slug]"
+ `)
})
it('should match hybrid pattern with literal [slug] and glob **', async () => {
@@ -129,15 +140,12 @@ describe('debug-build-paths', () => {
expect(buildResult.cliOutput).toBeDefined()
// Should build both blog/[slug] and blog/[slug]/comments routes
- expect(buildResult.cliOutput).toContain('Route (app)')
- expect(buildResult.cliOutput).toContain('/blog/[slug]')
- expect(buildResult.cliOutput).toContain('/blog/[slug]/comments')
- // Should not build other app routes
- expect(buildResult.cliOutput).not.toMatch(/○ \/\n/)
- expect(buildResult.cliOutput).not.toContain('○ /about')
- expect(buildResult.cliOutput).not.toContain('○ /dashboard')
- // Should not build pages routes
- expect(buildResult.cliOutput).not.toContain('Route (pages)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ ├ ƒ /blog/[slug]
+ └ ƒ /blog/[slug]/comments"
+ `)
})
it('should match multiple app routes with explicit patterns', async () => {
@@ -151,15 +159,15 @@ describe('debug-build-paths', () => {
expect(buildResult.cliOutput).toBeDefined()
// Should build specified app routes
- expect(buildResult.cliOutput).toContain('Route (app)')
- expect(buildResult.cliOutput).toContain('○ /')
- expect(buildResult.cliOutput).toContain('○ /about')
- expect(buildResult.cliOutput).toContain('○ /dashboard')
- expect(buildResult.cliOutput).toContain('/blog/[slug]')
- // Should not build routes not specified
- expect(buildResult.cliOutput).not.toContain('/with-type-error')
- // Should not build pages routes
- expect(buildResult.cliOutput).not.toContain('Route (pages)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /
+ ├ ○ /_not-found
+ ├ ○ /about
+ ├ ƒ /blog/[slug]
+ ├ ƒ /blog/[slug]/comments
+ └ ○ /dashboard"
+ `)
})
it('should exclude paths matching negation patterns', async () => {
@@ -188,9 +196,11 @@ describe('debug-build-paths', () => {
})
expect(buildResult.exitCode).toBe(0)
- expect(buildResult.cliOutput).toContain('Route (app)')
- expect(buildResult.cliOutput).toContain('/blog/[slug]')
- expect(buildResult.cliOutput).not.toContain('/blog/[slug]/comments')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ └ ƒ /blog/[slug]"
+ `)
})
it('should support multiple negation patterns', async () => {
@@ -228,12 +238,13 @@ describe('debug-build-paths', () => {
args: ['--debug-build-paths', 'app/(group)/**/page.tsx'],
})
expect(buildResult.exitCode).toBe(0)
- expect(buildResult.cliOutput).toContain('Route (app)')
+
// Route groups are stripped from the path, so /nested instead of /(group)/nested
- expect(buildResult.cliOutput).toContain('/nested')
- // Should not build other routes
- expect(buildResult.cliOutput).not.toContain('○ /about')
- expect(buildResult.cliOutput).not.toContain('○ /dashboard')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ └ ○ /nested"
+ `)
})
it('should build routes with parallel routes', async () => {
@@ -241,12 +252,12 @@ describe('debug-build-paths', () => {
args: ['--debug-build-paths', 'app/parallel-test/**/page.tsx'],
})
expect(buildResult.exitCode).toBe(0)
- expect(buildResult.cliOutput).toContain('Route (app)')
// Parallel route segments (@sidebar) are stripped from the path
- expect(buildResult.cliOutput).toContain('/parallel-test')
- // Should not build other routes
- expect(buildResult.cliOutput).not.toContain('○ /about')
- expect(buildResult.cliOutput).not.toContain('○ /dashboard')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ └ ○ /parallel-test"
+ `)
})
})
@@ -258,10 +269,11 @@ describe('debug-build-paths', () => {
})
// Build should succeed because the file with type error is not checked
expect(buildResult.exitCode).toBe(0)
- expect(buildResult.cliOutput).toContain('Route (pages)')
- expect(buildResult.cliOutput).toContain('○ /foo')
- // Should not include app routes
- expect(buildResult.cliOutput).not.toContain('Route (app)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (pages)
+ ┌ ○ /404
+ └ ○ /foo"
+ `)
})
it('should fail typechecking when route with type error is included', async () => {
@@ -281,6 +293,9 @@ describe('debug-build-paths', () => {
const { next } = nextTestSetup({
files: path.join(__dirname, 'fixtures/src-dir'),
skipStart: true,
+ env: {
+ __NEXT_PRIVATE_DETERMINISTIC_BUILD_OUTPUT: '1',
+ },
})
it('should resolve app patterns with explicit src/ prefix', async () => {
@@ -288,11 +303,11 @@ describe('debug-build-paths', () => {
args: ['--debug-build-paths', 'src/app/blog/[slug]/page.tsx'],
})
expect(buildResult.exitCode).toBe(0)
- expect(buildResult.cliOutput).toContain('Route (app)')
- expect(buildResult.cliOutput).toContain('/blog/[slug]')
- // Should not build other app routes
- expect(buildResult.cliOutput).not.toMatch(/○ \/\n/)
- expect(buildResult.cliOutput).not.toContain('Route (pages)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ └ ƒ /blog/[slug]"
+ `)
})
it('should resolve app patterns without src/ prefix when project uses src/app', async () => {
@@ -300,10 +315,11 @@ describe('debug-build-paths', () => {
args: ['--debug-build-paths', 'app/blog/[slug]/page.tsx'],
})
expect(buildResult.exitCode).toBe(0)
- expect(buildResult.cliOutput).toContain('Route (app)')
- expect(buildResult.cliOutput).toContain('/blog/[slug]')
- expect(buildResult.cliOutput).not.toMatch(/○ \/\n/)
- expect(buildResult.cliOutput).not.toContain('Route (pages)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ └ ƒ /blog/[slug]"
+ `)
})
it('should resolve pages patterns without src/ prefix when project uses src/pages', async () => {
@@ -311,9 +327,11 @@ describe('debug-build-paths', () => {
args: ['--debug-build-paths', 'pages/foo.tsx'],
})
expect(buildResult.exitCode).toBe(0)
- expect(buildResult.cliOutput).toContain('Route (pages)')
- expect(buildResult.cliOutput).toContain('○ /foo')
- expect(buildResult.cliOutput).not.toContain('Route (app)')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (pages)
+ ┌ ○ /404
+ └ ○ /foo"
+ `)
})
it('should resolve glob patterns without src/ prefix when project uses src/app', async () => {
@@ -321,10 +339,11 @@ describe('debug-build-paths', () => {
args: ['--debug-build-paths', 'app/(group)/**/page.tsx'],
})
expect(buildResult.exitCode).toBe(0)
- expect(buildResult.cliOutput).toContain('Route (app)')
- // Route groups are stripped from the path
- expect(buildResult.cliOutput).toContain('/nested')
- expect(buildResult.cliOutput).not.toContain('/blog/[slug]')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ └ ○ /nested"
+ `)
})
})
@@ -332,6 +351,9 @@ describe('debug-build-paths', () => {
const { next } = nextTestSetup({
files: path.join(__dirname, 'fixtures/with-compile-error'),
skipStart: true,
+ env: {
+ __NEXT_PRIVATE_DETERMINISTIC_BUILD_OUTPUT: '1',
+ },
})
it('should skip compilation of excluded routes with compile errors', async () => {
@@ -340,11 +362,11 @@ describe('debug-build-paths', () => {
args: ['--debug-build-paths', 'app/valid/page.tsx'],
})
// Build should succeed because the broken page is not compiled
- expect(buildResult.exitCode).toBe(0)
- expect(buildResult.cliOutput).toContain('Route (app)')
- expect(buildResult.cliOutput).toContain('○ /valid')
- // Should not include the broken route
- expect(buildResult.cliOutput).not.toContain('/broken')
+ expect(getTreeView(buildResult.cliOutput)).toMatchInlineSnapshot(`
+ "Route (app)
+ ┌ ○ /_not-found
+ └ ○ /valid"
+ `)
})
it('should fail compilation when route with compile error is included', async () => {
diff --git a/test/unit/instant-config-normalization.test.ts b/test/unit/instant-config-normalization.test.ts
index e33a8c7811db..670a6d0bbe12 100644
--- a/test/unit/instant-config-normalization.test.ts
+++ b/test/unit/instant-config-normalization.test.ts
@@ -14,7 +14,7 @@ function uniqueDir(tag: string) {
// `experimental.instantInsights.validationLevel` to a concrete value in
// one place so consumers don't each need to know the current framework default.
describe('experimental.instantInsights validationLevel normalization', () => {
- it('defaults to manual-warning when the instantInsights config is absent', async () => {
+ it('defaults to warning when the instantInsights config is absent', async () => {
const config = await loadConfig(
PHASE_PRODUCTION_SERVER,
uniqueDir('absent'),
@@ -23,11 +23,11 @@ describe('experimental.instantInsights validationLevel normalization', () => {
}
)
expect(config.experimental.instantInsights).toEqual({
- validationLevel: 'manual-warning',
+ validationLevel: 'warning',
})
})
- it('defaults to manual-warning when experimental.instantInsights is an empty object', async () => {
+ it('defaults to warning when experimental.instantInsights is an empty object', async () => {
const config = await loadConfig(
PHASE_PRODUCTION_SERVER,
uniqueDir('empty'),
@@ -36,7 +36,7 @@ describe('experimental.instantInsights validationLevel normalization', () => {
}
)
expect(config.experimental.instantInsights).toEqual({
- validationLevel: 'manual-warning',
+ validationLevel: 'warning',
})
})
diff --git a/test/unit/preserve-process-env.test.ts b/test/unit/preserve-process-env.test.ts
index cbddc233040e..174ea9f764d2 100644
--- a/test/unit/preserve-process-env.test.ts
+++ b/test/unit/preserve-process-env.test.ts
@@ -1,4 +1,8 @@
-import { loadEnvConfig } from '../../packages/next-env/'
+import {
+ loadEnvConfig,
+ resetEnv,
+ updateInitialEnv,
+} from '../../packages/next-env/'
describe('preserve process env', () => {
it('should not reassign `process.env`', () => {
@@ -6,4 +10,20 @@ describe('preserve process env', () => {
loadEnvConfig('.')
expect(Object.is(originalProcessEnv, process.env)).toBeTrue()
})
+
+ it('should remove values unset in the initial env snapshot', () => {
+ const key = '__NEXT_TEST_UNSET_INITIAL_ENV'
+
+ try {
+ loadEnvConfig('.')
+ process.env[key] = 'changed'
+ updateInitialEnv({ [key]: undefined })
+
+ resetEnv()
+
+ expect(process.env[key]).toBeUndefined()
+ } finally {
+ delete process.env[key]
+ }
+ })
})
diff --git a/test/use-node-streams-tests-manifest.json b/test/use-node-streams-tests-manifest.json
deleted file mode 100644
index 46c1c103b37e..000000000000
--- a/test/use-node-streams-tests-manifest.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "version": 2,
- "suites": {},
- "rules": {
- "include": [
- "test/e2e/**/*.test.{t,j}s{,x}",
- "test/production/app-*/**/*.test.{t,j}s{,x}",
- "test/development/app-*/**/*.test.{t,j}s{,x}",
- "test/development/acceptance-app/**/*.test.{t,j}s{,x}"
- ],
- "exclude": []
- }
-}
diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs b/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs
index 03cfd954f508..9ba4c465d2a3 100644
--- a/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs
+++ b/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs
@@ -31,8 +31,8 @@ use turbo_bincode::{TurboBincodeBuffer, new_turbo_bincode_decoder, new_turbo_bin
use turbo_tasks::{
CellId, RawVc, ReadCellOptions, ReadCellTracking, ReadConsistency, ReadOutputOptions,
ReadTracking, SharedReference, StackDynTaskInputs, TRANSIENT_TASK_BIT, TaskExecutionReason,
- TaskId, TaskPersistence, TaskPriority, TraitTypeId, TurboTasksBackendApi, TurboTasksPanic,
- ValueTypeId,
+ TaskId, TaskPersistence, TaskPriority, TraitTypeId, TurboTasks, TurboTasksCallApi,
+ TurboTasksPanic, ValueTypeId,
backend::{
Backend, CachedTaskType, CachedTaskTypeArc, CellContent, CellHash, TaskExecutionSpec,
TransientTaskType, TurboTaskContextError, TurboTaskLocalContextError, TurboTasksError,
@@ -225,7 +225,7 @@ impl TurboTasksBackend {
/// Returns `(snapshot_had_new_data, eviction_counts)`.
pub fn snapshot_and_evict_for_testing(
&self,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> (bool, EvictionCounts) {
self.0.snapshot_and_evict_for_testing(turbo_tasks)
}
@@ -270,7 +270,7 @@ impl TurboTasksBackendInner {
fn execute_context<'a>(
&'a self,
- turbo_tasks: &'a dyn TurboTasksBackendApi>,
+ turbo_tasks: &'a TurboTasks>,
) -> impl ExecuteContext<'a> {
ExecuteContextImpl::new(self, turbo_tasks)
}
@@ -312,7 +312,7 @@ impl TurboTasksBackendInner {
#[doc(hidden)]
pub fn snapshot_and_evict_for_testing(
&self,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> (bool, EvictionCounts) {
assert!(
self.should_persist(),
@@ -439,7 +439,7 @@ impl TurboTasksBackendInner {
task_id: TaskId,
reader: Option,
options: ReadOutputOptions,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> Result> {
self.assert_not_persistent_calling_transient(reader, task_id, /* cell_id */ None);
@@ -558,8 +558,7 @@ impl TurboTasksBackendInner {
let this = self.clone();
let tt = turbo_tasks.pin();
move || {
- let tt: &dyn TurboTasksBackendApi> = &*tt;
- let mut ctx = this.execute_context(tt);
+ let mut ctx = this.execute_context(&tt);
let mut visited = FxHashSet::default();
fn indent(s: &str) -> String {
s.split_inclusive('\n')
@@ -771,7 +770,7 @@ impl TurboTasksBackendInner {
reader: Option,
cell: CellId,
options: ReadCellOptions,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> Result> {
self.assert_not_persistent_calling_transient(reader, task_id, Some(cell));
@@ -938,7 +937,7 @@ impl TurboTasksBackendInner {
&self,
parent_span: Option,
reason: &str,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> Result<(Instant, bool), anyhow::Error> {
let snapshot_span =
tracing::trace_span!(parent: parent_span.clone(), "snapshot", reason = reason)
@@ -1376,7 +1375,7 @@ impl TurboTasksBackendInner {
Ok((snapshot_time, true))
}
- fn startup(&self, turbo_tasks: &dyn TurboTasksBackendApi>) {
+ fn startup(&self, turbo_tasks: &TurboTasks>) {
if self.should_restore() {
// Continue all uncompleted operations
// They can't be interrupted by a snapshot since the snapshotting job has not been
@@ -1409,7 +1408,7 @@ impl TurboTasksBackendInner {
}
#[allow(unused_variables)]
- fn stop(&self, turbo_tasks: &dyn TurboTasksBackendApi>) {
+ fn stop(&self, turbo_tasks: &TurboTasks>) {
#[cfg(feature = "verify_aggregation_graph")]
{
self.is_idle.store(false, Ordering::Release);
@@ -1427,7 +1426,7 @@ impl TurboTasksBackendInner {
}
#[allow(unused_variables)]
- fn idle_start(self: &Arc, turbo_tasks: &dyn TurboTasksBackendApi>) {
+ fn idle_start(self: &Arc, turbo_tasks: &TurboTasks>) {
self.idle_start_event.notify(usize::MAX);
#[cfg(feature = "verify_aggregation_graph")]
@@ -1467,7 +1466,7 @@ impl TurboTasksBackendInner {
arg: &mut dyn StackDynTaskInputs,
parent_task: Option,
persistence: TaskPersistence,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> TaskId {
let transient = matches!(persistence, TaskPersistence::Transient);
@@ -1692,11 +1691,7 @@ impl TurboTasksBackendInner {
)
}
- fn invalidate_task(
- &self,
- task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi>,
- ) {
+ fn invalidate_task(&self, task_id: TaskId, turbo_tasks: &TurboTasks>) {
if !self.should_track_dependencies() {
panic!("Dependency tracking is disabled so invalidation is not allowed");
}
@@ -1708,11 +1703,7 @@ impl TurboTasksBackendInner {
);
}
- fn invalidate_tasks(
- &self,
- tasks: &[TaskId],
- turbo_tasks: &dyn TurboTasksBackendApi>,
- ) {
+ fn invalidate_tasks(&self, tasks: &[TaskId], turbo_tasks: &TurboTasks>) {
if !self.should_track_dependencies() {
panic!("Dependency tracking is disabled so invalidation is not allowed");
}
@@ -1727,7 +1718,7 @@ impl TurboTasksBackendInner {
fn invalidate_tasks_set(
&self,
tasks: &AutoSet, 2>,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) {
if !self.should_track_dependencies() {
panic!("Dependency tracking is disabled so invalidation is not allowed");
@@ -1743,7 +1734,7 @@ impl TurboTasksBackendInner {
fn invalidate_serialization(
&self,
task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) {
if task_id.is_transient() {
return;
@@ -1767,7 +1758,7 @@ impl TurboTasksBackendInner {
fn get_task_name(
&self,
task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> String {
let mut ctx = self.execute_context(turbo_tasks);
let task = ctx.task(task_id, TaskDataCategory::Data);
@@ -1788,7 +1779,7 @@ impl TurboTasksBackendInner {
fn task_execution_canceled(
&self,
task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) {
let mut ctx = self.execute_context(turbo_tasks);
let mut task = ctx.task(task_id, TaskDataCategory::All);
@@ -1839,7 +1830,7 @@ impl TurboTasksBackendInner {
&self,
task_id: TaskId,
priority: TaskPriority,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> Option> {
let execution_reason;
let task_type;
@@ -1959,7 +1950,7 @@ impl TurboTasksBackendInner {
cell_counters: &AutoMap, 8>,
#[cfg(feature = "verify_determinism")] stateful: bool,
has_invalidator: bool,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> Option {
// Task completion is a 4 step process:
// 1. Remove old edges (dependencies, collectibles, children, cells) and update the
@@ -2793,7 +2784,7 @@ impl TurboTasksBackendInner {
fn run_backend_job<'a>(
self: &'a Arc,
job: TurboTasksBackendJob,
- turbo_tasks: &'a dyn TurboTasksBackendApi>,
+ turbo_tasks: &'a TurboTasks>,
) -> Pin + Send + 'a>> {
Box::pin(async move {
match job {
@@ -2977,7 +2968,7 @@ impl TurboTasksBackendInner {
&self,
task_id: TaskId,
cell: CellId,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> Result {
let mut ctx = self.execute_context(turbo_tasks);
let task = ctx.task(task_id, TaskDataCategory::Data);
@@ -2993,7 +2984,7 @@ impl TurboTasksBackendInner {
task_id: TaskId,
collectible_type: TraitTypeId,
reader_id: Option,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) -> AutoMap, 1> {
let mut ctx = self.execute_context(turbo_tasks);
let mut collectibles = AutoMap::default();
@@ -3056,7 +3047,7 @@ impl TurboTasksBackendInner {
collectible_type: TraitTypeId,
collectible: RawVc,
task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) {
self.assert_valid_collectible(task_id, collectible);
@@ -3084,7 +3075,7 @@ impl TurboTasksBackendInner {
collectible: RawVc,
count: u32,
task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) {
self.assert_valid_collectible(task_id, collectible);
@@ -3114,7 +3105,7 @@ impl TurboTasksBackendInner {
updated_key_hashes: Option>,
content_hash: Option,
verification_mode: VerificationMode,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) {
operation::UpdateCellOperation::run(
task_id,
@@ -3130,7 +3121,7 @@ impl TurboTasksBackendInner {
fn mark_own_task_as_finished(
&self,
task: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) {
let mut ctx = self.execute_context(turbo_tasks);
let mut task = ctx.task(task, TaskDataCategory::Data);
@@ -3151,7 +3142,7 @@ impl TurboTasksBackendInner {
&self,
task: TaskId,
parent_task: Option,
- turbo_tasks: &dyn TurboTasksBackendApi>,
+ turbo_tasks: &TurboTasks>,
) {
self.assert_not_persistent_calling_transient(parent_task, task, None);
ConnectChildOperation::run(parent_task, task, self.execute_context(turbo_tasks));
@@ -3168,11 +3159,7 @@ impl TurboTasksBackendInner {
task_id
}
- fn dispose_root_task(
- &self,
- task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi>,
- ) {
+ fn dispose_root_task(&self, task_id: TaskId, turbo_tasks: &TurboTasks>) {
#[cfg(feature = "verify_aggregation_graph")]
self.root_tasks.lock().remove(&task_id);
@@ -3194,11 +3181,7 @@ impl TurboTasksBackendInner {
}
#[cfg(feature = "verify_aggregation_graph")]
- fn verify_aggregation_graph(
- &self,
- turbo_tasks: &dyn TurboTasksBackendApi>,
- idle: bool,
- ) {
+ fn verify_aggregation_graph(&self, turbo_tasks: &TurboTasks>, idle: bool) {
if env::var("TURBO_ENGINE_VERIFY_GRAPH").ok().as_deref() == Some("0") {
return;
}
@@ -3456,23 +3439,23 @@ impl TurboTasksBackendInner {
}
impl Backend for TurboTasksBackend {
- fn startup(&self, turbo_tasks: &dyn TurboTasksBackendApi) {
+ fn startup(&self, turbo_tasks: &TurboTasks) {
self.0.startup(turbo_tasks);
}
- fn stopping(&self, _turbo_tasks: &dyn TurboTasksBackendApi) {
+ fn stopping(&self, _turbo_tasks: &TurboTasks) {
self.0.stopping();
}
- fn stop(&self, turbo_tasks: &dyn TurboTasksBackendApi) {
+ fn stop(&self, turbo_tasks: &TurboTasks) {
self.0.stop(turbo_tasks);
}
- fn idle_start(&self, turbo_tasks: &dyn TurboTasksBackendApi) {
+ fn idle_start(&self, turbo_tasks: &TurboTasks) {
self.0.idle_start(turbo_tasks);
}
- fn idle_end(&self, _turbo_tasks: &dyn TurboTasksBackendApi) {
+ fn idle_end(&self, _turbo_tasks: &TurboTasks) {
self.0.idle_end();
}
@@ -3483,37 +3466,33 @@ impl Backend for TurboTasksBackend {
arg: &mut dyn StackDynTaskInputs,
parent_task: Option,
persistence: TaskPersistence,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) -> TaskId {
self.0
.get_or_create_task(native_fn, this, arg, parent_task, persistence, turbo_tasks)
}
- fn invalidate_task(&self, task_id: TaskId, turbo_tasks: &dyn TurboTasksBackendApi) {
+ fn invalidate_task(&self, task_id: TaskId, turbo_tasks: &TurboTasks) {
self.0.invalidate_task(task_id, turbo_tasks);
}
- fn invalidate_tasks(&self, tasks: &[TaskId], turbo_tasks: &dyn TurboTasksBackendApi) {
+ fn invalidate_tasks(&self, tasks: &[TaskId], turbo_tasks: &TurboTasks) {
self.0.invalidate_tasks(tasks, turbo_tasks);
}
fn invalidate_tasks_set(
&self,
tasks: &AutoSet, 2>,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) {
self.0.invalidate_tasks_set(tasks, turbo_tasks);
}
- fn invalidate_serialization(
- &self,
- task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi,
- ) {
+ fn invalidate_serialization(&self, task_id: TaskId, turbo_tasks: &TurboTasks) {
self.0.invalidate_serialization(task_id, turbo_tasks);
}
- fn task_execution_canceled(&self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi) {
+ fn task_execution_canceled(&self, task: TaskId, turbo_tasks: &TurboTasks) {
self.0.task_execution_canceled(task, turbo_tasks)
}
@@ -3521,7 +3500,7 @@ impl Backend for TurboTasksBackend {
&self,
task_id: TaskId,
priority: TaskPriority,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) -> Option> {
self.0
.try_start_task_execution(task_id, priority, turbo_tasks)
@@ -3534,7 +3513,7 @@ impl Backend for TurboTasksBackend {
cell_counters: &AutoMap, 8>,
#[cfg(feature = "verify_determinism")] stateful: bool,
has_invalidator: bool,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) -> Option {
self.0.task_execution_completed(
task_id,
@@ -3552,7 +3531,7 @@ impl Backend for TurboTasksBackend {
fn run_backend_job<'a>(
&'a self,
job: Self::BackendJob,
- turbo_tasks: &'a dyn TurboTasksBackendApi,
+ turbo_tasks: &'a TurboTasks,
) -> Pin + Send + 'a>> {
self.0.run_backend_job(job, turbo_tasks)
}
@@ -3562,7 +3541,7 @@ impl Backend for TurboTasksBackend {
task_id: TaskId,
reader: Option,
options: ReadOutputOptions,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) -> Result> {
self.0
.try_read_task_output(task_id, reader, options, turbo_tasks)
@@ -3574,7 +3553,7 @@ impl Backend for TurboTasksBackend {
cell: CellId,
reader: Option,
options: ReadCellOptions,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) -> Result> {
self.0
.try_read_task_cell(task_id, reader, cell, options, turbo_tasks)
@@ -3584,7 +3563,7 @@ impl Backend for TurboTasksBackend {
&self,
task_id: TaskId,
cell: CellId,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) -> Result {
self.0.try_read_own_task_cell(task_id, cell, turbo_tasks)
}
@@ -3594,7 +3573,7 @@ impl Backend for TurboTasksBackend {
task_id: TaskId,
collectible_type: TraitTypeId,
reader: Option,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) -> AutoMap, 1> {
self.0
.read_task_collectibles(task_id, collectible_type, reader, turbo_tasks)
@@ -3605,7 +3584,7 @@ impl Backend for TurboTasksBackend {
collectible_type: TraitTypeId,
collectible: RawVc,
task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) {
self.0
.emit_collectible(collectible_type, collectible, task_id, turbo_tasks)
@@ -3617,7 +3596,7 @@ impl Backend for TurboTasksBackend {
collectible: RawVc,
count: u32,
task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) {
self.0
.unemit_collectible(collectible_type, collectible, count, task_id, turbo_tasks)
@@ -3631,7 +3610,7 @@ impl Backend for TurboTasksBackend {
updated_key_hashes: Option>,
content_hash: Option,
verification_mode: VerificationMode,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) {
self.0.update_task_cell(
task_id,
@@ -3644,11 +3623,7 @@ impl Backend for TurboTasksBackend {
);
}
- fn mark_own_task_as_finished(
- &self,
- task_id: TaskId,
- turbo_tasks: &dyn TurboTasksBackendApi,
- ) {
+ fn mark_own_task_as_finished(&self, task_id: TaskId, turbo_tasks: &TurboTasks) {
self.0.mark_own_task_as_finished(task_id, turbo_tasks);
}
@@ -3656,7 +3631,7 @@ impl Backend for TurboTasksBackend {
&self,
task: TaskId,
parent_task: Option,
- turbo_tasks: &dyn TurboTasksBackendApi,
+ turbo_tasks: &TurboTasks,
) {
self.0.connect_task(task, parent_task, turbo_tasks);
}
@@ -3664,12 +3639,12 @@ impl Backend for TurboTasksBackend {
fn create_transient_task(
&self,
task_type: TransientTaskType,
- _turbo_tasks: &dyn TurboTasksBackendApi,
+ _turbo_tasks: &TurboTasks,
) -> TaskId {
self.0.create_transient_task(task_type)
}
- fn dispose_root_task(&self, task_id: TaskId, turbo_tasks: &dyn TurboTasksBackendApi) {
+ fn dispose_root_task(&self, task_id: TaskId, turbo_tasks: &TurboTasks) {
self.0.dispose_root_task(task_id, turbo_tasks);
}
@@ -3681,7 +3656,7 @@ impl Backend for TurboTasksBackend {
self.0.options.dependency_tracking
}
- fn get_task_name(&self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi) -> String {
+ fn get_task_name(&self, task: TaskId, turbo_tasks: &TurboTasks) -> String {
self.0.get_task_name(task, turbo_tasks)
}
}
diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs
index fa686e558957..bedb94e8e38b 100644
--- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs
+++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs
@@ -19,7 +19,7 @@ use tracing::info_span;
use tracing::trace_span;
use turbo_tasks::{
CellId, DynTaskInputs, FxIndexMap, RawVc, SharedReference, TaskExecutionReason, TaskId,
- TaskPriority, TurboTasksBackendApi, TurboTasksCallApi, backend::CachedTaskTypeArc,
+ TaskPriority, TurboTasks, TurboTasksCallApi, backend::CachedTaskTypeArc,
macro_helpers::NativeFunction,
};
@@ -168,7 +168,7 @@ impl TaskLockCounter {
pub struct ExecuteContextImpl<'e, B: BackingStorage> {
backend: &'e TurboTasksBackendInner,
- turbo_tasks: &'e dyn TurboTasksBackendApi>,
+ turbo_tasks: &'e TurboTasks>,
_operation_guard: Option>,
task_lock_counter: TaskLockCounter,
}
@@ -176,7 +176,7 @@ pub struct ExecuteContextImpl<'e, B: BackingStorage> {
impl<'e, B: BackingStorage> ExecuteContextImpl<'e, B> {
pub(super) fn new(
backend: &'e TurboTasksBackendInner,
- turbo_tasks: &'e dyn TurboTasksBackendApi>,
+ turbo_tasks: &'e TurboTasks>,
) -> Self {
Self {
backend,
@@ -1018,7 +1018,7 @@ impl<'e, B: BackingStorage> ExecuteContext<'e> for ExecuteContextImpl<'e, B> {
struct ChildExecuteContextImpl<'e, B: BackingStorage> {
backend: &'e TurboTasksBackendInner,
- turbo_tasks: &'e dyn TurboTasksBackendApi>,
+ turbo_tasks: &'e TurboTasks>,
}
impl<'e, B: BackingStorage> ChildExecuteContext<'e> for ChildExecuteContextImpl<'e, B> {
diff --git a/turbopack/crates/turbo-tasks/src/backend.rs b/turbopack/crates/turbo-tasks/src/backend.rs
index a23282829a27..904015babf77 100644
--- a/turbopack/crates/turbo-tasks/src/backend.rs
+++ b/turbopack/crates/turbo-tasks/src/backend.rs
@@ -29,14 +29,13 @@ use turbo_rcstr::RcStr;
use turbo_tasks_hash::DeterministicHasher;
use crate::{
- RawVc, ReadCellOptions, ReadOutputOptions, ReadRef, SharedReference, TaskId, TaskIdSet,
+ CellId, RawVc, ReadCellOptions, ReadOutputOptions, ReadRef, SharedReference, TaskId, TaskIdSet,
TaskPriority, TraitRef, TraitTypeId, TurboTasksCallApi, TurboTasksPanic, ValueTypeId,
ValueTypePersistence, VcValueTrait, VcValueType,
dyn_task_inputs::{DynTaskInputs, StackDynTaskInputs},
event::EventListener,
macro_helpers::NativeFunction,
- manager::{TaskPersistence, TurboTasksBackendApi},
- raw_vc::CellId,
+ manager::{TaskPersistence, TurboTasks},
registry,
task::shared_reference::TypedSharedReference,
task_statistics::TaskStatisticsApi,
@@ -594,40 +593,35 @@ pub enum VerificationMode {
Skip,
}
-pub trait Backend: Sync + Send {
+pub trait Backend: Sized + Sync + Send {
#[allow(unused_variables)]
- fn startup(&self, turbo_tasks: &dyn TurboTasksBackendApi) {}
+ fn startup(&self, turbo_tasks: &TurboTasks) {}
#[allow(unused_variables)]
- fn stop(&self, turbo_tasks: &dyn TurboTasksBackendApi) {}
+ fn stop(&self, turbo_tasks: &TurboTasks) {}
#[allow(unused_variables)]
- fn stopping(&self, turbo_tasks: &dyn TurboTasksBackendApi) {}
+ fn stopping(&self, turbo_tasks: &TurboTasks) {}
#[allow(unused_variables)]
- fn idle_start(&self, turbo_tasks: &dyn TurboTasksBackendApi