From cee784dea0a4dffcf8f09261cfe2412f2df4f0d2 Mon Sep 17 00:00:00 2001 From: Luke Sandberg Date: Mon, 4 Aug 2025 16:16:57 -0700 Subject: [PATCH 1/7] [turbopack] vibecode a benchmark runner for module-cost (#82287) # Add automated benchmark runner for module-cost ## What? This PR adds an automated benchmark runner for the module-cost benchmark, allowing for consistent measurement of module loading and execution times. ## Why? It is to tedious and error prone to run the benchmark. ## Usage Build `next` and turbopack however you like ``` pnpm i pnpm prepare-bench pnpm build-webpack (or build-turbopack) pnpm benchmark ``` ## How? v0 mostly --- bench/module-cost/.gitignore | 3 +- bench/module-cost/components/client.js | 5 + bench/module-cost/package.json | 4 +- .../module-cost/scripts/benchmark-runner.mjs | 255 ++++++++++++++++++ pnpm-lock.yaml | 3 + 5 files changed, 268 insertions(+), 2 deletions(-) create mode 100644 bench/module-cost/scripts/benchmark-runner.mjs diff --git a/bench/module-cost/.gitignore b/bench/module-cost/.gitignore index fd0a8a0a91b50..b1d3827455c44 100644 --- a/bench/module-cost/.gitignore +++ b/bench/module-cost/.gitignore @@ -1,3 +1,4 @@ commonjs/* esm/* -CPU* \ No newline at end of file +CPU* +benchmark-results-*.json \ No newline at end of file diff --git a/bench/module-cost/components/client.js b/bench/module-cost/components/client.js index 967e20a312fa9..f8118beb353a2 100644 --- a/bench/module-cost/components/client.js +++ b/bench/module-cost/components/client.js @@ -4,6 +4,11 @@ import { useEffect, useRef, useState } from 'react' import { format, measure } from '../lib/measure' function report(result, element, textarea) { + if (!globalThis.BENCHMARK_RESULTS) { + globalThis.BENCHMARK_RESULTS = [] + } + globalThis.BENCHMARK_RESULTS.push(result) + const formattedResult = format(result) element.textContent += `: ${formattedResult}` textarea.current.value += `\n ${formattedResult}` diff --git a/bench/module-cost/package.json b/bench/module-cost/package.json index d50bd133aaf55..eb6e8a7478d92 100644 --- a/bench/module-cost/package.json +++ b/bench/module-cost/package.json @@ -2,6 +2,7 @@ "name": "module-cost", "scripts": { "prepare-bench": "node scripts/prepare-bench.mjs", + "benchmark": "node scripts/benchmark-runner.mjs", "dev-webpack": "next dev", "dev-turbopack": "next dev --turbo", "build-webpack": "next build", @@ -10,6 +11,7 @@ }, "devDependencies": { "rimraf": "6.0.1", - "next": "workspace:*" + "next": "workspace:*", + "playwright": "^1.40.0" } } diff --git a/bench/module-cost/scripts/benchmark-runner.mjs b/bench/module-cost/scripts/benchmark-runner.mjs new file mode 100644 index 0000000000000..eb96b27ce3571 --- /dev/null +++ b/bench/module-cost/scripts/benchmark-runner.mjs @@ -0,0 +1,255 @@ +import { spawn } from 'node:child_process' +import { writeFileSync } from 'node:fs' +import { chromium } from 'playwright' + +/// To use: +/// - Install Playwright: `npx playwright install chromium` +/// - Install dependencies: `pnpm install` +/// - Build the application: `pnpm build-webpack` or pnpm build-turbopack` +/// - Run the benchmark: `pnpm benchmark` + +class BenchmarkRunner { + constructor(options) { + this.name = options.name + this.samples = options.samples ?? 50 + this.buttonClickDelay = options.buttonClickDelay ?? 500 + this.results = [] + } + + async runBenchmark() { + for (let i = 1; i <= this.samples; i++) { + console.log(`\n--- Running sample ${i}/${this.samples} ---`) + + const result = await this.runSingleSample() + this.results.push(...result) + } + + this.saveResults() + console.log('\nBenchmark completed!') + } + + async runSingleSample() { + let server + let browser + + try { + // 1. Launch the server + server = await this.startServer() + + // 2. Launch Chrome incognito + console.log('Launching browser...') + browser = await chromium.launch({ + headless: true, // Set to true if you don't want to see the browser + args: ['--incognito'], + }) + + const context = await browser.newContext() + const page = await context.newPage() + + // 3. Navigate to localhost:3000 + await page.goto('http://localhost:3000', { waitUntil: 'load' }) + + // 4. Find and click all buttons + const buttons = await page.locator('button').all() + + for (let j = 0; j < buttons.length; j++) { + await buttons[j].click() + await this.sleep(this.buttonClickDelay) + } + + // 5. Capture data from textbox + console.log('Capturing data from the page...') + const textboxData = await this.capturePageData(page) + console.log('Captured data from the page:', textboxData) + + // 6. Close browser + console.log('Closing browser...') + await browser.close() + browser = null + + // 7. Shut down server + console.log('Shutting down server...') + await this.stopServer(server) + server = null + + return textboxData + } catch (error) { + // Cleanup in case of error + if (browser) { + try { + await browser.close() + } catch (e) { + console.error('Error closing browser:', e.message) + } + } + if (server) { + try { + await this.stopServer(server) + } catch (e) { + console.error('Error stopping server:', e.message) + } + } + throw error + } + } + + async startServer() { + return new Promise((resolve, reject) => { + const server = spawn('pnpm', ['start'], { + stdio: ['pipe', 'pipe', 'pipe'], + shell: true, + }) + + let serverReady = false + + server.stdout.on('data', (data) => { + const output = data.toString() + console.log('Server:', output.trim()) + + // Look for common Next.js ready indicators + if ( + output.includes('Ready') || + output.includes('started server') || + output.includes('Local:') + ) { + if (!serverReady) { + serverReady = true + resolve(server) + } + } + }) + + server.stderr.on('data', (data) => { + console.error('Server Error:', data.toString().trim()) + }) + + server.on('error', (error) => { + reject(new Error(`Failed to start server: ${error.message}`)) + }) + + server.on('close', (code) => { + if (!serverReady) { + reject( + new Error(`Server exited with code ${code} before becoming ready`) + ) + } + }) + + // Timeout after 30 seconds + setTimeout(() => { + if (!serverReady) { + server.kill() + reject(new Error('Server startup timeout')) + } + }, 30000) + }) + } + + async stopServer(server) { + return new Promise((resolve) => { + if (!server || server.killed) { + resolve() + return + } + + server.on('close', () => { + resolve() + }) + + // Try graceful shutdown first + server.kill('SIGTERM') + + // Force kill after 5 seconds + setTimeout(() => { + if (!server.killed) { + server.kill('SIGKILL') + } + resolve() + }, 5000) + }) + } + + async capturePageData(page) { + return await page.evaluate(() => globalThis.BENCHMARK_RESULTS) + } + + async sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)) + } + + saveResults() { + const timestamp = new Date().toISOString().replace(/[:.]/g, '-') + const filename = `benchmark-results-${this.name}-${timestamp}.json` + + writeFileSync( + filename, + JSON.stringify(summarizeDurations(this.results), null, 2) + ) + console.log(`Results saved to ${filename}`) + } +} + +const summarizeDurations = (data) => { + if (!Array.isArray(data) || data.length === 0) { + throw new Error('No data to summarize') + } + + const byName = new Map() + for (const item of data) { + const name = item.name + if (!byName.has(name)) { + byName.set(name, []) + } + byName.get(name).push(item) + } + const results = [] + for (const [name, data] of byName) { + const loadDurations = data + .map((item) => item.loadDuration) + .sort((a, b) => a - b) + const executeDurations = data + .map((item) => item.executeDuration) + .sort((a, b) => a - b) + + const getSummary = (durations) => { + const sum = durations.reduce((acc, val) => acc + val, 0) + const average = sum / durations.length + + const middle = Math.floor(durations.length / 2) + const median = + durations.length % 2 === 0 + ? (durations[middle - 1] + durations[middle]) / 2 + : durations[middle] + + const percentile75Index = Math.floor(durations.length * 0.75) + const percentile75 = durations[percentile75Index] + + return { + average, + median, + percentile75, + } + } + + results.push({ + name, + totalSamples: data.length, + loadDuration: getSummary(loadDurations), + executeDuration: getSummary(executeDurations), + }) + } + + return results +} + +// CLI usage +const args = process.argv.slice(2) +const samples = args.length > 0 ? Number.parseInt(args[0]) : undefined +const name = args.length > 1 ? args[1] : undefined + +const runner = new BenchmarkRunner({ + name, + samples, +}) + +runner.runBenchmark().catch(console.error) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ee92e26da969..497f1c252103b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -676,6 +676,9 @@ importers: next: specifier: workspace:* version: link:../../packages/next + playwright: + specifier: ^1.40.0 + version: 1.48.0 rimraf: specifier: 6.0.1 version: 6.0.1 From 5d899ef5b979ebe025324d16618a6066e7f411af Mon Sep 17 00:00:00 2001 From: nextjs-bot Date: Mon, 4 Aug 2025 23:25:05 +0000 Subject: [PATCH 2/7] v15.4.2-canary.30 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/eslint-config-next/package.json | 4 ++-- packages/eslint-plugin-internal/package.json | 2 +- packages/eslint-plugin-next/package.json | 2 +- packages/font/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-codemod/package.json | 2 +- packages/next-env/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-storybook/package.json | 2 +- packages/next-polyfill-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next-rspack/package.json | 2 +- packages/next-swc/package.json | 2 +- packages/next/package.json | 14 +++++++------- packages/react-refresh-utils/package.json | 2 +- packages/third-parties/package.json | 4 ++-- pnpm-lock.yaml | 16 ++++++++-------- 19 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lerna.json b/lerna.json index be7dbf64cac68..08d35255a24d0 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "15.4.2-canary.29" + "version": "15.4.2-canary.30" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index eeadcb988eaec..76378709edf28 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 15b7409185168..f20f03df276aa 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "description": "ESLint configuration used by Next.js.", "main": "index.js", "license": "MIT", @@ -10,7 +10,7 @@ }, "homepage": "https://nextjs.org/docs/app/api-reference/config/eslint", "dependencies": { - "@next/eslint-plugin-next": "15.4.2-canary.29", + "@next/eslint-plugin-next": "15.4.2-canary.30", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index e4919e29f9bee..4e070d7e0461b 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": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "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 f3cd18c2d59da..ea002317f5fbe 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": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "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 352ff38105d16..a5af286c37324 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,7 +1,7 @@ { "name": "@next/font", "private": true, - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "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 6c39d178fcf64..d82160a91eb3b 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 85cabc01e3472..aeac419254a82 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 8457e5915df23..724a3a50eb710 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index dc78d9e59ceb5..37e025eefe211 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 3a4b64d8fa63a..846c0e99d6ae3 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "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 90a1e49c271ec..3e1e03e03aed2 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "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 e43df0a08f540..d948a3e8cdd73 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-rspack/package.json b/packages/next-rspack/package.json index 713715173f337..b9cb7fc9da8ba 100644 --- a/packages/next-rspack/package.json +++ b/packages/next-rspack/package.json @@ -1,6 +1,6 @@ { "name": "next-rspack", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "repository": { "url": "vercel/next.js", "directory": "packages/next-rspack" diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index ef5752f126e58..5753073e50f0f 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "private": true, "files": [ "native/" diff --git a/packages/next/package.json b/packages/next/package.json index 08663a4144981..3cb1085bd31f0 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -102,7 +102,7 @@ ] }, "dependencies": { - "@next/env": "15.4.2-canary.29", + "@next/env": "15.4.2-canary.30", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -166,11 +166,11 @@ "@jest/types": "29.5.0", "@mswjs/interceptors": "0.23.0", "@napi-rs/triples": "1.2.0", - "@next/font": "15.4.2-canary.29", - "@next/polyfill-module": "15.4.2-canary.29", - "@next/polyfill-nomodule": "15.4.2-canary.29", - "@next/react-refresh-utils": "15.4.2-canary.29", - "@next/swc": "15.4.2-canary.29", + "@next/font": "15.4.2-canary.30", + "@next/polyfill-module": "15.4.2-canary.30", + "@next/polyfill-nomodule": "15.4.2-canary.30", + "@next/react-refresh-utils": "15.4.2-canary.30", + "@next/swc": "15.4.2-canary.30", "@opentelemetry/api": "1.6.0", "@playwright/test": "1.51.1", "@rspack/core": "1.4.5", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index 35f6a0ca70b1c..601a1f089f14c 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": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "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 5f317e7a870b9..33aae304ec1f6 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -1,6 +1,6 @@ { "name": "@next/third-parties", - "version": "15.4.2-canary.29", + "version": "15.4.2-canary.30", "repository": { "url": "vercel/next.js", "directory": "packages/third-parties" @@ -26,7 +26,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "15.4.2-canary.29", + "next": "15.4.2-canary.30", "outdent": "0.8.0", "prettier": "2.5.1", "typescript": "5.8.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 497f1c252103b..27d73760c6940 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -854,7 +854,7 @@ importers: packages/eslint-config-next: dependencies: '@next/eslint-plugin-next': - specifier: 15.4.2-canary.29 + specifier: 15.4.2-canary.30 version: link:../eslint-plugin-next '@rushstack/eslint-patch': specifier: ^1.10.3 @@ -924,7 +924,7 @@ importers: packages/next: dependencies: '@next/env': - specifier: 15.4.2-canary.29 + specifier: 15.4.2-canary.30 version: link:../next-env '@swc/helpers': specifier: 0.5.15 @@ -1049,19 +1049,19 @@ importers: specifier: 1.2.0 version: 1.2.0 '@next/font': - specifier: 15.4.2-canary.29 + specifier: 15.4.2-canary.30 version: link:../font '@next/polyfill-module': - specifier: 15.4.2-canary.29 + specifier: 15.4.2-canary.30 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 15.4.2-canary.29 + specifier: 15.4.2-canary.30 version: link:../next-polyfill-nomodule '@next/react-refresh-utils': - specifier: 15.4.2-canary.29 + specifier: 15.4.2-canary.30 version: link:../react-refresh-utils '@next/swc': - specifier: 15.4.2-canary.29 + specifier: 15.4.2-canary.30 version: link:../next-swc '@opentelemetry/api': specifier: 1.6.0 @@ -1764,7 +1764,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 15.4.2-canary.29 + specifier: 15.4.2-canary.30 version: link:../next outdent: specifier: 0.8.0 From 9e9ee8521784b9c92c24f3599d84fde55bbae12c Mon Sep 17 00:00:00 2001 From: huperniketes Date: Tue, 5 Aug 2025 04:29:05 -0400 Subject: [PATCH 3/7] Documentation: Removed reference to treeShaking in 08-turbopack.mdx (#82352) --- docs/01-app/03-api-reference/08-turbopack.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/01-app/03-api-reference/08-turbopack.mdx b/docs/01-app/03-api-reference/08-turbopack.mdx index 5aafcd4cd3b94..06bb270786246 100644 --- a/docs/01-app/03-api-reference/08-turbopack.mdx +++ b/docs/01-app/03-api-reference/08-turbopack.mdx @@ -132,8 +132,6 @@ Turbopack can be configured via `next.config.js` (or `next.config.ts`) under the Change or extend file extensions for module resolution. - **`moduleIds`** Set how module IDs are generated (`'named'` vs `'deterministic'`). -- **`treeShaking`** - Enable or disable tree shaking in dev and future production builds. - **`memoryLimit`** Set a memory limit (in bytes) for Turbopack. From fb83b8345fe63838e5e5efc46b772b5c9eb76717 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Tue, 5 Aug 2025 14:33:50 +0200 Subject: [PATCH 4/7] Turbopack: run styled-jsx after typescript transform (#82359) Closes PACK-5183 Based on https://github.com/swc-project/swc/issues/10944#issuecomment-3120775091, we need to run styled-jsx after typescript Fixes this crash ``` entered unreachable code: This visitor does not support TypeScript. This method fails for optimization purposes. Encountered in unreachable visitor: visit_ts_interface_decl ``` This has become more pressing as swc seems to have turned the debug_assertion into a proper panic/crash in recent versions (I was seeing SIGSEGV when building some app): https://vercel.slack.com/archives/C03EWR7LGEN/p1754390155508789?thread_ts=1753477535.369549&cid=C03EWR7LGEN We now have three phases/stages: - preprocess: to strip typescript/decorators (so to normalize the syntax) - main: for transforms that want to operate on "standard" EcmaScript (though still with raw JSX) - postprocess: react transform, preset-env, etc (so low level "codegen") --- crates/next-core/src/next_server/context.rs | 10 +- .../next_shared/transforms/debug_fn_name.rs | 5 +- .../src/next_shared/transforms/emotion.rs | 13 ++- .../src/next_shared/transforms/mod.rs | 28 +++-- .../transforms/modularize_imports.rs | 5 +- .../transforms/next_amp_attributes.rs | 5 +- .../transforms/next_cjs_optimizer.rs | 5 +- .../next_disallow_re_export_all_in_page.rs | 5 +- .../next_shared/transforms/next_dynamic.rs | 5 +- .../transforms/next_edge_node_api_assert.rs | 5 +- .../src/next_shared/transforms/next_font.rs | 5 +- .../src/next_shared/transforms/next_lint.rs | 7 +- .../next_middleware_dynamic_assert.rs | 5 +- .../transforms/next_optimize_server_react.rs | 5 +- .../transforms/next_page_config.rs | 5 +- .../transforms/next_page_static_info.rs | 5 +- .../src/next_shared/transforms/next_pure.rs | 5 +- .../next_react_server_components.rs | 4 +- .../transforms/next_shake_exports.rs | 5 +- .../transforms/next_strip_page_exports.rs | 5 +- .../transforms/next_track_dynamic_imports.rs | 7 +- .../transforms/react_remove_properties.rs | 7 +- .../src/next_shared/transforms/relay.rs | 4 +- .../next_shared/transforms/remove_console.rs | 7 +- .../next_shared/transforms/server_actions.rs | 5 +- .../transforms/styled_components.rs | 10 +- .../src/next_shared/transforms/styled_jsx.rs | 4 +- .../transforms/swc_ecma_transform_plugins.rs | 4 +- test/e2e/styled-jsx/index.test.ts | 9 ++ test/e2e/styled-jsx/pages/typescript.tsx | 20 ++++ .../crates/turbopack-cli/src/contexts.rs | 20 +--- .../crates/turbopack-tests/tests/snapshot.rs | 5 +- turbopack/crates/turbopack/src/lib.rs | 52 ++++++--- .../turbopack/src/module_options/mod.rs | 109 ++++++++++-------- .../src/module_options/module_rule.rs | 32 +++-- 35 files changed, 276 insertions(+), 156 deletions(-) create mode 100644 test/e2e/styled-jsx/pages/typescript.tsx diff --git a/crates/next-core/src/next_server/context.rs b/crates/next-core/src/next_server/context.rs index b29e3945b334c..99d607e7d53c7 100644 --- a/crates/next-core/src/next_server/context.rs +++ b/crates/next-core/src/next_server/context.rs @@ -58,7 +58,7 @@ use crate::{ get_invalid_styled_jsx_resolve_plugin, }, transforms::{ - emotion::get_emotion_transform_rule, get_ecma_transform_rule, + EcmascriptTransformStage, emotion::get_emotion_transform_rule, get_ecma_transform_rule, next_react_server_components::get_next_react_server_components_transform_rule, react_remove_properties::get_react_remove_properties_transform_rule, relay::get_relay_transform_rule, remove_console::get_remove_console_transform_rule, @@ -768,7 +768,7 @@ pub async fn get_server_module_options_context( ecmascript_client_reference_transition_name, )), enable_mdx_rs.is_some(), - true, + EcmascriptTransformStage::Preprocess, )); } @@ -844,7 +844,7 @@ pub async fn get_server_module_options_context( ecmascript_client_reference_transition_name, )), enable_mdx_rs.is_some(), - true, + EcmascriptTransformStage::Preprocess, )); } @@ -922,7 +922,7 @@ pub async fn get_server_module_options_context( ecmascript_client_reference_transition_name, )), enable_mdx_rs.is_some(), - true, + EcmascriptTransformStage::Preprocess, )); } else { custom_source_transform_rules.push(get_ecma_transform_rule( @@ -930,7 +930,7 @@ pub async fn get_server_module_options_context( "next/dist/client/use-client-disallowed.js".to_string(), )), enable_mdx_rs.is_some(), - true, + EcmascriptTransformStage::Preprocess, )); } diff --git a/crates/next-core/src/next_shared/transforms/debug_fn_name.rs b/crates/next-core/src/next_shared/transforms/debug_fn_name.rs index a95e10ed6034f..7664fd190da3b 100644 --- a/crates/next-core/src/next_shared/transforms/debug_fn_name.rs +++ b/crates/next-core/src/next_shared/transforms/debug_fn_name.rs @@ -16,8 +16,9 @@ pub fn get_debug_fn_name_rule(enable_mdx_rs: bool) -> ModuleRule { ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![debug_fn_name_transform]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![debug_fn_name_transform]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/emotion.rs b/crates/next-core/src/next_shared/transforms/emotion.rs index d8997df8695ee..6fd4d324b7b72 100644 --- a/crates/next-core/src/next_shared/transforms/emotion.rs +++ b/crates/next-core/src/next_shared/transforms/emotion.rs @@ -4,7 +4,10 @@ use turbopack::module_options::ModuleRule; use turbopack_ecmascript_plugins::transform::emotion::EmotionTransformer; use super::get_ecma_transform_rule; -use crate::next_config::{EmotionTransformOptionsOrBoolean, NextConfig}; +use crate::{ + next_config::{EmotionTransformOptionsOrBoolean, NextConfig}, + next_shared::transforms::EcmascriptTransformStage, +}; pub async fn get_emotion_transform_rule(next_config: Vc) -> Result> { let enable_mdx_rs = next_config.mdx_rs().await?.is_some(); @@ -20,7 +23,13 @@ pub async fn get_emotion_transform_rule(next_config: Vc) -> Result EmotionTransformer::new(value), _ => None, }) - .map(|transformer| get_ecma_transform_rule(Box::new(transformer), enable_mdx_rs, true)); + .map(|transformer| { + get_ecma_transform_rule( + Box::new(transformer), + enable_mdx_rs, + EcmascriptTransformStage::Main, + ) + }); Ok(module_rule) } diff --git a/crates/next-core/src/next_shared/transforms/mod.rs b/crates/next-core/src/next_shared/transforms/mod.rs index 4491f6aa86b15..071e89f33cb22 100644 --- a/crates/next-core/src/next_shared/transforms/mod.rs +++ b/crates/next-core/src/next_shared/transforms/mod.rs @@ -134,28 +134,32 @@ pub(crate) fn module_rule_match_pages_page_file( ]) } +pub(crate) enum EcmascriptTransformStage { + Preprocess, + Main, + Postprocess, +} + /// Create a new module rule for the given ecmatransform, runs against /// any ecmascript (with mdx if enabled) except url reference type pub(crate) fn get_ecma_transform_rule( transformer: Box, enable_mdx_rs: bool, - prepend: bool, + stage: EcmascriptTransformStage, ) -> ModuleRule { let transformer = EcmascriptInputTransform::Plugin(ResolvedVc::cell(transformer as _)); - let (prepend, append) = if prepend { - ( - ResolvedVc::cell(vec![transformer]), - ResolvedVc::cell(vec![]), - ) - } else { - ( - ResolvedVc::cell(vec![]), - ResolvedVc::cell(vec![transformer]), - ) + let (preprocess, main, postprocess) = match stage { + EcmascriptTransformStage::Preprocess => (vec![transformer], vec![], vec![]), + EcmascriptTransformStage::Main => (vec![], vec![transformer], vec![]), + EcmascriptTransformStage::Postprocess => (vec![], vec![], vec![transformer]), }; ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), - vec![ModuleRuleEffect::ExtendEcmascriptTransforms { prepend, append }], + vec![ModuleRuleEffect::ExtendEcmascriptTransforms { + preprocess: ResolvedVc::cell(preprocess), + main: ResolvedVc::cell(main), + postprocess: ResolvedVc::cell(postprocess), + }], ) } diff --git a/crates/next-core/src/next_shared/transforms/modularize_imports.rs b/crates/next-core/src/next_shared/transforms/modularize_imports.rs index d963108dfb707..50e9298a42a0c 100644 --- a/crates/next-core/src/next_shared/transforms/modularize_imports.rs +++ b/crates/next-core/src/next_shared/transforms/modularize_imports.rs @@ -63,8 +63,9 @@ pub fn get_next_modularize_imports_rule( ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![transformer]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_amp_attributes.rs b/crates/next-core/src/next_shared/transforms/next_amp_attributes.rs index e3a78f105c422..3d0128750d4ff 100644 --- a/crates/next-core/src/next_shared/transforms/next_amp_attributes.rs +++ b/crates/next-core/src/next_shared/transforms/next_amp_attributes.rs @@ -14,8 +14,9 @@ pub fn get_next_amp_attr_rule(enable_mdx_rs: bool) -> ModuleRule { ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![transformer]), + postprocess: ResolvedVc::cell(vec![]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_cjs_optimizer.rs b/crates/next-core/src/next_shared/transforms/next_cjs_optimizer.rs index cf09fbcd7e1c5..6ad13957c82db 100644 --- a/crates/next-core/src/next_shared/transforms/next_cjs_optimizer.rs +++ b/crates/next-core/src/next_shared/transforms/next_cjs_optimizer.rs @@ -54,8 +54,9 @@ pub fn get_next_cjs_optimizer_rule(enable_mdx_rs: bool) -> ModuleRule { ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![transformer]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_disallow_re_export_all_in_page.rs b/crates/next-core/src/next_shared/transforms/next_disallow_re_export_all_in_page.rs index 855ae62c15061..a581a16f8c6ee 100644 --- a/crates/next-core/src/next_shared/transforms/next_disallow_re_export_all_in_page.rs +++ b/crates/next-core/src/next_shared/transforms/next_disallow_re_export_all_in_page.rs @@ -19,8 +19,9 @@ pub fn get_next_disallow_export_all_in_page_rule( ModuleRule::new( module_rule_match_pages_page_file(enable_mdx_rs, pages_dir), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![transformer]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_dynamic.rs b/crates/next-core/src/next_shared/transforms/next_dynamic.rs index 0fb60d6bad27a..ceb173230be76 100644 --- a/crates/next-core/src/next_shared/transforms/next_dynamic.rs +++ b/crates/next-core/src/next_shared/transforms/next_dynamic.rs @@ -27,8 +27,9 @@ pub async fn get_next_dynamic_transform_rule( Ok(ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![dynamic_transform]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![dynamic_transform]), }], )) } diff --git a/crates/next-core/src/next_shared/transforms/next_edge_node_api_assert.rs b/crates/next-core/src/next_shared/transforms/next_edge_node_api_assert.rs index 657cf64efca70..5ebd7ceb70c1f 100644 --- a/crates/next-core/src/next_shared/transforms/next_edge_node_api_assert.rs +++ b/crates/next-core/src/next_shared/transforms/next_edge_node_api_assert.rs @@ -24,8 +24,9 @@ pub fn next_edge_node_api_assert( ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![transformer]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_font.rs b/crates/next-core/src/next_shared/transforms/next_font.rs index 0b81722d400aa..b40bcf080ff1c 100644 --- a/crates/next-core/src/next_shared/transforms/next_font.rs +++ b/crates/next-core/src/next_shared/transforms/next_font.rs @@ -28,8 +28,9 @@ pub fn get_next_font_transform_rule(enable_mdx_rs: bool) -> ModuleRule { // TODO: Only match in pages (not pages/api), app/, etc. module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![transformer]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_lint.rs b/crates/next-core/src/next_shared/transforms/next_lint.rs index 3009dc7f217fd..453e962683837 100644 --- a/crates/next-core/src/next_shared/transforms/next_lint.rs +++ b/crates/next-core/src/next_shared/transforms/next_lint.rs @@ -6,9 +6,14 @@ use turbopack::module_options::ModuleRule; use turbopack_ecmascript::{CustomTransformer, TransformContext}; use super::get_ecma_transform_rule; +use crate::next_shared::transforms::EcmascriptTransformStage; pub fn get_next_lint_transform_rule(enable_mdx_rs: bool) -> ModuleRule { - get_ecma_transform_rule(Box::new(LintTransformer {}), enable_mdx_rs, true) + get_ecma_transform_rule( + Box::new(LintTransformer {}), + enable_mdx_rs, + EcmascriptTransformStage::Preprocess, + ) } #[derive(Debug)] diff --git a/crates/next-core/src/next_shared/transforms/next_middleware_dynamic_assert.rs b/crates/next-core/src/next_shared/transforms/next_middleware_dynamic_assert.rs index c23635607b19c..cc6eadc459329 100644 --- a/crates/next-core/src/next_shared/transforms/next_middleware_dynamic_assert.rs +++ b/crates/next-core/src/next_shared/transforms/next_middleware_dynamic_assert.rs @@ -15,8 +15,9 @@ pub fn get_middleware_dynamic_assert_rule(enable_mdx_rs: bool) -> ModuleRule { ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![transformer]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_optimize_server_react.rs b/crates/next-core/src/next_shared/transforms/next_optimize_server_react.rs index acbe54ce12714..5e5340e15e7be 100644 --- a/crates/next-core/src/next_shared/transforms/next_optimize_server_react.rs +++ b/crates/next-core/src/next_shared/transforms/next_optimize_server_react.rs @@ -20,8 +20,9 @@ pub fn get_next_optimize_server_react_rule( ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![transformer]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_page_config.rs b/crates/next-core/src/next_shared/transforms/next_page_config.rs index c95fe13a92685..a2914c96674bb 100644 --- a/crates/next-core/src/next_shared/transforms/next_page_config.rs +++ b/crates/next-core/src/next_shared/transforms/next_page_config.rs @@ -17,8 +17,9 @@ pub fn get_next_page_config_rule(enable_mdx_rs: bool, pages_dir: FileSystemPath) ModuleRule::new( module_rule_match_pages_page_file(enable_mdx_rs, pages_dir), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![transformer]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_page_static_info.rs b/crates/next-core/src/next_shared/transforms/next_page_static_info.rs index 658c044adb33d..3217f8847b163 100644 --- a/crates/next-core/src/next_shared/transforms/next_page_static_info.rs +++ b/crates/next-core/src/next_shared/transforms/next_page_static_info.rs @@ -39,8 +39,9 @@ pub fn get_next_page_static_info_assert_rule( ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![transformer]), - append: ResolvedVc::cell(vec![]), + preprocess: ResolvedVc::cell(vec![transformer]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_pure.rs b/crates/next-core/src/next_shared/transforms/next_pure.rs index 8226499ecd47e..31baeca2947d4 100644 --- a/crates/next-core/src/next_shared/transforms/next_pure.rs +++ b/crates/next-core/src/next_shared/transforms/next_pure.rs @@ -14,8 +14,9 @@ pub fn get_next_pure_rule(enable_mdx_rs: bool) -> ModuleRule { ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![transformer]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_react_server_components.rs b/crates/next-core/src/next_shared/transforms/next_react_server_components.rs index cd0b1fe71e640..89a3e3ba984e5 100644 --- a/crates/next-core/src/next_shared/transforms/next_react_server_components.rs +++ b/crates/next-core/src/next_shared/transforms/next_react_server_components.rs @@ -11,7 +11,7 @@ use turbopack::module_options::ModuleRule; use turbopack_ecmascript::{CustomTransformer, TransformContext}; use super::get_ecma_transform_rule; -use crate::next_config::NextConfig; +use crate::{next_config::NextConfig, next_shared::transforms::EcmascriptTransformStage}; /// Returns a rule which applies the Next.js react server components transform. /// This transform owns responsibility to assert various import / usage @@ -44,7 +44,7 @@ pub async fn get_next_react_server_components_transform_rule( app_dir, )), enable_mdx_rs, - true, + EcmascriptTransformStage::Preprocess, )) } diff --git a/crates/next-core/src/next_shared/transforms/next_shake_exports.rs b/crates/next-core/src/next_shared/transforms/next_shake_exports.rs index d8cc453ff975b..9a1bc958bfb94 100644 --- a/crates/next-core/src/next_shared/transforms/next_shake_exports.rs +++ b/crates/next-core/src/next_shared/transforms/next_shake_exports.rs @@ -16,8 +16,9 @@ pub fn get_next_shake_exports_rule(enable_mdx_rs: bool, ignore: Vec) -> ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![transformer]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![transformer]), }], ) } diff --git a/crates/next-core/src/next_shared/transforms/next_strip_page_exports.rs b/crates/next-core/src/next_shared/transforms/next_strip_page_exports.rs index a7df0711325b3..94c150bcf307f 100644 --- a/crates/next-core/src/next_shared/transforms/next_strip_page_exports.rs +++ b/crates/next-core/src/next_shared/transforms/next_strip_page_exports.rs @@ -40,8 +40,9 @@ pub async fn get_next_pages_transforms_rule( module_rule_match_js_no_url(enable_mdx_rs), ]), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![strip_transform]), + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![strip_transform]), }], )) } diff --git a/crates/next-core/src/next_shared/transforms/next_track_dynamic_imports.rs b/crates/next-core/src/next_shared/transforms/next_track_dynamic_imports.rs index 653df152afce8..abcec227dfc54 100644 --- a/crates/next-core/src/next_shared/transforms/next_track_dynamic_imports.rs +++ b/crates/next-core/src/next_shared/transforms/next_track_dynamic_imports.rs @@ -6,9 +6,14 @@ use turbopack::module_options::ModuleRule; use turbopack_ecmascript::{CustomTransformer, TransformContext}; use super::get_ecma_transform_rule; +use crate::next_shared::transforms::EcmascriptTransformStage; pub fn get_next_track_dynamic_imports_transform_rule(mdx_rs: bool) -> ModuleRule { - get_ecma_transform_rule(Box::new(NextTrackDynamicImports {}), mdx_rs, false) + get_ecma_transform_rule( + Box::new(NextTrackDynamicImports {}), + mdx_rs, + EcmascriptTransformStage::Postprocess, + ) } #[derive(Debug)] diff --git a/crates/next-core/src/next_shared/transforms/react_remove_properties.rs b/crates/next-core/src/next_shared/transforms/react_remove_properties.rs index 7b0c62bcaa537..47a783211fd63 100644 --- a/crates/next-core/src/next_shared/transforms/react_remove_properties.rs +++ b/crates/next-core/src/next_shared/transforms/react_remove_properties.rs @@ -6,7 +6,10 @@ use turbopack::module_options::ModuleRule; use turbopack_ecmascript::{CustomTransformer, TransformContext}; use super::get_ecma_transform_rule; -use crate::next_config::{NextConfig, ReactRemoveProperties}; +use crate::{ + next_config::{NextConfig, ReactRemoveProperties}, + next_shared::transforms::EcmascriptTransformStage, +}; /// Returns a rule which applies the react_remove_properties transform. pub async fn get_react_remove_properties_transform_rule( @@ -34,7 +37,7 @@ pub async fn get_react_remove_properties_transform_rule( get_ecma_transform_rule( Box::new(ReactRemovePropertiesTransformer { config }), enable_mdx_rs, - true, + EcmascriptTransformStage::Preprocess, ) }); diff --git a/crates/next-core/src/next_shared/transforms/relay.rs b/crates/next-core/src/next_shared/transforms/relay.rs index 6e2d9b27bb7de..51d22a30b3b72 100644 --- a/crates/next-core/src/next_shared/transforms/relay.rs +++ b/crates/next-core/src/next_shared/transforms/relay.rs @@ -5,7 +5,7 @@ use turbopack::module_options::ModuleRule; use turbopack_ecmascript_plugins::transform::relay::RelayTransformer; use super::get_ecma_transform_rule; -use crate::next_config::NextConfig; +use crate::{next_config::NextConfig, next_shared::transforms::EcmascriptTransformStage}; /// Returns a transform rule for the relay graphql transform. pub async fn get_relay_transform_rule( @@ -17,7 +17,7 @@ pub async fn get_relay_transform_rule( get_ecma_transform_rule( Box::new(RelayTransformer::new(config, &project_path)), enable_mdx_rs, - true, + EcmascriptTransformStage::Preprocess, ) }); diff --git a/crates/next-core/src/next_shared/transforms/remove_console.rs b/crates/next-core/src/next_shared/transforms/remove_console.rs index b730a75f2a331..f454e86e46168 100644 --- a/crates/next-core/src/next_shared/transforms/remove_console.rs +++ b/crates/next-core/src/next_shared/transforms/remove_console.rs @@ -6,7 +6,10 @@ use turbopack::module_options::ModuleRule; use turbopack_ecmascript::{CustomTransformer, TransformContext}; use super::get_ecma_transform_rule; -use crate::next_config::{NextConfig, RemoveConsoleConfig}; +use crate::{ + next_config::{NextConfig, RemoveConsoleConfig}, + next_shared::transforms::EcmascriptTransformStage, +}; /// Returns a rule which applies the remove_console transform. pub async fn get_remove_console_transform_rule( @@ -37,7 +40,7 @@ pub async fn get_remove_console_transform_rule( get_ecma_transform_rule( Box::new(RemoveConsoleTransformer { config }), enable_mdx_rs, - true, + EcmascriptTransformStage::Preprocess, ) }); diff --git a/crates/next-core/src/next_shared/transforms/server_actions.rs b/crates/next-core/src/next_shared/transforms/server_actions.rs index 93ece0814ab76..12ba811184ed9 100644 --- a/crates/next-core/src/next_shared/transforms/server_actions.rs +++ b/crates/next-core/src/next_shared/transforms/server_actions.rs @@ -40,8 +40,9 @@ pub async fn get_server_actions_transform_rule( Ok(ModuleRule::new( module_rule_match_js_no_url(enable_mdx_rs), vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![transformer]), - append: ResolvedVc::cell(vec![]), + preprocess: ResolvedVc::cell(vec![transformer]), + main: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![]), }], )) } diff --git a/crates/next-core/src/next_shared/transforms/styled_components.rs b/crates/next-core/src/next_shared/transforms/styled_components.rs index 7b81974b1f8cc..ac3f5ef65d41f 100644 --- a/crates/next-core/src/next_shared/transforms/styled_components.rs +++ b/crates/next-core/src/next_shared/transforms/styled_components.rs @@ -5,7 +5,7 @@ use turbopack_ecmascript_plugins::transform::styled_components::StyledComponents use crate::{ next_config::{NextConfig, StyledComponentsTransformOptionsOrBoolean}, - next_shared::transforms::get_ecma_transform_rule, + next_shared::transforms::{EcmascriptTransformStage, get_ecma_transform_rule}, }; pub async fn get_styled_components_transform_rule( @@ -27,7 +27,13 @@ pub async fn get_styled_components_transform_rule( } _ => None, }) - .map(|transformer| get_ecma_transform_rule(Box::new(transformer), enable_mdx_rs, true)); + .map(|transformer| { + get_ecma_transform_rule( + Box::new(transformer), + enable_mdx_rs, + EcmascriptTransformStage::Main, + ) + }); Ok(module_rule) } diff --git a/crates/next-core/src/next_shared/transforms/styled_jsx.rs b/crates/next-core/src/next_shared/transforms/styled_jsx.rs index 04c4163bd59f1..28fdcf49fec3f 100644 --- a/crates/next-core/src/next_shared/transforms/styled_jsx.rs +++ b/crates/next-core/src/next_shared/transforms/styled_jsx.rs @@ -5,7 +5,7 @@ use turbopack_core::environment::RuntimeVersions; use turbopack_ecmascript_plugins::transform::styled_jsx::StyledJsxTransformer; use super::get_ecma_transform_rule; -use crate::next_config::NextConfig; +use crate::{next_config::NextConfig, next_shared::transforms::EcmascriptTransformStage}; /// Returns a transform rule for the styled jsx transform. pub async fn get_styled_jsx_transform_rule( @@ -19,6 +19,6 @@ pub async fn get_styled_jsx_transform_rule( Ok(Some(get_ecma_transform_rule( Box::new(transformer), enable_mdx_rs, - true, + EcmascriptTransformStage::Main, ))) } diff --git a/crates/next-core/src/next_shared/transforms/swc_ecma_transform_plugins.rs b/crates/next-core/src/next_shared/transforms/swc_ecma_transform_plugins.rs index f09a04f613caf..78fc1e0ca51f4 100644 --- a/crates/next-core/src/next_shared/transforms/swc_ecma_transform_plugins.rs +++ b/crates/next-core/src/next_shared/transforms/swc_ecma_transform_plugins.rs @@ -48,7 +48,7 @@ pub async fn get_swc_ecma_transform_rule_impl( SwcEcmaTransformPluginsTransformer, SwcPluginModule, }; - use crate::next_shared::transforms::get_ecma_transform_rule; + use crate::next_shared::transforms::{EcmascriptTransformStage, get_ecma_transform_rule}; let plugins = plugin_configs .iter() @@ -116,6 +116,6 @@ pub async fn get_swc_ecma_transform_rule_impl( Ok(Some(get_ecma_transform_rule( Box::new(SwcEcmaTransformPluginsTransformer::new(plugins)), enable_mdx_rs, - true, + EcmascriptTransformStage::Main, ))) } diff --git a/test/e2e/styled-jsx/index.test.ts b/test/e2e/styled-jsx/index.test.ts index 3e4bebe999d7d..c3f39f2c72abe 100644 --- a/test/e2e/styled-jsx/index.test.ts +++ b/test/e2e/styled-jsx/index.test.ts @@ -41,4 +41,13 @@ describe('styled-jsx', () => { const html = await next.render('/amp') expect(html).toMatch(/color:.*?cyan/) }) + + it('should render styles inside TypeScript', async () => { + const browser = await next.browser('/typescript') + const color = await browser.eval( + `getComputedStyle(document.querySelector('button')).color` + ) + + expect(color).toMatch('255, 0, 0') + }) }) diff --git a/test/e2e/styled-jsx/pages/typescript.tsx b/test/e2e/styled-jsx/pages/typescript.tsx new file mode 100644 index 0000000000000..1dfbf4409eef2 --- /dev/null +++ b/test/e2e/styled-jsx/pages/typescript.tsx @@ -0,0 +1,20 @@ +interface Props { + color: string +} + +function Test(p: Props) { + return ( +
+ + +
+ ) +} + +export default function Page() { + return +} diff --git a/turbopack/crates/turbopack-cli/src/contexts.rs b/turbopack/crates/turbopack-cli/src/contexts.rs index 1a6c933deff70..a00803a23ac10 100644 --- a/turbopack/crates/turbopack-cli/src/contexts.rs +++ b/turbopack/crates/turbopack-cli/src/contexts.rs @@ -8,8 +8,8 @@ use turbopack::{ ModuleAssetContext, ecmascript::TreeShakingMode, module_options::{ - EcmascriptOptionsContext, JsxTransformOptions, ModuleOptionsContext, ModuleRule, - ModuleRuleEffect, RuleCondition, TypescriptTransformOptions, + EcmascriptOptionsContext, JsxTransformOptions, ModuleOptionsContext, + TypescriptTransformOptions, }, }; use turbopack_browser::react_refresh::assert_can_resolve_react_refresh; @@ -135,21 +135,6 @@ async fn get_client_module_options_context( .resolved_cell(), ); - let conditions = RuleCondition::any(vec![ - RuleCondition::ResourcePathEndsWith(".js".to_string()), - RuleCondition::ResourcePathEndsWith(".jsx".to_string()), - RuleCondition::ResourcePathEndsWith(".ts".to_string()), - RuleCondition::ResourcePathEndsWith(".tsx".to_string()), - ]); - - let module_rules = ModuleRule::new( - conditions, - vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![]), - append: ResolvedVc::cell(vec![]), - }], - ); - let module_options_context = ModuleOptionsContext { ecmascript: EcmascriptOptionsContext { enable_jsx, @@ -164,7 +149,6 @@ async fn get_client_module_options_context( foreign_code_context_condition(), module_options_context.clone().resolved_cell(), )], - module_rules: vec![module_rules], ..module_options_context } .cell(); diff --git a/turbopack/crates/turbopack-tests/tests/snapshot.rs b/turbopack/crates/turbopack-tests/tests/snapshot.rs index ca2b7a478a530..bd58646e492f5 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot.rs +++ b/turbopack/crates/turbopack-tests/tests/snapshot.rs @@ -321,7 +321,8 @@ async fn run_test_operation(resource: RcStr) -> Result> { let module_rules = ModuleRule::new( conditions, vec![ModuleRuleEffect::ExtendEcmascriptTransforms { - prepend: ResolvedVc::cell(vec![ + preprocess: ResolvedVc::cell(vec![]), + main: ResolvedVc::cell(vec![ EcmascriptInputTransform::Plugin(ResolvedVc::cell(Box::new( EmotionTransformer::new(&EmotionTransformConfig::default()) .expect("Should be able to create emotion transformer"), @@ -330,7 +331,7 @@ async fn run_test_operation(resource: RcStr) -> Result> { StyledComponentsTransformer::new(&StyledComponentsTransformConfig::default()), ) as _)), ]), - append: ResolvedVc::cell(vec![]), + postprocess: ResolvedVc::cell(vec![]), }], ); let asset_context: Vc> = Vc::upcast(ModuleAssetContext::new( diff --git a/turbopack/crates/turbopack/src/lib.rs b/turbopack/crates/turbopack/src/lib.rs index eebe8672d570a..98130b4c42c86 100644 --- a/turbopack/crates/turbopack/src/lib.rs +++ b/turbopack/crates/turbopack/src/lib.rs @@ -80,17 +80,23 @@ async fn apply_module_type( let module_type = &*module_type.await?; Ok(ProcessResult::Module(match module_type { ModuleType::Ecmascript { - transforms, + preprocess, + main, + postprocess, options, } | ModuleType::Typescript { - transforms, + preprocess, + main, + postprocess, tsx: _, analyze_types: _, options, } | ModuleType::TypescriptDeclaration { - transforms, + preprocess, + main, + postprocess, options, } => { let context_for_module = match module_type { @@ -107,7 +113,11 @@ async fn apply_module_type( let mut builder = EcmascriptModuleAsset::builder( source, ResolvedVc::upcast(context_for_module), - *transforms, + preprocess + .extend(**main) + .extend(**postprocess) + .to_resolved() + .await?, *options, module_asset_context .compile_time_info() @@ -555,28 +565,44 @@ async fn process_default_internal( ModuleRuleEffect::ModuleType(module) => { current_module_type = Some(module.clone()); } - ModuleRuleEffect::ExtendEcmascriptTransforms { prepend, append } => { + ModuleRuleEffect::ExtendEcmascriptTransforms { + preprocess: extend_preprocess, + main: extend_main, + postprocess: extend_postprocess, + } => { current_module_type = match current_module_type { Some(ModuleType::Ecmascript { - transforms, + preprocess, + main, + postprocess, options, }) => Some(ModuleType::Ecmascript { - transforms: prepend - .extend(*transforms) - .extend(**append) + preprocess: extend_preprocess + .extend(*preprocess) + .to_resolved() + .await?, + main: extend_main.extend(*main).to_resolved().await?, + postprocess: postprocess + .extend(**extend_postprocess) .to_resolved() .await?, options, }), Some(ModuleType::Typescript { - transforms, + preprocess, + main, + postprocess, tsx, analyze_types, options, }) => Some(ModuleType::Typescript { - transforms: prepend - .extend(*transforms) - .extend(**append) + preprocess: extend_preprocess + .extend(*preprocess) + .to_resolved() + .await?, + main: extend_main.extend(*main).to_resolved().await?, + postprocess: postprocess + .extend(**extend_postprocess) .to_resolved() .await?, tsx, diff --git a/turbopack/crates/turbopack/src/module_options/mod.rs b/turbopack/crates/turbopack/src/module_options/mod.rs index 3d71f94998871..e946dfe47fffd 100644 --- a/turbopack/crates/turbopack/src/module_options/mod.rs +++ b/turbopack/crates/turbopack/src/module_options/mod.rs @@ -149,7 +149,9 @@ impl ModuleOptions { .. } = *module_options_context.await?; - let mut transforms = vec![]; + let mut ts_preprocess = vec![]; + let mut ecma_preprocess = vec![]; + let mut postprocess = vec![]; // Order of transforms is important. e.g. if the React transform occurs before // Styled JSX, there won't be JSX nodes for Styled JSX to transform. @@ -158,7 +160,7 @@ impl ModuleOptions { if let Some(enable_jsx) = enable_jsx { let jsx = enable_jsx.await?; - transforms.push(EcmascriptInputTransform::React { + postprocess.push(EcmascriptInputTransform::React { development: jsx.development, refresh: jsx.react_refresh, import_source: ResolvedVc::cell(jsx.import_source.clone()), @@ -178,11 +180,11 @@ impl ModuleOptions { let ecmascript_options_vc = ecmascript_options.resolved_cell(); if let Some(environment) = environment { - transforms.push(EcmascriptInputTransform::PresetEnv(environment)); + postprocess.push(EcmascriptInputTransform::PresetEnv(environment)); } if let Some(enable_typeof_window_inlining) = enable_typeof_window_inlining { - transforms.push(EcmascriptInputTransform::GlobalTypeofs { + postprocess.push(EcmascriptInputTransform::GlobalTypeofs { window_value: match enable_typeof_window_inlining { TypeofWindow::Object => rcstr!("object"), TypeofWindow::Undefined => rcstr!("undefined"), @@ -214,43 +216,30 @@ impl ModuleOptions { None }; - let vendor_transforms = Vc::::cell(vec![]); - let ts_app_transforms = if let Some(transform) = &ts_transform { - let base_transforms = if let Some(decorators_transform) = &decorators_transform { - vec![decorators_transform.clone(), transform.clone()] - } else { - vec![transform.clone()] - }; - Vc::::cell( - base_transforms - .iter() - .cloned() - .chain(transforms.iter().cloned()) - .collect(), - ) - } else { - Vc::cell(transforms.clone()) - }; - - // Apply decorators transform for the ModuleType::Ecmascript as well after - // constructing ts_app_transforms. Ecmascript can have decorators for - // the cases of 1. using jsconfig, to enable ts-specific runtime - // decorators (i.e legacy) 2. ecma spec decorators - // - // Since typescript transform (`ts_app_transforms`) needs to apply decorators - // _before_ stripping types, we create ts_app_transforms first in a - // specific order with typescript, then apply decorators to app_transforms. - let app_transforms = Vc::::cell( + if let Some(ts_transform) = &ts_transform { if let Some(decorators_transform) = &decorators_transform { - vec![decorators_transform.clone()] + ts_preprocess.splice(0..0, [decorators_transform.clone(), ts_transform.clone()]); } else { - vec![] + ts_preprocess.splice(0..0, [ts_transform.clone()]); } - .iter() - .cloned() - .chain(transforms.iter().cloned()) - .collect(), - ); + } + if let Some(decorators_transform) = &decorators_transform { + // Apply decorators transform for the ModuleType::Ecmascript as well after + // constructing ts_app_transforms. Ecmascript can have decorators for + // the cases of 1. using jsconfig, to enable ts-specific runtime + // decorators (i.e legacy) 2. ecma spec decorators + // + // Since typescript transform (`ts_app_transforms`) needs to apply decorators + // _before_ stripping types, we create ts_app_transforms first in a + // specific order with typescript, then apply decorators to app_transforms. + ecma_preprocess.splice(0..0, [decorators_transform.clone()]); + } + + let ts_preprocess = ResolvedVc::cell(ts_preprocess); + let ecma_preprocess = ResolvedVc::cell(ecma_preprocess); + let main = ResolvedVc::::cell(vec![]); + let postprocess = ResolvedVc::cell(postprocess); + let empty = ResolvedVc::::cell(vec![]); let mut rules = vec![ ModuleRule::new_all( @@ -268,14 +257,18 @@ impl ModuleOptions { RuleCondition::ContentTypeStartsWith("text/javascript".to_string()), ]), vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript { - transforms: app_transforms.to_resolved().await?, + preprocess: ecma_preprocess, + main, + postprocess, options: ecmascript_options_vc, })], ), ModuleRule::new_all( RuleCondition::ResourcePathEndsWith(".mjs".to_string()), vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript { - transforms: app_transforms.to_resolved().await?, + preprocess: ecma_preprocess, + main, + postprocess, options: EcmascriptOptions { specified_module_type: SpecifiedModuleType::EcmaScript, ..ecmascript_options @@ -286,7 +279,9 @@ impl ModuleOptions { ModuleRule::new_all( RuleCondition::ResourcePathEndsWith(".cjs".to_string()), vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript { - transforms: app_transforms.to_resolved().await?, + preprocess: ecma_preprocess, + main, + postprocess, options: EcmascriptOptions { specified_module_type: SpecifiedModuleType::CommonJs, ..ecmascript_options @@ -297,7 +292,9 @@ impl ModuleOptions { ModuleRule::new_all( RuleCondition::ResourcePathEndsWith(".ts".to_string()), vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript { - transforms: ts_app_transforms.to_resolved().await?, + preprocess: ts_preprocess, + main, + postprocess, tsx: false, analyze_types: enable_types, options: ecmascript_options_vc, @@ -306,7 +303,9 @@ impl ModuleOptions { ModuleRule::new_all( RuleCondition::ResourcePathEndsWith(".tsx".to_string()), vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript { - transforms: ts_app_transforms.to_resolved().await?, + preprocess: ts_preprocess, + main, + postprocess, tsx: true, analyze_types: enable_types, options: ecmascript_options_vc, @@ -315,7 +314,9 @@ impl ModuleOptions { ModuleRule::new_all( RuleCondition::ResourcePathEndsWith(".mts".to_string()), vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript { - transforms: ts_app_transforms.to_resolved().await?, + preprocess: ts_preprocess, + main, + postprocess, tsx: false, analyze_types: enable_types, options: EcmascriptOptions { @@ -328,7 +329,9 @@ impl ModuleOptions { ModuleRule::new_all( RuleCondition::ResourcePathEndsWith(".mtsx".to_string()), vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript { - transforms: ts_app_transforms.to_resolved().await?, + preprocess: ts_preprocess, + main, + postprocess, tsx: true, analyze_types: enable_types, options: EcmascriptOptions { @@ -341,7 +344,9 @@ impl ModuleOptions { ModuleRule::new_all( RuleCondition::ResourcePathEndsWith(".cts".to_string()), vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript { - transforms: ts_app_transforms.to_resolved().await?, + preprocess: ts_preprocess, + main, + postprocess, tsx: false, analyze_types: enable_types, options: EcmascriptOptions { @@ -354,7 +359,9 @@ impl ModuleOptions { ModuleRule::new_all( RuleCondition::ResourcePathEndsWith(".ctsx".to_string()), vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript { - transforms: ts_app_transforms.to_resolved().await?, + preprocess: ts_preprocess, + main, + postprocess, tsx: true, analyze_types: enable_types, options: EcmascriptOptions { @@ -368,7 +375,9 @@ impl ModuleOptions { RuleCondition::ResourcePathEndsWith(".d.ts".to_string()), vec![ModuleRuleEffect::ModuleType( ModuleType::TypescriptDeclaration { - transforms: vendor_transforms.to_resolved().await?, + preprocess: empty, + main: empty, + postprocess: empty, options: ecmascript_options_vc, }, )], @@ -404,7 +413,9 @@ impl ModuleOptions { RuleCondition::ContentTypeEmpty, ]), vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript { - transforms: vendor_transforms.to_resolved().await?, + preprocess: empty, + main: empty, + postprocess: empty, options: ecmascript_options_vc, })], ), diff --git a/turbopack/crates/turbopack/src/module_options/module_rule.rs b/turbopack/crates/turbopack/src/module_options/module_rule.rs index 66fb41f5736ce..9cf9f48fcb44c 100644 --- a/turbopack/crates/turbopack/src/module_options/module_rule.rs +++ b/turbopack/crates/turbopack/src/module_options/module_rule.rs @@ -67,11 +67,14 @@ impl ModuleRule { pub enum ModuleRuleEffect { ModuleType(ModuleType), /// Allow to extend an existing Ecmascript module rules for the additional - /// transforms. First argument will prepend the existing transforms, and - /// the second argument will append the new transforms. + /// transforms ExtendEcmascriptTransforms { - prepend: ResolvedVc, - append: ResolvedVc, + /// Transforms to run first: transpile TypeScript, decorators, ... + preprocess: ResolvedVc, + /// Transforms to execute on standard EcmaScript (plus JSX): styled-jsx, swc plugins, ... + main: ResolvedVc, + /// Transforms to run last: JSX, preset-env, scan for imports, ... + postprocess: ResolvedVc, }, SourceTransforms(ResolvedVc), Ignore, @@ -81,12 +84,22 @@ pub enum ModuleRuleEffect { #[derive(Hash, Debug, Clone)] pub enum ModuleType { Ecmascript { - transforms: ResolvedVc, + /// Transforms to run first: transpile TypeScript, decorators, ... + preprocess: ResolvedVc, + /// Transforms to execute on standard EcmaScript (plus JSX): styled-jsx, swc plugins, ... + main: ResolvedVc, + /// Transforms to run last: JSX, preset-env, scan for imports, ... + postprocess: ResolvedVc, #[turbo_tasks(trace_ignore)] options: ResolvedVc, }, Typescript { - transforms: ResolvedVc, + /// Transforms to run first: transpile TypeScript, decorators, ... + preprocess: ResolvedVc, + /// Transforms to execute on standard EcmaScript (plus JSX): styled-jsx, swc plugins, ... + main: ResolvedVc, + /// Transforms to run last: JSX, preset-env, scan for imports, ... + postprocess: ResolvedVc, // parse JSX syntax. tsx: bool, // follow references to imported types. @@ -95,7 +108,12 @@ pub enum ModuleType { options: ResolvedVc, }, TypescriptDeclaration { - transforms: ResolvedVc, + /// Transforms to run first: transpile TypeScript, decorators, ... + preprocess: ResolvedVc, + /// Transforms to execute on standard EcmaScript (plus JSX): styled-jsx, swc plugins, ... + main: ResolvedVc, + /// Transforms to run last: JSX, preset-env, scan for imports, ... + postprocess: ResolvedVc, #[turbo_tasks(trace_ignore)] options: ResolvedVc, }, From d63378945224ab2d246d817768e690402d512a9d Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Tue, 5 Aug 2025 14:36:22 +0200 Subject: [PATCH 5/7] Turbopack: pass `is_development` to `next_page_config` (#82363) --- crates/next-core/src/next_client/transforms.rs | 9 +++++++-- .../src/next_shared/transforms/next_page_config.rs | 9 ++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/next-core/src/next_client/transforms.rs b/crates/next-core/src/next_client/transforms.rs index 1fa152e99b98b..a51d033d0900f 100644 --- a/crates/next-core/src/next_client/transforms.rs +++ b/crates/next-core/src/next_client/transforms.rs @@ -45,7 +45,8 @@ pub async fn get_next_client_transforms_rules( rules.push(get_next_font_transform_rule(enable_mdx_rs)); - if mode.await?.is_development() { + let is_development = mode.await?.is_development(); + if is_development { rules.push(get_debug_fn_name_rule(enable_mdx_rs)); } @@ -68,7 +69,11 @@ pub async fn get_next_client_transforms_rules( enable_mdx_rs, pages_dir.clone(), )); - rules.push(get_next_page_config_rule(enable_mdx_rs, pages_dir.clone())); + rules.push(get_next_page_config_rule( + is_development, + enable_mdx_rs, + pages_dir.clone(), + )); } } ClientContextType::App { .. } => { diff --git a/crates/next-core/src/next_shared/transforms/next_page_config.rs b/crates/next-core/src/next_shared/transforms/next_page_config.rs index a2914c96674bb..69c0c9f77d1e4 100644 --- a/crates/next-core/src/next_shared/transforms/next_page_config.rs +++ b/crates/next-core/src/next_shared/transforms/next_page_config.rs @@ -9,10 +9,13 @@ use turbopack_ecmascript::{CustomTransformer, EcmascriptInputTransform, Transfor use super::module_rule_match_pages_page_file; -pub fn get_next_page_config_rule(enable_mdx_rs: bool, pages_dir: FileSystemPath) -> ModuleRule { +pub fn get_next_page_config_rule( + is_development: bool, + enable_mdx_rs: bool, + pages_dir: FileSystemPath, +) -> ModuleRule { let transformer = EcmascriptInputTransform::Plugin(ResolvedVc::cell(Box::new(NextPageConfig { - // [TODO]: update once turbopack build works - is_development: true, + is_development, }) as _)); ModuleRule::new( module_rule_match_pages_page_file(enable_mdx_rs, pages_dir), From 8e2184f107192fb17a0fc7da117ee42f6cbf1a47 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Tue, 5 Aug 2025 15:08:57 +0200 Subject: [PATCH 6/7] Turbopack: clean up some old TODOs --- .../turbopack-core/src/module_graph/mod.rs | 2 +- turbopack/crates/turbopack-core/src/output.rs | 3 - turbopack/crates/turbopack-core/src/source.rs | 2 - .../turbopack-css/src/references/compose.rs | 4 +- .../src/transform/emotion.rs | 15 +-- .../src/asset_context.rs | 3 +- .../src/browser_runtime.rs | 1 - .../src/analyzer/graph.rs | 1 - .../src/references/esm/binding.rs | 1 - .../turbopack-nodejs/src/chunking_context.rs | 4 +- .../turbopack-resolve/src/ecmascript.rs | 2 - .../crates/turbopack-tests/tests/snapshot.rs | 9 +- turbopack/crates/turbopack/src/lib.rs | 1 - .../crates/turbopack/src/unsupported_sass.rs | 108 ------------------ 14 files changed, 12 insertions(+), 144 deletions(-) delete mode 100644 turbopack/crates/turbopack/src/unsupported_sass.rs diff --git a/turbopack/crates/turbopack-core/src/module_graph/mod.rs b/turbopack/crates/turbopack-core/src/module_graph/mod.rs index 2d5bcc610a991..43116c5b7189c 100644 --- a/turbopack/crates/turbopack-core/src/module_graph/mod.rs +++ b/turbopack/crates/turbopack-core/src/module_graph/mod.rs @@ -2074,7 +2074,7 @@ pub mod tests { impl Asset for MockModule { #[turbo_tasks::function] fn content(&self) -> Vc { - todo!() + panic!("MockModule::content shouldn't be called") } } diff --git a/turbopack/crates/turbopack-core/src/output.rs b/turbopack/crates/turbopack-core/src/output.rs index 7b808e1335b51..381ad568eeaa0 100644 --- a/turbopack/crates/turbopack-core/src/output.rs +++ b/turbopack/crates/turbopack-core/src/output.rs @@ -59,6 +59,3 @@ impl OutputAssets { /// A set of [OutputAsset]s #[turbo_tasks::value(transparent)] pub struct OutputAssetsSet(FxIndexSet>>); - -// TODO All Vc::try_resolve_downcast::> calls should be -// removed diff --git a/turbopack/crates/turbopack-core/src/source.rs b/turbopack/crates/turbopack-core/src/source.rs index 2d0e8a131107b..2954179b90900 100644 --- a/turbopack/crates/turbopack-core/src/source.rs +++ b/turbopack/crates/turbopack-core/src/source.rs @@ -17,5 +17,3 @@ pub struct OptionSource(Option>>); #[turbo_tasks::value(transparent)] pub struct Sources(Vec>>); - -// TODO All Vc::try_resolve_downcast::> calls should be removed diff --git a/turbopack/crates/turbopack-css/src/references/compose.rs b/turbopack/crates/turbopack-css/src/references/compose.rs index ceef4646e4e66..c2a54ef81c6ff 100644 --- a/turbopack/crates/turbopack-css/src/references/compose.rs +++ b/turbopack/crates/turbopack-css/src/references/compose.rs @@ -38,9 +38,7 @@ impl ModuleReference for CssModuleComposeReference { *self.origin, *self.request, CssReferenceSubType::Compose, - // TODO: add real issue source, currently impossible because `CssClassName` doesn't - // contain the source span - // https://docs.rs/swc_css_modules/0.21.16/swc_css_modules/enum.CssClassName.html + // TODO: add real issue source, currently impossible None, ) } diff --git a/turbopack/crates/turbopack-ecmascript-plugins/src/transform/emotion.rs b/turbopack/crates/turbopack-ecmascript-plugins/src/transform/emotion.rs index 585e6eab8c24f..5e145ae5dda3e 100644 --- a/turbopack/crates/turbopack-ecmascript-plugins/src/transform/emotion.rs +++ b/turbopack/crates/turbopack-ecmascript-plugins/src/transform/emotion.rs @@ -102,15 +102,12 @@ impl EmotionTransformer { enabled: Some(true), sourcemap: config.sourcemap, label_format: config.label_format.as_deref().map(From::from), - auto_label: if let Some(auto_label) = config.auto_label.as_ref() { - match auto_label { - EmotionLabelKind::Always => Some(true), - EmotionLabelKind::Never => Some(false), - // [TODO]: this is not correct coerece, need to be fixed - EmotionLabelKind::DevOnly => None, - } - } else { - None + auto_label: match config.auto_label.as_ref() { + Some(EmotionLabelKind::Always) => Some(true), + Some(EmotionLabelKind::Never) => Some(false), + // [TODO]: this is not correct (doesn't take current mode into account)ƒ + Some(EmotionLabelKind::DevOnly) => None, + None => None, }, import_map: config.import_map.as_ref().map(|map| { map.iter() diff --git a/turbopack/crates/turbopack-ecmascript-runtime/src/asset_context.rs b/turbopack/crates/turbopack-ecmascript-runtime/src/asset_context.rs index 8ca989aaf100c..b6b77a79676d7 100644 --- a/turbopack/crates/turbopack-ecmascript-runtime/src/asset_context.rs +++ b/turbopack/crates/turbopack-ecmascript-runtime/src/asset_context.rs @@ -22,7 +22,8 @@ pub async fn get_runtime_asset_context( ), ..Default::default() }, - // TODO: Somehow this fails to compile when enabled. + // TODO: This fails when enabled, we cannot insert helpers for the runtime code as this + // happens after bundling. // environment: Some(environment), environment: None, tree_shaking_mode: Some(TreeShakingMode::ReexportsOnly), diff --git a/turbopack/crates/turbopack-ecmascript-runtime/src/browser_runtime.rs b/turbopack/crates/turbopack-ecmascript-runtime/src/browser_runtime.rs index d56ec35fb172a..47948bd63b706 100644 --- a/turbopack/crates/turbopack-ecmascript-runtime/src/browser_runtime.rs +++ b/turbopack/crates/turbopack-ecmascript-runtime/src/browser_runtime.rs @@ -73,7 +73,6 @@ pub async fn get_browser_runtime_code( runtime_backend_code.push("browser/runtime/dom/dev-backend-dom.ts"); } (ChunkLoading::Dom, RuntimeType::Production) => { - // TODO runtime_backend_code.push("browser/runtime/dom/runtime-backend-dom.ts"); } diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs index c6a03d9c8bf03..c9227b47c23b2 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs @@ -622,7 +622,6 @@ impl EvalContext { if let Expr::Member(MemberExpr { obj, prop, .. }) = unparen(callee) { let obj = Box::new(self.eval(obj)); let prop = Box::new(match prop { - // TODO avoid clone MemberProp::Ident(i) => i.sym.clone().into(), MemberProp::PrivateName(_) => { return JsValue::unknown_empty( diff --git a/turbopack/crates/turbopack-ecmascript/src/references/esm/binding.rs b/turbopack/crates/turbopack-ecmascript/src/references/esm/binding.rs index d485342c84b20..ef7f5e6916b6b 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/esm/binding.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/esm/binding.rs @@ -92,7 +92,6 @@ impl EsmBinding { visit_mut_prop, |prop: &mut Prop| { if let Prop::Shorthand(ident) = prop { - // TODO: Merge with the above condition when https://rust-lang.github.io/rfcs/2497-if-let-chains.html lands. match &imported_ident { ImportedIdent::Module(imported_ident) => { *prop = Prop::KeyValue(KeyValueProp { diff --git a/turbopack/crates/turbopack-nodejs/src/chunking_context.rs b/turbopack/crates/turbopack-nodejs/src/chunking_context.rs index 535fd67cd6fbf..558290ac78b41 100644 --- a/turbopack/crates/turbopack-nodejs/src/chunking_context.rs +++ b/turbopack/crates/turbopack-nodejs/src/chunking_context.rs @@ -484,9 +484,7 @@ impl ChunkingContext for NodeJsChunkingContext { _module_graph: Vc, _availability_info: AvailabilityInfo, ) -> Result> { - // TODO(alexkirsz) This method should be part of a separate trait that is - // only implemented for client/edge runtimes. - bail!("the build chunking context does not support evaluated chunk groups") + bail!("the Node.js chunking context does not support evaluated chunk groups") } #[turbo_tasks::function] diff --git a/turbopack/crates/turbopack-resolve/src/ecmascript.rs b/turbopack/crates/turbopack-resolve/src/ecmascript.rs index c45d1c0b99c23..a0105160431be 100644 --- a/turbopack/crates/turbopack-resolve/src/ecmascript.rs +++ b/turbopack/crates/turbopack-resolve/src/ecmascript.rs @@ -107,7 +107,6 @@ pub async fn cjs_resolve( issue_source: Option, is_optional: bool, ) -> Result> { - // TODO pass CommonJsReferenceSubType let ty = ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined); let options = apply_cjs_specific_options(origin.resolve_options(ty.clone()).await?) .resolve() @@ -122,7 +121,6 @@ pub async fn cjs_resolve_source( issue_source: Option, is_optional: bool, ) -> Result> { - // TODO pass CommonJsReferenceSubType let ty = ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined); let options = apply_cjs_specific_options(origin.resolve_options(ty.clone()).await?) .resolve() diff --git a/turbopack/crates/turbopack-tests/tests/snapshot.rs b/turbopack/crates/turbopack-tests/tests/snapshot.rs index bd58646e492f5..2507613e23967 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot.rs +++ b/turbopack/crates/turbopack-tests/tests/snapshot.rs @@ -560,14 +560,7 @@ async fn walk_asset( diff(path.clone(), asset.content()).await?; } - queue.extend( - asset - .references() - .await? - .iter() - .copied() - .flat_map(ResolvedVc::try_downcast::>), - ); + queue.extend(asset.references().await?.iter().copied()); Ok(()) } diff --git a/turbopack/crates/turbopack/src/lib.rs b/turbopack/crates/turbopack/src/lib.rs index 98130b4c42c86..678164f2e6202 100644 --- a/turbopack/crates/turbopack/src/lib.rs +++ b/turbopack/crates/turbopack/src/lib.rs @@ -12,7 +12,6 @@ pub mod global_module_ids; mod graph; pub mod module_options; pub mod transition; -pub(crate) mod unsupported_sass; use anyhow::{Result, bail}; use css::{CssModuleAsset, ModuleCssAsset}; diff --git a/turbopack/crates/turbopack/src/unsupported_sass.rs b/turbopack/crates/turbopack/src/unsupported_sass.rs deleted file mode 100644 index 7804c941aad43..0000000000000 --- a/turbopack/crates/turbopack/src/unsupported_sass.rs +++ /dev/null @@ -1,108 +0,0 @@ -//! TODO(WEB-741) Remove this file once Sass is supported. - -use anyhow::Result; -use turbo_rcstr::rcstr; -use turbo_tasks::{ResolvedVc, Vc}; -use turbo_tasks_fs::{FileSystemPath, glob::Glob}; -use turbopack_core::{ - issue::{Issue, IssueExt, IssueSeverity, IssueStage, OptionStyledString, StyledString}, - reference_type::ReferenceType, - resolve::{ - ResolveResultOption, - parse::Request, - plugin::{AfterResolvePlugin, AfterResolvePluginCondition}, - }, -}; - -/// Resolve plugins that warns when importing a sass file. -#[turbo_tasks::value] -pub(crate) struct UnsupportedSassResolvePlugin { - root: FileSystemPath, -} - -#[turbo_tasks::value_impl] -impl UnsupportedSassResolvePlugin { - #[turbo_tasks::function] - pub fn new(root: FileSystemPath) -> Vc { - UnsupportedSassResolvePlugin { root }.cell() - } -} - -#[turbo_tasks::value_impl] -impl AfterResolvePlugin for UnsupportedSassResolvePlugin { - #[turbo_tasks::function] - async fn after_resolve_condition(&self) -> Result> { - Ok(AfterResolvePluginCondition::new( - self.root.root().owned().await?, - Glob::new(rcstr!("**/*.{sass,scss}")), - )) - } - - #[turbo_tasks::function] - async fn after_resolve( - &self, - fs_path: FileSystemPath, - lookup_path: FileSystemPath, - _reference_type: ReferenceType, - request: ResolvedVc, - ) -> Result> { - let extension = fs_path.extension(); - if ["sass", "scss"].contains(&extension) { - UnsupportedSassModuleIssue { - file_path: lookup_path, - request, - } - .resolved_cell() - .emit(); - } - - Ok(ResolveResultOption::none()) - } -} - -#[turbo_tasks::value(shared)] -struct UnsupportedSassModuleIssue { - // TODO(PACK-4879): The `file_path` is incorrect for this issue and we should supply - // detailed source information. - file_path: FileSystemPath, - request: ResolvedVc, -} - -#[turbo_tasks::value_impl] -impl Issue for UnsupportedSassModuleIssue { - fn severity(&self) -> IssueSeverity { - IssueSeverity::Warning - } - - #[turbo_tasks::function] - async fn title(&self) -> Result> { - Ok(StyledString::Text( - format!( - "Unsupported Sass request: {}", - self.request.await?.request().unwrap_or(rcstr!("N/A")) - ) - .into(), - ) - .cell()) - } - - #[turbo_tasks::function] - fn file_path(&self) -> Vc { - self.file_path.clone().cell() - } - - #[turbo_tasks::function] - fn description(&self) -> Vc { - Vc::cell(Some( - StyledString::Text(rcstr!( - "Turbopack does not yet support importing Sass modules." - )) - .resolved_cell(), - )) - } - - #[turbo_tasks::function] - fn stage(&self) -> Vc { - IssueStage::Unsupported.cell() - } -} From 6953253167bb5f5cb5c5d1c658b890dd5f78e157 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Tue, 5 Aug 2025 15:19:13 +0200 Subject: [PATCH 7/7] Update turbopack/crates/turbopack-ecmascript-plugins/src/transform/emotion.rs Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> --- .../turbopack-ecmascript-plugins/src/transform/emotion.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/turbopack/crates/turbopack-ecmascript-plugins/src/transform/emotion.rs b/turbopack/crates/turbopack-ecmascript-plugins/src/transform/emotion.rs index 5e145ae5dda3e..b654a75eb48f4 100644 --- a/turbopack/crates/turbopack-ecmascript-plugins/src/transform/emotion.rs +++ b/turbopack/crates/turbopack-ecmascript-plugins/src/transform/emotion.rs @@ -105,7 +105,7 @@ impl EmotionTransformer { auto_label: match config.auto_label.as_ref() { Some(EmotionLabelKind::Always) => Some(true), Some(EmotionLabelKind::Never) => Some(false), - // [TODO]: this is not correct (doesn't take current mode into account)ƒ + // [TODO]: this is not correct (doesn't take current mode into account) Some(EmotionLabelKind::DevOnly) => None, None => None, },