From bf11e75ad507996eddf60b4cdf82f1c134d4f60b Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Fri, 6 Mar 2026 22:34:43 +0100 Subject: [PATCH] chore: Remove workspace root local dependencies --- .config/eslintignore.mjs | 1 + eslint.config.mjs | 2 +- package.json | 22 +- packages/font/src/google/loader.ts | 2 +- .../get-fallback-metrics-from-font-file.ts | 2 +- packages/font/src/local/loader.ts | 2 +- packages/font/src/types.ts | 31 +++ packages/next/font/index.d.ts | 34 +-- packages/next/next-devtools.webpack-config.js | 25 +- .../console-dim.external.test.ts | 84 ++++--- .../unhandled-rejection.external.test.ts | 222 ++++++++++++------ packages/third-parties/package.json | 2 +- pnpm-lock.yaml | 101 ++------ pnpm-workspace.yaml | 1 + scripts/create-next-self-symlink.mjs | 19 ++ scripts/validate-externals-doc.js | 2 +- test/.gitignore | 2 +- test/package.json | 10 + 18 files changed, 310 insertions(+), 254 deletions(-) create mode 100644 scripts/create-next-self-symlink.mjs create mode 100644 test/package.json diff --git a/.config/eslintignore.mjs b/.config/eslintignore.mjs index 62407c08a66e..d1c854e30123 100644 --- a/.config/eslintignore.mjs +++ b/.config/eslintignore.mjs @@ -53,6 +53,7 @@ export default globalIgnores([ 'test/e2e/app-dir/server-source-maps/fixtures/default/external-pkg/sourcemapped.js', 'test/development/next-lint-eslint-formatter-compact/**/*.js', 'test/e2e/app-dir/app-external/app/mixed/import/mixed-mod.mjs', + 'test/e2e/babel/fixture/lib/mycomponent.js', 'turbopack/crates/*/tests/**/*', 'turbopack/crates/*/js/src/compiled', // Eval fixtures are deliberately imperfect code for agents to fix. diff --git a/eslint.config.mjs b/eslint.config.mjs index 3ff56f0429d2..827154dbddfc 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -60,7 +60,7 @@ export default defineConfig([ jsx: true, }, babelOptions: { - presets: ['next/babel'], + presets: ['./packages/next/babel'], caller: { supportsTopLevelAwait: true, }, diff --git a/package.json b/package.json index 9aeeb06fecd7..67ce7f782b3b 100644 --- a/package.json +++ b/package.json @@ -79,12 +79,13 @@ "check-precompiled": "./scripts/check-pre-compiled.sh", "lint-staged": "lint-staged", "next-with-deps": "./scripts/next-with-deps.sh", - "next": "cross-env NEXT_PRIVATE_LOCAL_DEV=1 NEXT_TELEMETRY_DISABLED=1 NODE_OPTIONS=\"--trace-deprecation --enable-source-maps\" next", + "create-next-app": "packages/create-next-app/dist/index.js", + "next": "cross-env NEXT_PRIVATE_LOCAL_DEV=1 NEXT_TELEMETRY_DISABLED=1 NODE_OPTIONS=\"--trace-deprecation --enable-source-maps\" packages/next/dist/bin/next", "next-no-sourcemaps": "echo 'No longer supported. Use `pnpm next --disable-source-maps` instead'; exit 1;", "debug": "cross-env NEXT_PRIVATE_LOCAL_DEV=1 NEXT_TELEMETRY_DISABLED=1 node --inspect --trace-deprecation --enable-source-maps packages/next/dist/bin/next", "debug-brk": "cross-env NEXT_PRIVATE_LOCAL_DEV=1 NEXT_TELEMETRY_DISABLED=1 node --inspect-brk --trace-deprecation --enable-source-maps packages/next/dist/bin/next", "pnpm:devPreinstall": "node scripts/create-next-bin-placeholder.mjs", - "postinstall": "node scripts/git-configure.mjs && node scripts/install-native.mjs", + "postinstall": "node scripts/git-configure.mjs && node scripts/install-native.mjs && node scripts/create-next-self-symlink.mjs", "version": "pnpm install --no-frozen-lockfile && git add .", "prepare": "husky", "sync-react": "node ./scripts/sync-react.js", @@ -126,18 +127,7 @@ "@jest/expect-utils": "29.7.0", "@mdx-js/loader": "2.2.1", "@mdx-js/react": "2.2.1", - "@next/bundle-analyzer": "workspace:*", - "@next/env": "workspace:*", - "@next/eslint-plugin-next": "workspace:*", - "@next/font": "workspace:*", - "@next/mdx": "workspace:*", - "@next/playwright": "workspace:*", - "@next/plugin-storybook": "workspace:*", - "@next/polyfill-module": "workspace:*", - "@next/polyfill-nomodule": "workspace:*", - "@next/swc": "workspace:*", - "@next/third-parties": "workspace:*", - "@opentelemetry/api": "1.4.1", + "@opentelemetry/api": "1.6.0", "@picocss/pico": "1.5.10", "@rspack/core": "1.6.7", "@slack/web-api": "7.9.1", @@ -168,7 +158,6 @@ "@types/yargs": "16.0.11", "@vercel/agent-eval": "0.9.5", "@vercel/blob": "2.3.2", - "@vercel/devlow-bench": "workspace:*", "@vercel/kv": "3.0.0", "@vercel/og": "0.11.1", "abort-controller": "3.0.0", @@ -181,7 +170,6 @@ "cookie": "0.4.1", "cors": "2.8.5", "coveralls": "3.0.3", - "create-next-app": "workspace:*", "critters": "0.0.6", "cross-env": "6.0.3", "cross-spawn": "6.0.5", @@ -189,7 +177,6 @@ "es5-ext": "0.10.53", "escape-string-regexp": "2.0.0", "eslint": "9.37.0", - "eslint-config-next": "workspace:*", "eslint-formatter-codeframe": "7.32.1", "eslint-plugin-eslint-plugin": "5.2.1", "eslint-plugin-import": "2.31.0", @@ -237,7 +224,6 @@ "minimatch": "3.0.4", "moment": "^2.24.0", "nanoid": "3.1.30", - "next": "workspace:*", "node-fetch": "2.6.7", "node-plop": "0.31.1", "npm-run-all": "4.1.5", diff --git a/packages/font/src/google/loader.ts b/packages/font/src/google/loader.ts index 9e57e064e618..69c3d8b34927 100644 --- a/packages/font/src/google/loader.ts +++ b/packages/font/src/google/loader.ts @@ -1,4 +1,4 @@ -import type { AdjustFontFallback, FontLoader } from 'next/font' +import type { AdjustFontFallback, FontLoader } from '../types' // @ts-ignore import * as Log from 'next/dist/build/output/log' import { validateGoogleFontFunctionCall } from './validate-google-font-function-call' diff --git a/packages/font/src/local/get-fallback-metrics-from-font-file.ts b/packages/font/src/local/get-fallback-metrics-from-font-file.ts index eb4891f8c4bf..cea19e7a3479 100644 --- a/packages/font/src/local/get-fallback-metrics-from-font-file.ts +++ b/packages/font/src/local/get-fallback-metrics-from-font-file.ts @@ -1,5 +1,5 @@ import type { Font } from 'fontkit' -import type { AdjustFontFallback } from 'next/font' +import type { AdjustFontFallback } from '../types' // The font metadata of the fallback fonts, retrieved with fontkit on system font files // The average width is calculated with the calcAverageWidth function below diff --git a/packages/font/src/local/loader.ts b/packages/font/src/local/loader.ts index b9a814b7c1b6..1a25c1fd0807 100644 --- a/packages/font/src/local/loader.ts +++ b/packages/font/src/local/loader.ts @@ -5,7 +5,7 @@ try { const mod = require('../fontkit').default fontFromBuffer = mod.default || mod } catch {} -import type { AdjustFontFallback, FontLoader } from 'next/font' +import type { AdjustFontFallback, FontLoader } from '../types' import { promisify } from 'util' import { pickFontFileForFallbackGeneration } from './pick-font-file-for-fallback-generation' diff --git a/packages/font/src/types.ts b/packages/font/src/types.ts index 11e7c06e9a45..5b151f8b85ad 100644 --- a/packages/font/src/types.ts +++ b/packages/font/src/types.ts @@ -1,3 +1,34 @@ +export type AdjustFontFallback = { + fallbackFont: string + ascentOverride?: string + descentOverride?: string + lineGapOverride?: string + sizeAdjust?: string +} + +export type FontLoader = (options: { + functionName: string + variableName: string + data: any[] + emitFontFile: ( + content: Buffer, + ext: string, + preload: boolean, + isUsingSizeAdjust?: boolean + ) => string + resolve: (src: string) => string + isDev: boolean + isServer: boolean + loaderContext: any +}) => Promise<{ + css: string + fallbackFonts?: string[] + variable?: string + adjustFontFallback?: AdjustFontFallback + weight?: string + style?: string +}> + export type CssVariable = `--${string}` export type Display = 'auto' | 'block' | 'swap' | 'fallback' | 'optional' diff --git a/packages/next/font/index.d.ts b/packages/next/font/index.d.ts index fe10e31d59b9..96588bc02fd0 100644 --- a/packages/next/font/index.d.ts +++ b/packages/next/font/index.d.ts @@ -1,30 +1,4 @@ -export type AdjustFontFallback = { - fallbackFont: string - ascentOverride?: string - descentOverride?: string - lineGapOverride?: string - sizeAdjust?: string -} - -export type FontLoader = (options: { - functionName: string - variableName: string - data: any[] - emitFontFile: ( - content: Buffer, - ext: string, - preload: boolean, - isUsingSizeAdjust?: boolean - ) => string - resolve: (src: string) => string - isDev: boolean - isServer: boolean - loaderContext: any -}) => Promise<{ - css: string - fallbackFonts?: string[] - variable?: string - adjustFontFallback?: AdjustFontFallback - weight?: string - style?: string -}> +export { + AdjustFontFallback, + FontLoader, +} from 'next/dist/compiled/@next/font/dist/types' diff --git a/packages/next/next-devtools.webpack-config.js b/packages/next/next-devtools.webpack-config.js index 9bbcd58f5379..e1c989004a7c 100644 --- a/packages/next/next-devtools.webpack-config.js +++ b/packages/next/next-devtools.webpack-config.js @@ -58,11 +58,26 @@ module.exports = ({ dev, ...rest }) => { resolve: { alias: { // TODO: Get dedicated React version for NDT to uncouple development. - react: `next/dist/compiled/react${bundledReactChannel}`, - 'react-dom$': `next/dist/compiled/react-dom${bundledReactChannel}`, - 'react-dom/client$': `next/dist/compiled/react-dom${bundledReactChannel}/client`, - 'react-is$': `next/dist/compiled/react-is${bundledReactChannel}`, - scheduler$: `next/dist/compiled/scheduler${bundledReactChannel}`, + react: path.join( + __dirname, + `dist/compiled/react${bundledReactChannel}` + ), + 'react-dom$': path.join( + __dirname, + `dist/compiled/react-dom${bundledReactChannel}` + ), + 'react-dom/client$': path.join( + __dirname, + `dist/compiled/react-dom${bundledReactChannel}/client` + ), + 'react-is$': path.join( + __dirname, + `dist/compiled/react-is${bundledReactChannel}` + ), + scheduler$: path.join( + __dirname, + `dist/compiled/scheduler${bundledReactChannel}` + ), }, extensions: ['.ts', '.tsx', '.js', '.json'], }, diff --git a/packages/next/src/server/node-environment-extensions/console-dim.external.test.ts b/packages/next/src/server/node-environment-extensions/console-dim.external.test.ts index a8254eeb10d7..10e672a57ed7 100644 --- a/packages/next/src/server/node-environment-extensions/console-dim.external.test.ts +++ b/packages/next/src/server/node-environment-extensions/console-dim.external.test.ts @@ -33,6 +33,7 @@ declare global { } import { Worker } from 'node:worker_threads' +import path from '../../shared/lib/isomorphic/path' type WorkerResult = { exitCode: number @@ -42,10 +43,12 @@ type WorkerResult = { messages: Array } -export function runWorkerCode(fn: Function): Promise { +export function runWorkerCode( + fn: (nextDir: string) => Promise +): Promise { return new Promise((resolve, reject) => { const script = ` - const { parentPort } = require('node:worker_threads'); + const { parentPort, workerData } = require('node:worker_threads'); (async () => { const { AsyncLocalStorage } = require('node:async_hooks'); // We need to put this on the global because Next.js does not import it @@ -58,7 +61,7 @@ export function runWorkerCode(fn: Function): Promise { const fn = (${fn.toString()}); try { - const out = await fn(); + const out = await fn(workerData.nextDir); await new Promise(r => setImmediate(r)); reportResult({ type: 'result', out }); } catch (e) { @@ -69,7 +72,7 @@ export function runWorkerCode(fn: Function): Promise { const w = new Worker(script, { eval: true, - workerData: null, + workerData: { nextDir: path.resolve(__dirname, '../../..') }, argv: [], execArgv: ['--conditions=react-server'], stderr: true, @@ -118,10 +121,10 @@ export function runWorkerCode(fn: Function): Promise { describe('console-exit patches', () => { describe('basic functionality', () => { it('should patch console methods to dim when instructed', async () => { - async function testForWorker() { - const { - consoleAsyncStorage, - } = require('next/dist/server/app-render/console-async-storage.external') + async function testForWorker(nextDir: string) { + const { consoleAsyncStorage } = require( + nextDir + '/dist/server/app-render/console-async-storage.external' + ) // First, replace console.log to track what storage context it runs in console.log = function (...args) { @@ -141,7 +144,10 @@ describe('console-exit patches', () => { } // Install patches - this wraps the current console.log - require('next/dist/server/node-environment-extensions/console-dim.external') + require( + nextDir + + '/dist/server/node-environment-extensions/console-dim.external' + ) // Test outside storage context console.log('outside') @@ -163,13 +169,16 @@ describe('console-exit patches', () => { }) it('should not wrap console methods assigned after patching', async () => { - async function testForWorker() { - const { - consoleAsyncStorage, - } = require('next/dist/server/app-render/console-async-storage.external') + async function testForWorker(nextDir: string) { + const { consoleAsyncStorage } = require( + nextDir + '/dist/server/app-render/console-async-storage.external' + ) // Install patches first - require('next/dist/server/node-environment-extensions/console-dim.external') + require( + nextDir + + '/dist/server/node-environment-extensions/console-dim.external' + ) // Assign a new console.log after patching - this will NOT be wrapped console.log = function (...args) { @@ -207,7 +216,7 @@ describe('console-exit patches', () => { }) it('should preserve function properties and behavior', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { reportResult({ type: 'serialized', key: 'originalName', @@ -221,7 +230,10 @@ describe('console-exit patches', () => { }) // install patch - require('next/dist/server/node-environment-extensions/console-dim.external') + require( + nextDir + + '/dist/server/node-environment-extensions/console-dim.external' + ) // Test that patched methods preserve name and other properties reportResult({ @@ -246,10 +258,10 @@ describe('console-exit patches', () => { }) it('should enter a dimming context when a prerender store exists with an aborted renderSignal', async () => { - async function testForWorker() { - const { - workUnitAsyncStorage, - } = require('next/dist/server/app-render/work-unit-async-storage.external') + async function testForWorker(nextDir: string) { + const { workUnitAsyncStorage } = require( + nextDir + '/dist/server/app-render/work-unit-async-storage.external' + ) // First, replace console.log to track what storage context it runs in console.log = function (...args) { @@ -269,7 +281,10 @@ describe('console-exit patches', () => { } // Install patches - this wraps the current console.log - require('next/dist/server/node-environment-extensions/console-dim.external') + require( + nextDir + + '/dist/server/node-environment-extensions/console-dim.external' + ) // Test outside storage context console.log('outside') @@ -298,7 +313,7 @@ describe('console-exit patches', () => { }) it('should enter a dimming context when a react cacheSignal exists and is aborted', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { const originalLog = console.log let controller: null | AbortController = new AbortController() @@ -321,12 +336,14 @@ describe('console-exit patches', () => { } // Install patches - this wraps the current console.log - require('next/dist/server/node-environment-extensions/console-dim.external') + require( + nextDir + + '/dist/server/node-environment-extensions/console-dim.external' + ) - const { - registerServerReact, - registerClientReact, - } = require('next/dist/server/runtime-reacts.external') + const { registerServerReact, registerClientReact } = require( + nextDir + '/dist/server/runtime-reacts.external' + ) registerServerReact({ cacheSignal() { @@ -360,20 +377,23 @@ describe('console-exit patches', () => { describe('inspector-aware dimming', () => { it('should skip dimming when inspector is open', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { const nodeInspector = require('node:inspector') nodeInspector.open(0) - const { - consoleAsyncStorage, - } = require('next/dist/server/app-render/console-async-storage.external') + const { consoleAsyncStorage } = require( + nextDir + '/dist/server/app-render/console-async-storage.external' + ) const capturedCalls: Array<{ args: any[] }> = [] console.error = function (...args) { capturedCalls.push({ args }) } - require('next/dist/server/node-environment-extensions/console-dim.external') + require( + nextDir + + '/dist/server/node-environment-extensions/console-dim.external' + ) consoleAsyncStorage.run({ dim: true }, () => { console.error('prefix', { key: 'value' }, 42) diff --git a/packages/next/src/server/node-environment-extensions/unhandled-rejection.external.test.ts b/packages/next/src/server/node-environment-extensions/unhandled-rejection.external.test.ts index 87ceb59494c5..117ad9bff11c 100644 --- a/packages/next/src/server/node-environment-extensions/unhandled-rejection.external.test.ts +++ b/packages/next/src/server/node-environment-extensions/unhandled-rejection.external.test.ts @@ -19,8 +19,6 @@ * @jest-environment node */ -/* eslint-disable @next/internal/typechecked-require */ - type ReportableResult = | CountReport | UHRReport @@ -52,6 +50,7 @@ declare global { function reportResult(result: ReportableResult): void } +import path from '../../shared/lib/isomorphic/path' import type { WorkUnitStore } from '../app-render/work-unit-async-storage.external' import { Worker } from 'node:worker_threads' @@ -66,24 +65,25 @@ type WorkerResult = { messages: Array } -export function runWorkerCode(fn: Function): Promise { +export function runWorkerCode( + fn: (nextDir: string) => Promise +): Promise { return new Promise((resolve, reject) => { const script = ` - const { parentPort } = require('node:worker_threads'); + const { parentPort, workerData } = require('node:worker_threads'); (async () => { const { AsyncLocalStorage } = require('node:async_hooks'); // We need to put this on the global because Next.js does not import it // from node directly to be compatible with edge runtimes. globalThis.AsyncLocalStorage = AsyncLocalStorage; - const { parentPort } = require('node:worker_threads'); global.reportResult = (value) => { parentPort?.postMessage(value); }; const fn = (${fn.toString()}); try { - const out = await fn(); + const out = await fn(workerData.nextDir); await new Promise(r => setImmediate(r)); reportResult({ type: 'result', out }); } catch (e) { @@ -94,7 +94,7 @@ export function runWorkerCode(fn: Function): Promise { const w = new Worker(script, { eval: true, - workerData: null, + workerData: { nextDir: path.resolve(__dirname, '../../..') }, argv: [], execArgv: [], stderr: true, @@ -147,8 +147,11 @@ export function runWorkerCode(fn: Function): Promise { describe('unhandled-rejection filter', () => { describe('environment variable configuration', () => { it('should install filter by default', async () => { - async function testForWorker() { - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + async function testForWorker(nextDir: string) { + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) reportResult({ type: 'count', @@ -165,9 +168,12 @@ describe('unhandled-rejection filter', () => { }) it('should not install filter when disabled', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'disabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) reportResult({ type: 'count', @@ -184,9 +190,12 @@ describe('unhandled-rejection filter', () => { }) it('should install filter rejections when environment variable is enabled', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'enabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) reportResult({ type: 'count', @@ -203,9 +212,12 @@ describe('unhandled-rejection filter', () => { }) it('should install filter rejections when environment variable is enabled in debug mode', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'debug' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) reportResult({ type: 'count', @@ -222,14 +234,17 @@ describe('unhandled-rejection filter', () => { }) it('should warn once when you uninstall the filter with removeListener', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { const originalWarn = console.warn console.warn = (...args: Array) => { reportResult({ type: 'error-log', message: args.join(' ') }) originalWarn(...args) } - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) const filterListener = process.listeners('unhandledRejection')[0] process.removeListener('unhandledRejection', filterListener) @@ -251,14 +266,17 @@ describe('unhandled-rejection filter', () => { }) it('should warn once when you uninstall the filter with off', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { const originalWarn = console.warn console.warn = (...args: Array) => { reportResult({ type: 'error-log', message: args.join(' ') }) originalWarn(...args) } - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) const filterListener = process.listeners('unhandledRejection')[0] process.off('unhandledRejection', filterListener) @@ -280,14 +298,17 @@ describe('unhandled-rejection filter', () => { }) it('should warn once when you uninstall the filter with removeAllListeners', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { const originalWarn = console.warn console.warn = (...args: Array) => { reportResult({ type: 'error-log', message: args.join(' ') }) originalWarn(...args) } - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) const filterListener = process.listeners('unhandledRejection')[0] process.removeAllListeners() @@ -309,7 +330,7 @@ describe('unhandled-rejection filter', () => { }) it('does not warn when environment variable is set to silent mode', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'silent' const originalWarn = console.warn console.warn = (...args: Array) => { @@ -317,7 +338,10 @@ describe('unhandled-rejection filter', () => { originalWarn(...args) } - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) const filterListener = process.listeners('unhandledRejection')[0] process.removeAllListeners() @@ -333,13 +357,16 @@ describe('unhandled-rejection filter', () => { describe('filtering functionality', () => { it('should suppress rejections from aborted prerender contexts', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = '1' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) - const { - workUnitAsyncStorage, - } = require('next/dist/server/app-render/work-unit-async-storage.external') + const { workUnitAsyncStorage } = require( + nextDir + '/dist/server/app-render/work-unit-async-storage.external' + ) process.on('unhandledRejection', (reason) => { reportResult({ type: 'uhr', reason: String(reason) }) @@ -401,13 +428,16 @@ describe('unhandled-rejection filter', () => { }) it('should suppress rejections from aborted prerender-client contexts', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = '1' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) - const { - workUnitAsyncStorage, - } = require('next/dist/server/app-render/work-unit-async-storage.external') + const { workUnitAsyncStorage } = require( + nextDir + '/dist/server/app-render/work-unit-async-storage.external' + ) process.on('unhandledRejection', (reason) => { reportResult({ type: 'uhr', reason: String(reason) }) @@ -469,13 +499,16 @@ describe('unhandled-rejection filter', () => { }) it('should suppress rejections from aborted prerender-runtime contexts', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = '1' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) - const { - workUnitAsyncStorage, - } = require('next/dist/server/app-render/work-unit-async-storage.external') + const { workUnitAsyncStorage } = require( + nextDir + '/dist/server/app-render/work-unit-async-storage.external' + ) process.on('unhandledRejection', (reason) => { reportResult({ type: 'uhr', reason: String(reason) }) @@ -537,13 +570,16 @@ describe('unhandled-rejection filter', () => { }) it('should pass through rejections from non-aborted prerender contexts', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = '1' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) - const { - workUnitAsyncStorage, - } = require('next/dist/server/app-render/work-unit-async-storage.external') + const { workUnitAsyncStorage } = require( + nextDir + '/dist/server/app-render/work-unit-async-storage.external' + ) process.on('unhandledRejection', (reason) => { reportResult({ type: 'uhr', reason: String(reason) }) @@ -580,9 +616,12 @@ describe('unhandled-rejection filter', () => { }) it('should call console.error when no handlers are present', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = '1' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) console.error = (...args: Array) => { reportResult({ type: 'error-log', message: args.join(' ') }) @@ -601,9 +640,12 @@ describe('unhandled-rejection filter', () => { describe('process method interception', () => { it('should handle process.once listeners correctly', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'enabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) let callCount = 0 @@ -636,9 +678,12 @@ describe('unhandled-rejection filter', () => { }) it('should handle process.removeListener correctly', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'enabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) const handler1 = (reason: unknown) => { reportResult({ type: 'uhr', reason: `[1]: ${String(reason)}` }) @@ -706,13 +751,16 @@ describe('unhandled-rejection filter', () => { }) it('should uninstall filter when removeAllListeners() is called without arguments', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'enabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) - const { - workUnitAsyncStorage, - } = require('next/dist/server/app-render/work-unit-async-storage.external') + const { workUnitAsyncStorage } = require( + nextDir + '/dist/server/app-render/work-unit-async-storage.external' + ) process.on('unhandledRejection', (reason) => { reportResult({ type: 'uhr', reason: String(reason) }) @@ -768,13 +816,16 @@ describe('unhandled-rejection filter', () => { }) it('should not uninstall filter when removeAllListeners("unhandledRejection") is called', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'enabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) - const { - workUnitAsyncStorage, - } = require('next/dist/server/app-render/work-unit-async-storage.external') + const { workUnitAsyncStorage } = require( + nextDir + '/dist/server/app-render/work-unit-async-storage.external' + ) process.on('unhandledRejection', (reason) => { reportResult({ type: 'uhr', reason: String(reason) }) @@ -827,9 +878,12 @@ describe('unhandled-rejection filter', () => { // This test asserts that our patch preserves node's semantics that all listeners registered when // an event is emitted will be invoked regardless of whether there are mutations to the listeners // during event handling. - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'enabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) const onceHandler = (reason: unknown) => { reportResult({ type: 'uhr', reason: `once: ${String(reason)}` }) @@ -918,7 +972,7 @@ describe('unhandled-rejection filter', () => { }) it('should preserve native function toString behavior for patched process methods', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { const originalMethods = [ process.on, process.addListener, @@ -935,7 +989,10 @@ describe('unhandled-rejection filter', () => { const originalNames = originalMethods.map((m) => m.name) process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'enabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) const patchedMethods = [ process.on, @@ -993,13 +1050,16 @@ describe('unhandled-rejection filter', () => { describe('error handling in handlers', () => { it('should handle errors thrown by user handlers gracefully', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'enabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) - const { - workUnitAsyncStorage, - } = require('next/dist/server/app-render/work-unit-async-storage.external') + const { workUnitAsyncStorage } = require( + nextDir + '/dist/server/app-render/work-unit-async-storage.external' + ) process.on('unhandledRejection', () => { throw new Error('Handler error') @@ -1032,18 +1092,21 @@ describe('unhandled-rejection filter', () => { describe('integration with existing listeners', () => { it('should capture and preserve existing listeners during installation', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { // Add listener before installing filter process.on('unhandledRejection', (reason) => { reportResult({ type: 'uhr', reason: `existing: ${String(reason)}` }) }) process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'enabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) - const { - workUnitAsyncStorage, - } = require('next/dist/server/app-render/work-unit-async-storage.external') + const { workUnitAsyncStorage } = require( + nextDir + '/dist/server/app-render/work-unit-async-storage.external' + ) // Test non-filtered rejection workUnitAsyncStorage.run( @@ -1074,14 +1137,17 @@ describe('unhandled-rejection filter', () => { }) it('should be able to clear listeners that existed prior to installation', async () => { - async function testForWorker() { + async function testForWorker(nextDir: string) { // Add listener before installing filter process.on('unhandledRejection', (reason) => { reportResult({ type: 'uhr', reason: `existing: ${String(reason)}` }) }) process.env.NEXT_UNHANDLED_REJECTION_FILTER = 'enabled' - require('next/dist/server/node-environment-extensions/unhandled-rejection.external') + require( + nextDir + + '/dist/server/node-environment-extensions/unhandled-rejection.external' + ) process.removeAllListeners('unhandledRejection') @@ -1089,9 +1155,9 @@ describe('unhandled-rejection filter', () => { reportResult({ type: 'uhr', reason: `after: ${String(reason)}` }) }) - const { - workUnitAsyncStorage, - } = require('next/dist/server/app-render/work-unit-async-storage.external') + const { workUnitAsyncStorage } = require( + nextDir + '/dist/server/app-render/work-unit-async-storage.external' + ) // Test non-filtered rejection workUnitAsyncStorage.run( diff --git a/packages/third-parties/package.json b/packages/third-parties/package.json index 1ac955f4cd1d..cd7f0a225d8f 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -26,7 +26,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "16.3.0-canary.58", + "next": "workspace:*", "outdent": "0.8.0", "prettier": "2.5.1", "typescript": "6.0.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5fd0698f32fc..a98508a89d41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -123,42 +123,9 @@ importers: '@mdx-js/react': specifier: 2.2.1 version: 2.2.1(react@19.3.0-canary-b1786c31-20260618) - '@next/bundle-analyzer': - specifier: workspace:* - version: link:packages/next-bundle-analyzer - '@next/env': - specifier: workspace:* - version: link:packages/next-env - '@next/eslint-plugin-next': - specifier: workspace:* - version: link:packages/eslint-plugin-next - '@next/font': - specifier: workspace:* - version: link:packages/font - '@next/mdx': - specifier: workspace:* - version: link:packages/next-mdx - '@next/playwright': - specifier: workspace:* - version: link:packages/next-playwright - '@next/plugin-storybook': - specifier: workspace:* - version: link:packages/next-plugin-storybook - '@next/polyfill-module': - specifier: workspace:* - version: link:packages/next-polyfill-module - '@next/polyfill-nomodule': - specifier: workspace:* - version: link:packages/next-polyfill-nomodule - '@next/swc': - specifier: workspace:* - version: link:packages/next-swc - '@next/third-parties': - specifier: workspace:* - version: link:packages/third-parties '@opentelemetry/api': - specifier: 1.4.1 - version: 1.4.1 + specifier: 1.6.0 + version: 1.6.0 '@picocss/pico': specifier: 1.5.10 version: 1.5.10 @@ -249,9 +216,6 @@ importers: '@vercel/blob': specifier: 2.3.2 version: 2.3.2(patch_hash=cb53bfa5effb15b3a361e176003df667cadf757b27b7c9b458c2f0fce86ea893) - '@vercel/devlow-bench': - specifier: workspace:* - version: link:turbopack/packages/devlow-bench '@vercel/kv': specifier: 3.0.0 version: 3.0.0 @@ -288,9 +252,6 @@ importers: coveralls: specifier: 3.0.3 version: 3.0.3 - create-next-app: - specifier: workspace:* - version: link:packages/create-next-app critters: specifier: 0.0.6 version: 0.0.6 @@ -312,9 +273,6 @@ importers: eslint: specifier: 9.37.0 version: 9.37.0(jiti@2.6.1) - eslint-config-next: - specifier: workspace:* - version: link:packages/eslint-config-next eslint-formatter-codeframe: specifier: 7.32.1 version: 7.32.1 @@ -456,9 +414,6 @@ importers: nanoid: specifier: 3.1.30 version: 3.1.30 - next: - specifier: workspace:* - version: link:packages/next node-fetch: specifier: 2.6.7 version: 2.6.7(encoding@0.1.13) @@ -1936,7 +1891,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 16.3.0-canary.58 + specifier: workspace:* version: link:../next outdent: specifier: 0.8.0 @@ -1948,6 +1903,18 @@ importers: specifier: 6.0.2 version: 6.0.2 + test: + devDependencies: + '@next/mdx': + specifier: workspace:* + version: link:../packages/next-mdx + '@next/playwright': + specifier: workspace:* + version: link:../packages/next-playwright + next: + specifier: workspace:* + version: link:../packages/next + turbopack/crates/turbopack-cli/js: dependencies: '@vercel/turbopack-ecmascript-runtime': @@ -4823,10 +4790,6 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} - '@opentelemetry/api@1.4.1': - resolution: {integrity: sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==} - engines: {node: '>=8.0.0'} - '@opentelemetry/api@1.6.0': resolution: {integrity: sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g==} engines: {node: '>=8.0.0'} @@ -8251,10 +8214,6 @@ packages: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} - cli-cursor@4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -12868,10 +12827,6 @@ packages: resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} engines: {node: '>=10'} - log-update@6.0.0: - resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==} - engines: {node: '>=18'} - log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} @@ -16046,10 +16001,6 @@ packages: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} - restore-cursor@4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -17997,6 +17948,7 @@ packages: uuid@10.0.0: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true uuid@11.1.0: @@ -21849,8 +21801,6 @@ snapshots: '@open-draft/until@2.1.0': {} - '@opentelemetry/api@1.4.1': {} - '@opentelemetry/api@1.6.0': {} '@opentelemetry/api@1.9.0': {} @@ -25580,10 +25530,6 @@ snapshots: dependencies: restore-cursor: 3.1.0 - cli-cursor@4.0.0: - dependencies: - restore-cursor: 4.0.0 - cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -31241,7 +31187,7 @@ snapshots: cli-truncate: 4.0.0 colorette: 2.0.20 eventemitter3: 5.0.1 - log-update: 6.0.0 + log-update: 6.1.0 rfdc: 1.3.0 wrap-ansi: 9.0.0 @@ -31440,14 +31386,6 @@ snapshots: slice-ansi: 4.0.0 wrap-ansi: 6.2.0 - log-update@6.0.0: - dependencies: - ansi-escapes: 6.2.1 - cli-cursor: 4.0.0 - slice-ansi: 7.1.0 - strip-ansi: 7.1.0 - wrap-ansi: 9.0.0 - log-update@6.1.0: dependencies: ansi-escapes: 7.3.0 @@ -35465,11 +35403,6 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 - restore-cursor@4.0.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - restore-cursor@5.1.0: dependencies: onetime: 7.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1aae278670be..83286b631579 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,6 +6,7 @@ packages: - 'turbopack/crates/*/js' - 'turbopack/crates/turbopack-tests/tests/execution' - 'turbopack/packages/*' + - 'test' updateNotifier: false publicHoistPattern: - '*eslint*' diff --git a/scripts/create-next-self-symlink.mjs b/scripts/create-next-self-symlink.mjs new file mode 100644 index 000000000000..7fb7f73e5317 --- /dev/null +++ b/scripts/create-next-self-symlink.mjs @@ -0,0 +1,19 @@ +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const scriptDir = path.dirname(fileURLToPath(import.meta.url)) +const repoRoot = path.join(scriptDir, '..') + +const selfLinkPath = path.join( + repoRoot, + 'packages', + 'next', + 'node_modules', + 'next' +) +const selfLinkTarget = path.join(repoRoot, 'packages', 'next') +// Remove any existing symlink or directory before (re-)creating +fs.rmSync(selfLinkPath, { force: true }) +fs.symlinkSync(selfLinkTarget, selfLinkPath, 'junction') +console.log(`Created self-link: ${selfLinkPath} -> ${selfLinkTarget}`) diff --git a/scripts/validate-externals-doc.js b/scripts/validate-externals-doc.js index e1a64b34c81b..8a0ce515131f 100644 --- a/scripts/validate-externals-doc.js +++ b/scripts/validate-externals-doc.js @@ -1,6 +1,6 @@ const fs = require('fs') const path = require('path') -const JSON5 = require('next/dist/compiled/json5') +const JSON5 = require('../packages/next/dist/compiled/json5') const serverExternals = JSON5.parse( fs.readFileSync( diff --git a/test/.gitignore b/test/.gitignore index 5782cc142201..6c44575b170e 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1,4 +1,4 @@ -!node_modules +!*/**/node_modules .vscode e2e/**/tsconfig.json diff --git a/test/package.json b/test/package.json new file mode 100644 index 000000000000..f9458945e3c2 --- /dev/null +++ b/test/package.json @@ -0,0 +1,10 @@ +{ + "name": "@next/internal-test", + "version": "16.0.2-canary.16", + "private": true, + "devDependencies": { + "@next/mdx": "workspace:*", + "@next/playwright": "workspace:*", + "next": "workspace:*" + } +}