diff --git a/Cargo.lock b/Cargo.lock index ac6cfacff7a05..be4ebea6f3f80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10145,6 +10145,7 @@ dependencies = [ "anyhow", "either", "flate2", + "hashbrown 0.14.5", "indexmap 2.7.1", "itertools 0.10.5", "postcard", 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 f877ba4245bff..6aa0ec2cb076c 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 @@ -35,8 +35,8 @@ pub async fn get_swc_ecma_transform_rule_impl( plugin_configs: &[(RcStr, serde_json::Value)], enable_mdx_rs: bool, ) -> Result> { - use anyhow::{bail, Context}; - use turbo_tasks::{TryJoinIterExt, Value}; + use anyhow::bail; + use turbo_tasks::{TryFlatJoinIterExt, Value}; use turbo_tasks_fs::FileContent; use turbopack::{resolve_options, resolve_options_context::ResolveOptionsContext}; use turbopack_core::{ @@ -78,30 +78,34 @@ pub async fn get_swc_ecma_transform_rule_impl( ) .as_raw_module_result(), Value::new(ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined)), + // TODO proper error location *project_path, request, resolve_options, false, + // TODO proper error location None, ) .await?; - let plugin_module = plugin_wasm_module_resolve_result - .first_module() - .await? - .context("Expected to find module")?; - let content = &*plugin_module.content().file_content().await?; + let Some(plugin_module) = &*plugin_wasm_module_resolve_result.first_module().await? + else { + // Ignore unresolveable plugin modules, handle_resolve_error has already emitted an + // issue. + return Ok(None); + }; + let content = &*plugin_module.content().file_content().await?; let FileContent::Content(file) = content else { bail!("Expected file content for plugin module"); }; - Ok(( + Ok(Some(( SwcPluginModule::new(name, file.content().to_bytes()?.to_vec()).resolved_cell(), config.clone(), - )) + ))) }) - .try_join() + .try_flat_join() .await?; Ok(Some(get_ecma_transform_rule( diff --git a/lerna.json b/lerna.json index ece029a9a189e..e1fb1301d4a77 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "15.3.1-canary.2" + "version": "15.3.1-canary.4" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index f4dd2a4254246..eeb3decc39b91 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.3.1-canary.2", + "version": "15.3.1-canary.4", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 8c15d9531ce77..27cc252126dd0 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.3.1-canary.2", + "version": "15.3.1-canary.4", "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.3.1-canary.2", + "@next/eslint-plugin-next": "15.3.1-canary.4", "@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-next/package.json b/packages/eslint-plugin-next/package.json index dca65029feb5b..6d75c41507b98 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.3.1-canary.2", + "version": "15.3.1-canary.4", "description": "ESLint plugin for Next.js.", "main": "dist/index.js", "license": "MIT", diff --git a/packages/font/package.json b/packages/font/package.json index 8e1471541f648..643592fb6e48d 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,7 +1,7 @@ { "name": "@next/font", "private": true, - "version": "15.3.1-canary.2", + "version": "15.3.1-canary.4", "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 c2eaf0d683118..ce7ae48f07486 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.3.1-canary.2", + "version": "15.3.1-canary.4", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 46b8111efb547..2ddfb5932133b 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "15.3.1-canary.2", + "version": "15.3.1-canary.4", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 1cd236af7daa4..d02d29dec971d 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "15.3.1-canary.2", + "version": "15.3.1-canary.4", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 7a2cd5e98c7c0..5cdbc5281fb98 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "15.3.1-canary.2", + "version": "15.3.1-canary.4", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 0ec976364fd5c..e3d6f3e817f07 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.3.1-canary.2", + "version": "15.3.1-canary.4", "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 ba04a293cc15e..2be7a96515b7a 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.3.1-canary.2", + "version": "15.3.1-canary.4", "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 d147f868c0f9e..69b9fa5d3840f 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.3.1-canary.2", + "version": "15.3.1-canary.4", "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 5ed59ce3f520a..0bcd62ff020c6 100644 --- a/packages/next-rspack/package.json +++ b/packages/next-rspack/package.json @@ -1,6 +1,6 @@ { "name": "next-rspack", - "version": "15.3.1-canary.2", + "version": "15.3.1-canary.4", "repository": { "url": "vercel/next.js", "directory": "packages/next-rspack" diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index ed133f92fe673..92b2e7837936f 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "15.3.1-canary.2", + "version": "15.3.1-canary.4", "private": true, "files": [ "native/" diff --git a/packages/next/package.json b/packages/next/package.json index 2ec29f5de645f..ac75db1f5b36c 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "15.3.1-canary.2", + "version": "15.3.1-canary.4", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -100,7 +100,7 @@ ] }, "dependencies": { - "@next/env": "15.3.1-canary.2", + "@next/env": "15.3.1-canary.4", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", @@ -164,11 +164,11 @@ "@jest/types": "29.5.0", "@mswjs/interceptors": "0.23.0", "@napi-rs/triples": "1.2.0", - "@next/font": "15.3.1-canary.2", - "@next/polyfill-module": "15.3.1-canary.2", - "@next/polyfill-nomodule": "15.3.1-canary.2", - "@next/react-refresh-utils": "15.3.1-canary.2", - "@next/swc": "15.3.1-canary.2", + "@next/font": "15.3.1-canary.4", + "@next/polyfill-module": "15.3.1-canary.4", + "@next/polyfill-nomodule": "15.3.1-canary.4", + "@next/react-refresh-utils": "15.3.1-canary.4", + "@next/swc": "15.3.1-canary.4", "@opentelemetry/api": "1.6.0", "@playwright/test": "1.41.2", "@storybook/addon-a11y": "8.6.0", diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 9d85556ab49c0..f7cbf822fff76 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -3301,6 +3301,16 @@ export default async function build( orig, path.join(distDir, 'server', updatedRelativeDest) ) + + // since the app router not found is prioritized over pages router, + // we have to ensure the app router entries are available for all locales + if (i18n) { + for (const locale of i18n.locales) { + const curPath = `/${locale}/404` + pagesManifest[curPath] = updatedRelativeDest + } + } + pagesManifest['/404'] = updatedRelativeDest } }) diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts index df29672da0216..6db12ca87290e 100644 --- a/packages/next/src/server/config-schema.ts +++ b/packages/next/src/server/config-schema.ts @@ -390,6 +390,7 @@ export const configSchema: zod.ZodType = z.lazy(() => prerenderEarlyExit: z.boolean().optional(), proxyTimeout: z.number().gte(0).optional(), routerBFCache: z.boolean().optional(), + removeUncaughtErrorAndRejectionListeners: z.boolean().optional(), scrollRestoration: z.boolean().optional(), sri: z .object({ diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts index 2837e6e28dee1..fc8c63442106b 100644 --- a/packages/next/src/server/config-shared.ts +++ b/packages/next/src/server/config-shared.ts @@ -509,6 +509,16 @@ export interface ExperimentalConfig { */ routerBFCache?: boolean + /** + * Uninstalls all "unhandledRejection" and "uncaughtException" listeners from + * the global process so that we can override the behavior, which in some + * runtimes is to exit the process. + * + * This is experimental until we've considered the impact in various + * deployment environments. + */ + removeUncaughtErrorAndRejectionListeners?: boolean + serverActions?: { /** * Allows adjusting body parser size limit for server actions. @@ -1302,6 +1312,7 @@ export const defaultConfig: NextConfig = { useEarlyImport: false, viewTransition: false, routerBFCache: false, + removeUncaughtErrorAndRejectionListeners: false, staleTimes: { dynamic: 0, static: 300, diff --git a/packages/next/src/server/lib/start-server.ts b/packages/next/src/server/lib/start-server.ts index f19cdd24a5309..35f1e5af30d3c 100644 --- a/packages/next/src/server/lib/start-server.ts +++ b/packages/next/src/server/lib/start-server.ts @@ -30,7 +30,6 @@ import { CONFIG_FILES } from '../../shared/lib/constants' import { getStartServerInfo, logStartInfo } from './app-info-log' import { validateTurboNextConfig } from '../../lib/turbopack-warning' import { type Span, trace, flushAllTraces } from '../../trace' -import { isPostpone } from './router-utils/is-postpone' import { isIPv6 } from './is-ipv6' import { AsyncCallbackSet } from './async-callback-set' import type { NextServer } from '../next' @@ -331,29 +330,13 @@ export async function startServer( process.exit(0) })() } - const exception = (err: Error) => { - if (isPostpone(err)) { - // React postpones that are unhandled might end up logged here but they're - // not really errors. They're just part of rendering. - return - } - // This is the render worker, we keep the process alive - console.error(err) - } // Make sure commands gracefully respect termination signals (e.g. from Docker) // Allow the graceful termination to be manually configurable if (!process.env.NEXT_MANUAL_SIG_HANDLE) { process.on('SIGINT', cleanup) process.on('SIGTERM', cleanup) } - process.on('rejectionHandled', () => { - // It is ok to await a Promise late in Next.js as it allows for better - // prefetching patterns to avoid waterfalls. We ignore loggining these. - // We should've already errored in anyway unhandledRejection. - }) - process.on('uncaughtException', exception) - process.on('unhandledRejection', exception) const initResult = await getRequestHandlers({ dir, diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index eb98fd4bc565e..a08e2db8b9eba 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -112,6 +112,7 @@ import { AsyncCallbackSet } from './lib/async-callback-set' import { initializeCacheHandlers, setCacheHandler } from './use-cache/handlers' import type { UnwrapPromise } from '../lib/coalesced-function' import { populateStaticEnv } from '../lib/static-env' +import { isPostpone } from './lib/router-utils/is-postpone' export * from './base-server' @@ -159,6 +160,88 @@ function getMiddlewareMatcher( return matcher } +function installProcessErrorHandlers( + shouldRemoveUncaughtErrorAndRejectionListeners: boolean +) { + // The conventional wisdom of Node.js and other runtimes is to treat + // unhandled errors as fatal and exit the process. + // + // But Next.js is not a generic JS runtime — it's a specialized runtime for + // React Server Components. + // + // Many unhandled rejections are due to the late-awaiting pattern for + // prefetching data. In Next.js it's OK to call an async function without + // immediately awaiting it, to start the request as soon as possible + // without blocking unncessarily on the result. These can end up + // triggering an "unhandledRejection" if it later turns out that the + // data is not needed to render the page. Example: + // + // const promise = fetchData() + // const shouldShow = await checkCondition() + // if (shouldShow) { + // return + // } + // + // In this example, `fetchData` is called immediately to start the request + // as soon as possible, but if `shouldShow` is false, then it will be + // discarded without unwrapping its result. If it errors, it will trigger + // an "unhandledRejection" event. + // + // Ideally, we would suppress these rejections completely without warning, + // because we don't consider them real errors. (TODO: Currently we do warn.) + // + // But regardless of whether we do or don't warn, we definitely shouldn't + // crash the entire process. + // + // Even a "legit" unhandled error unrelated to prefetching shouldn't + // prevent the rest of the page from rendering. + // + // So, we're going to intentionally override the default error handling + // behavior of the outer JS runtime to be more forgiving + + // Remove any existing "unhandledRejection" and "uncaughtException" handlers. + // This is gated behind an experimental flag until we've considered the impact + // in various deployment environments. It's possible this may always need to + // be configurable. + if (shouldRemoveUncaughtErrorAndRejectionListeners) { + process.removeAllListeners('uncaughtException') + process.removeAllListeners('unhandledRejection') + } + + // Install a new handler to prevent the process from crashing. + process.on('unhandledRejection', (reason: unknown) => { + if (isPostpone(reason)) { + // React postpones that are unhandled might end up logged here but they're + // not really errors. They're just part of rendering. + return + } + // Immediately log the error. + // TODO: Ideally, if we knew that this error was triggered by application + // code, we would suppress it entirely without logging. We can't reliably + // detect all of these, but when dynamicIO is enabled, we could suppress + // at least some of them by waiting to log the error until after all in- + // progress renders have completed. Then, only log errors for which there + // was not a corresponding "rejectionHandled" event. + console.error(reason) + }) + + process.on('rejectionHandled', () => { + // TODO: See note in the unhandledRejection handler above. In the future, + // we may use the "rejectionHandled" event to de-queue an error from + // being logged. + }) + + // Unhandled exceptions are errors triggered by non-async functions, so this + // is unrelated to the late-awaiting pattern. However, for similar reasons, + // we still shouldn't crash the process. Just log it. + process.on('uncaughtException', (reason: unknown) => { + if (isPostpone(reason)) { + return + } + console.error(reason) + }) +} + export default class NextNodeServer extends BaseServer< Options, NodeNextRequest, @@ -287,6 +370,11 @@ export default class NextNodeServer extends BaseServer< if (this.renderOpts.isExperimentalCompile) { populateStaticEnv(this.nextConfig) } + + const shouldRemoveUncaughtErrorAndRejectionListeners = Boolean( + options.conf.experimental?.removeUncaughtErrorAndRejectionListeners + ) + installProcessErrorHandlers(shouldRemoveUncaughtErrorAndRejectionListeners) } public async unstable_preloadEntries(): Promise { diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index ac5e2ecdcf0c3..e84fed7245274 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.3.1-canary.2", + "version": "15.3.1-canary.4", "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 634fbb92776cd..7902ada901bd8 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -1,6 +1,6 @@ { "name": "@next/third-parties", - "version": "15.3.1-canary.2", + "version": "15.3.1-canary.4", "repository": { "url": "vercel/next.js", "directory": "packages/third-parties" @@ -26,7 +26,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "15.3.1-canary.2", + "next": "15.3.1-canary.4", "outdent": "0.8.0", "prettier": "2.5.1", "typescript": "5.8.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c5e64e5b5e6e..5e037a07bd4b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -826,7 +826,7 @@ importers: packages/eslint-config-next: dependencies: '@next/eslint-plugin-next': - specifier: 15.3.1-canary.2 + specifier: 15.3.1-canary.4 version: link:../eslint-plugin-next '@rushstack/eslint-patch': specifier: ^1.10.3 @@ -890,7 +890,7 @@ importers: packages/next: dependencies: '@next/env': - specifier: 15.3.1-canary.2 + specifier: 15.3.1-canary.4 version: link:../next-env '@swc/counter': specifier: 0.1.3 @@ -1015,19 +1015,19 @@ importers: specifier: 1.2.0 version: 1.2.0 '@next/font': - specifier: 15.3.1-canary.2 + specifier: 15.3.1-canary.4 version: link:../font '@next/polyfill-module': - specifier: 15.3.1-canary.2 + specifier: 15.3.1-canary.4 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 15.3.1-canary.2 + specifier: 15.3.1-canary.4 version: link:../next-polyfill-nomodule '@next/react-refresh-utils': - specifier: 15.3.1-canary.2 + specifier: 15.3.1-canary.4 version: link:../react-refresh-utils '@next/swc': - specifier: 15.3.1-canary.2 + specifier: 15.3.1-canary.4 version: link:../next-swc '@opentelemetry/api': specifier: 1.6.0 @@ -1712,7 +1712,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 15.3.1-canary.2 + specifier: 15.3.1-canary.4 version: link:../next outdent: specifier: 0.8.0 diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 41eb67abca569..e7fbc0c176d98 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2025-02-12" +channel = "nightly-2025-04-10" components = ["rustfmt", "clippy", "rust-analyzer"] profile = "minimal" diff --git a/test/e2e/app-dir/not-found-with-pages-i18n/app/app-dir/[[...slug]]/page.tsx b/test/e2e/app-dir/not-found-with-pages-i18n/app/app-dir/[[...slug]]/page.tsx new file mode 100644 index 0000000000000..84e51b866c775 --- /dev/null +++ b/test/e2e/app-dir/not-found-with-pages-i18n/app/app-dir/[[...slug]]/page.tsx @@ -0,0 +1,44 @@ +import { notFound } from 'next/navigation' + +export async function generateStaticParams() { + return [] +} + +async function validateSlug(slug: string[]) { + try { + const isValidPath = + slug.length === 1 && (slug[0] === 'about' || slug[0] === 'contact') + + if (!isValidPath) { + return false + } + + return true + } catch (error) { + throw error + } +} + +export default async function CatchAll({ + params, +}: { + params: Promise<{ slug: string[] }> +}) { + const { slug } = await params + const slugArray = Array.isArray(slug) ? slug : [slug] + + // Validate the slug + const isValid = await validateSlug(slugArray) + + // If not valid, show 404 + if (!isValid) { + notFound() + } + + return ( +
+

Catch All

+

This is a catch all page added to the APP router

+
+ ) +} diff --git a/test/e2e/app-dir/not-found-with-pages-i18n/app/layout.tsx b/test/e2e/app-dir/not-found-with-pages-i18n/app/layout.tsx new file mode 100644 index 0000000000000..a14e64fcd5e33 --- /dev/null +++ b/test/e2e/app-dir/not-found-with-pages-i18n/app/layout.tsx @@ -0,0 +1,16 @@ +export const metadata = { + title: 'Next.js', + description: 'Generated by Next.js', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/test/e2e/app-dir/not-found-with-pages-i18n/app/not-found.tsx b/test/e2e/app-dir/not-found-with-pages-i18n/app/not-found.tsx new file mode 100644 index 0000000000000..9e2f20f4cca7c --- /dev/null +++ b/test/e2e/app-dir/not-found-with-pages-i18n/app/not-found.tsx @@ -0,0 +1,10 @@ +import React from 'react' + +const NotFound = () => ( +
+

APP ROUTER - 404 PAGE

+

This page is using the APP ROUTER

+
+) + +export default NotFound diff --git a/test/e2e/app-dir/not-found-with-pages-i18n/next.config.js b/test/e2e/app-dir/not-found-with-pages-i18n/next.config.js new file mode 100644 index 0000000000000..ee7663cef2d2b --- /dev/null +++ b/test/e2e/app-dir/not-found-with-pages-i18n/next.config.js @@ -0,0 +1,12 @@ +/** + * @type {import('next').NextConfig} + */ +const nextConfig = { + i18n: { + locales: ['en-GB', 'en'], + defaultLocale: 'en', + localeDetection: false, + }, +} + +module.exports = nextConfig diff --git a/test/e2e/app-dir/not-found-with-pages-i18n/not-found-with-pages.test.ts b/test/e2e/app-dir/not-found-with-pages-i18n/not-found-with-pages.test.ts new file mode 100644 index 0000000000000..5b6a635bd3d6b --- /dev/null +++ b/test/e2e/app-dir/not-found-with-pages-i18n/not-found-with-pages.test.ts @@ -0,0 +1,31 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('not-found-with-pages', () => { + const { next, isNextStart } = nextTestSetup({ + files: __dirname, + }) + + if (isNextStart) { + it('should write all locales to the pages manifest', async () => { + const pagesManifest = JSON.parse( + await next.readFile('.next/server/pages-manifest.json') + ) + + expect(pagesManifest['/404']).toBe('pages/404.html') + expect(pagesManifest['/en/404']).toBe('pages/404.html') + expect(pagesManifest['/en-GB/404']).toBe('pages/404.html') + }) + } + + it('should prefer the app router 404 over the pages router 404 when both are present', async () => { + const browser = await next.browser('/app-dir/foo') + expect(await browser.elementByCss('h1').text()).toBe( + 'APP ROUTER - 404 PAGE' + ) + + await browser.loadPage(next.url) + expect(await browser.elementByCss('h1').text()).toBe( + 'APP ROUTER - 404 PAGE' + ) + }) +}) diff --git a/test/e2e/app-dir/not-found-with-pages-i18n/pages/404.tsx b/test/e2e/app-dir/not-found-with-pages-i18n/pages/404.tsx new file mode 100644 index 0000000000000..be5a6a9368ef2 --- /dev/null +++ b/test/e2e/app-dir/not-found-with-pages-i18n/pages/404.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { GetStaticProps } from 'next' + +interface NotFoundProps { + message: string +} + +const NotFound = ({ message }: NotFoundProps) => ( +
+

PAGES ROUTER - 404 PAGE

+

This page is using the PAGES ROUTER

+

{message}

+
+) + +export const getStaticProps: GetStaticProps = async () => { + return { + props: { + message: 'Custom message fetched at build time', + }, + } +} + +export default NotFound diff --git a/test/e2e/app-dir/not-found-with-pages-i18n/pages/[[...slug]].tsx b/test/e2e/app-dir/not-found-with-pages-i18n/pages/[[...slug]].tsx new file mode 100644 index 0000000000000..2da5fd5f318d5 --- /dev/null +++ b/test/e2e/app-dir/not-found-with-pages-i18n/pages/[[...slug]].tsx @@ -0,0 +1,39 @@ +import React from 'react' + +export const getStaticProps = ({ params }: { params: { slug: string[] } }) => { + try { + const slugArray = Array.isArray(params.slug) ? params.slug : [params.slug] + + const isValidPath = + slugArray.length === 1 && + (slugArray[0] === 'about' || slugArray[0] === 'contact') + + if (!isValidPath) { + return { + notFound: true, + } + } + + return { + props: { + slug: params.slug, + }, + } + } catch (error) { + throw error + } +} + +export const getStaticPaths = async () => ({ + paths: [], + fallback: 'blocking', +}) + +const CatchAll = () => ( +
+

Catch All

+

This is a catch all page added to the pages router

+
+) + +export default CatchAll diff --git a/test/e2e/app-dir/rsc-basic/rsc-basic-react-experimental.test.ts b/test/e2e/app-dir/rsc-basic/rsc-basic-react-experimental.test.ts new file mode 100644 index 0000000000000..4826f9ebc34e0 --- /dev/null +++ b/test/e2e/app-dir/rsc-basic/rsc-basic-react-experimental.test.ts @@ -0,0 +1,76 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('react@experimental', () => { + const { next } = nextTestSetup({ + files: __dirname, + overrideFiles: { + 'next.config.js': ` + module.exports = { + experimental: { + taint: true, + } + } + `, + }, + }) + + it('should opt into the react@experimental when enabling $flag', async () => { + const resPages$ = await next.render$('/app-react') + const [ + ssrReact, + ssrReactDOM, + ssrClientReact, + ssrClientReactDOM, + ssrClientReactDOMServer, + ] = [ + resPages$('#react').text(), + resPages$('#react-dom').text(), + resPages$('#client-react').text(), + resPages$('#client-react-dom').text(), + resPages$('#client-react-dom-server').text(), + ] + expect({ + ssrReact, + ssrReactDOM, + ssrClientReact, + ssrClientReactDOM, + ssrClientReactDOMServer, + }).toEqual({ + ssrReact: expect.stringMatching('-experimental-'), + ssrReactDOM: expect.stringMatching('-experimental-'), + ssrClientReact: expect.stringMatching('-experimental-'), + ssrClientReactDOM: expect.stringMatching('-experimental-'), + ssrClientReactDOMServer: expect.stringMatching('-experimental-'), + }) + + const browser = await next.browser('/app-react') + const [ + browserReact, + browserReactDOM, + browserClientReact, + browserClientReactDOM, + browserClientReactDOMServer, + ] = await browser.eval(` + [ + document.querySelector('#react').innerText, + document.querySelector('#react-dom').innerText, + document.querySelector('#client-react').innerText, + document.querySelector('#client-react-dom').innerText, + document.querySelector('#client-react-dom-server').innerText, + ] + `) + expect({ + browserReact, + browserReactDOM, + browserClientReact, + browserClientReactDOM, + browserClientReactDOMServer, + }).toEqual({ + browserReact: expect.stringMatching('-experimental-'), + browserReactDOM: expect.stringMatching('-experimental-'), + browserClientReact: expect.stringMatching('-experimental-'), + browserClientReactDOM: expect.stringMatching('-experimental-'), + browserClientReactDOMServer: expect.stringMatching('-experimental-'), + }) + }) +}) diff --git a/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts b/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts index 2df3252119e5c..46a52b0d180b3 100644 --- a/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts +++ b/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts @@ -621,84 +621,4 @@ describe('app dir - rsc basics', () => { await Promise.all(promises) }) } - - describe('react@experimental', () => { - it.each([{ flag: 'ppr' }, { flag: 'taint' }])( - 'should opt into the react@experimental when enabling $flag', - async ({ flag }) => { - await next.stop() - await next.patchFile( - 'next.config.js', - ` - module.exports = { - experimental: { - ${flag}: true - } - } - `, - async () => { - await next.start() - const resPages$ = await next.render$('/app-react') - const [ - ssrReact, - ssrReactDOM, - ssrClientReact, - ssrClientReactDOM, - ssrClientReactDOMServer, - ] = [ - resPages$('#react').text(), - resPages$('#react-dom').text(), - resPages$('#client-react').text(), - resPages$('#client-react-dom').text(), - resPages$('#client-react-dom-server').text(), - ] - expect({ - ssrReact, - ssrReactDOM, - ssrClientReact, - ssrClientReactDOM, - ssrClientReactDOMServer, - }).toEqual({ - ssrReact: expect.stringMatching('-experimental-'), - ssrReactDOM: expect.stringMatching('-experimental-'), - ssrClientReact: expect.stringMatching('-experimental-'), - ssrClientReactDOM: expect.stringMatching('-experimental-'), - ssrClientReactDOMServer: expect.stringMatching('-experimental-'), - }) - - const browser = await next.browser('/app-react') - const [ - browserReact, - browserReactDOM, - browserClientReact, - browserClientReactDOM, - browserClientReactDOMServer, - ] = await browser.eval(` - [ - document.querySelector('#react').innerText, - document.querySelector('#react-dom').innerText, - document.querySelector('#client-react').innerText, - document.querySelector('#client-react-dom').innerText, - document.querySelector('#client-react-dom-server').innerText, - ] - `) - expect({ - browserReact, - browserReactDOM, - browserClientReact, - browserClientReactDOM, - browserClientReactDOMServer, - }).toEqual({ - browserReact: expect.stringMatching('-experimental-'), - browserReactDOM: expect.stringMatching('-experimental-'), - browserClientReact: expect.stringMatching('-experimental-'), - browserClientReactDOM: expect.stringMatching('-experimental-'), - browserClientReactDOMServer: - expect.stringMatching('-experimental-'), - }) - } - ) - } - ) - }) }) diff --git a/test/e2e/app-dir/worker/app/string/page.js b/test/e2e/app-dir/worker/app/string/page.js new file mode 100644 index 0000000000000..ba9f0a9795f38 --- /dev/null +++ b/test/e2e/app-dir/worker/app/string/page.js @@ -0,0 +1,22 @@ +'use client' +import { useState } from 'react' + +export default function Home() { + const [state, setState] = useState('default') + return ( +
+ +

Worker state:

+

{state}

+
+ ) +} diff --git a/test/e2e/app-dir/worker/public/unbundled-worker.js b/test/e2e/app-dir/worker/public/unbundled-worker.js new file mode 100644 index 0000000000000..50de186169b44 --- /dev/null +++ b/test/e2e/app-dir/worker/public/unbundled-worker.js @@ -0,0 +1 @@ +self.postMessage('unbundled-worker') diff --git a/test/e2e/app-dir/worker/worker.test.ts b/test/e2e/app-dir/worker/worker.test.ts index d6674b372acd2..aa4bd25edf7d1 100644 --- a/test/e2e/app-dir/worker/worker.test.ts +++ b/test/e2e/app-dir/worker/worker.test.ts @@ -1,5 +1,5 @@ import { nextTestSetup } from 'e2e-utils' -import { check } from 'next-test-utils' +import { retry } from 'next-test-utils' describe('app dir - workers', () => { const { next, skipped } = nextTestSetup({ @@ -17,9 +17,10 @@ describe('app dir - workers', () => { await browser.elementByCss('button').click() - await check( - async () => browser.elementByCss('#worker-state').text(), - 'worker.ts:worker-dep' + await retry(async () => + expect(await browser.elementByCss('#worker-state').text()).toBe( + 'worker.ts:worker-dep' + ) ) }) @@ -29,9 +30,23 @@ describe('app dir - workers', () => { await browser.elementByCss('button').click() - await check( - async () => browser.elementByCss('#worker-state').text(), - 'worker.ts:worker-dep' + await retry(async () => + expect(await browser.elementByCss('#worker-state').text()).toBe( + 'worker.ts:worker-dep' + ) + ) + }) + + it('should not bundle web workers with string specifiers', async () => { + const browser = await next.browser('/string') + expect(await browser.elementByCss('#worker-state').text()).toBe('default') + + await browser.elementByCss('button').click() + + await retry(async () => + expect(await browser.elementByCss('#worker-state').text()).toBe( + 'unbundled-worker' + ) ) }) }) diff --git a/test/e2e/swc-plugins/app/layout.js b/test/e2e/swc-plugins/app/layout.js new file mode 100644 index 0000000000000..9342eed18c78f --- /dev/null +++ b/test/e2e/swc-plugins/app/layout.js @@ -0,0 +1,9 @@ +import React from 'react' + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} diff --git a/test/e2e/swc-plugins/app/page.js b/test/e2e/swc-plugins/app/page.js new file mode 100644 index 0000000000000..e5e30cce60408 --- /dev/null +++ b/test/e2e/swc-plugins/app/page.js @@ -0,0 +1,5 @@ +import React from 'react' + +export default function Page({ children }) { + return
Hello World
+} diff --git a/test/e2e/swc-plugins/index.test.ts b/test/e2e/swc-plugins/index.test.ts new file mode 100644 index 0000000000000..8c5c70af08ebe --- /dev/null +++ b/test/e2e/swc-plugins/index.test.ts @@ -0,0 +1,58 @@ +import { nextTestSetup, isNextDev } from 'e2e-utils' + +describe('swcPlugins', () => { + describe('supports swcPlugins', () => { + const { next, skipped } = nextTestSetup({ + files: __dirname, + skipDeployment: true, + dependencies: { + '@swc/plugin-react-remove-properties': '7.0.2', + }, + }) + if (skipped) return + + it('basic case', async () => { + const html = await next.render('/') + expect(html).toContain('Hello World') + expect(html).not.toContain('data-custom-attribute') + }) + }) + ;(isNextDev ? describe : describe.skip)('invalid plugin name', () => { + const { next, skipped, isTurbopack } = nextTestSetup({ + files: __dirname, + skipDeployment: true, + overrideFiles: { + 'next.config.js': ` +module.exports = { + experimental: { + swcPlugins: [['@swc/plugin-nonexistent', {}]], + }, +}`, + }, + }) + if (skipped) return + + it('shows a redbox in dev', async () => { + const browser = await next.browser('/') + + if (isTurbopack) { + await expect(browser).toDisplayRedbox(` + { + "description": "Module not found: Can't resolve '@swc/plugin-nonexistent'", + "environmentLabel": null, + "label": "Build Error", + "source": "./ + Module not found: Can't resolve '@swc/plugin-nonexistent' + https://nextjs.org/docs/messages/module-not-found", + "stack": [], + } + `) + } else { + // TODO missing proper error with Webpack + await expect(browser).toDisplayRedbox( + `"Expected Redbox but found no visible one."` + ) + } + }) + }) +}) diff --git a/test/e2e/swc-plugins/next.config.js b/test/e2e/swc-plugins/next.config.js new file mode 100644 index 0000000000000..3acbf16005e7a --- /dev/null +++ b/test/e2e/swc-plugins/next.config.js @@ -0,0 +1,13 @@ +/** @type {import('next').NextConfig} */ +module.exports = { + experimental: { + swcPlugins: [ + [ + '@swc/plugin-react-remove-properties', + { + properties: ['^data-custom-attribute$'], + }, + ], + ], + }, +} diff --git a/turbopack/crates/turbo-static/src/main.rs b/turbopack/crates/turbo-static/src/main.rs index 8b996f62c08c5..498f80be8056a 100644 --- a/turbopack/crates/turbo-static/src/main.rs +++ b/turbopack/crates/turbo-static/src/main.rs @@ -194,6 +194,7 @@ fn resolve_concurrency( for (ident, references) in dep_tree { for reference in references { + #[allow(clippy::map_entry)] // This doesn't insert into dep_tree, so entry isn't useful if !dep_tree.contains_key(&reference.identifier) { // this is a task that is not in the task list // so we can't resolve it diff --git a/turbopack/crates/turbo-tasks-auto-hash-map/src/lib.rs b/turbopack/crates/turbo-tasks-auto-hash-map/src/lib.rs index e003b024b4f5c..42526ed9e2b25 100644 --- a/turbopack/crates/turbo-tasks-auto-hash-map/src/lib.rs +++ b/turbopack/crates/turbo-tasks-auto-hash-map/src/lib.rs @@ -1,6 +1,3 @@ -#![feature(hash_raw_entry)] -#![feature(hash_extract_if)] - pub mod map; pub mod set; diff --git a/turbopack/crates/turbo-tasks-fs/src/lib.rs b/turbopack/crates/turbo-tasks-fs/src/lib.rs index 464f8d3867e95..20f8395e8b2f0 100644 --- a/turbopack/crates/turbo-tasks-fs/src/lib.rs +++ b/turbopack/crates/turbo-tasks-fs/src/lib.rs @@ -1,6 +1,5 @@ #![allow(clippy::needless_return)] // tokio macro-generated code doesn't respect this #![feature(trivial_bounds)] -#![feature(hash_extract_if)] #![feature(min_specialization)] #![feature(iter_advance_by)] #![feature(io_error_more)] diff --git a/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_operation_method_self_type.stderr b/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_operation_method_self_type.stderr index 7fb9a0baa3031..0a167e2e1632d 100644 --- a/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_operation_method_self_type.stderr +++ b/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_operation_method_self_type.stderr @@ -4,6 +4,15 @@ error: methods taking `self` are not supported with `operation` 13 | fn arbitrary_self_type(self: OperationVc) -> Vc<()> { | ^^^^^^^^^^^^^^^^^^^^^^^ +error[E0307]: invalid `self` parameter type: `OperationVc` + --> tests/function/fail_operation_method_self_type.rs:13:34 + | +13 | fn arbitrary_self_type(self: OperationVc) -> Vc<()> { + | ^^^^^^^^^^^^^^^^^ + | + = note: type of `self` must be `Self` or some type implementing `Receiver` + = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` + error[E0277]: the trait bound `fn(...) -> ... {...::arbitrary_self_type_turbo_tasks_function_inline}: IntoTaskFnWithThis<_, _, _>` is not satisfied --> tests/function/fail_operation_method_self_type.rs:10:1 | @@ -22,12 +31,3 @@ note: required by a bound in `NativeFunction::new_method` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `NativeFunction::new_method` = note: consider using `--verbose` to print the full type name to the console = note: this error originates in the attribute macro `turbo_tasks::value_impl` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0307]: invalid `self` parameter type: `OperationVc` - --> tests/function/fail_operation_method_self_type.rs:13:34 - | -13 | fn arbitrary_self_type(self: OperationVc) -> Vc<()> { - | ^^^^^^^^^^^^^^^^^ - | - = note: type of `self` must be `Self` or some type implementing `Receiver` - = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` diff --git a/turbopack/crates/turbo-tasks-memory/src/lib.rs b/turbopack/crates/turbo-tasks-memory/src/lib.rs index 9f1f3e3ba67e4..bd2a5476b5b40 100644 --- a/turbopack/crates/turbo-tasks-memory/src/lib.rs +++ b/turbopack/crates/turbo-tasks-memory/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(hash_extract_if)] #![feature(type_alias_impl_trait)] #![feature(box_patterns)] #![feature(int_roundings)] diff --git a/turbopack/crates/turbo-tasks/src/lib.rs b/turbopack/crates/turbo-tasks/src/lib.rs index 35a34f3e3f493..c76166d663222 100644 --- a/turbopack/crates/turbo-tasks/src/lib.rs +++ b/turbopack/crates/turbo-tasks/src/lib.rs @@ -29,7 +29,6 @@ #![feature(trivial_bounds)] #![feature(min_specialization)] #![feature(try_trait_v2)] -#![feature(hash_extract_if)] #![deny(unsafe_op_in_unsafe_fn)] #![feature(result_flattening)] #![feature(error_generic_member_access)] diff --git a/turbopack/crates/turbopack-css/src/process.rs b/turbopack/crates/turbopack-css/src/process.rs index 81120ec02f420..1ca595ce6779b 100644 --- a/turbopack/crates/turbopack-css/src/process.rs +++ b/turbopack/crates/turbopack-css/src/process.rs @@ -112,6 +112,7 @@ impl StyleSheetLike<'_, '_> { pub struct UnresolvedUrlReferences(pub Vec<(String, ResolvedVc)>); #[turbo_tasks::value(shared, serialization = "none", eq = "manual", cell = "new")] +#[allow(clippy::large_enum_variant)] // This is a turbo-tasks value pub enum ParseCssResult { Ok { code: ResolvedVc, diff --git a/turbopack/crates/turbopack-dev-server/src/http.rs b/turbopack/crates/turbopack-dev-server/src/http.rs index 62b2acac33f1e..84d6657396f2b 100644 --- a/turbopack/crates/turbopack-dev-server/src/http.rs +++ b/turbopack/crates/turbopack-dev-server/src/http.rs @@ -1,5 +1,3 @@ -use std::io::{Error, ErrorKind}; - use anyhow::{anyhow, Result}; use auto_hash_map::AutoSet; use futures::{StreamExt, TryStreamExt}; @@ -177,10 +175,7 @@ pub async fn process_request_with_content_source( header_map.insert(CONTENT_ENCODING, HeaderValue::from_static("gzip")); // Grab ropereader stream, coerce anyhow::Error to std::io::Error - let stream_ext = content - .read() - .into_stream() - .map_err(|err| Error::new(ErrorKind::Other, err)); + let stream_ext = content.read().into_stream().map_err(std::io::Error::other); let gzipped_stream = ReaderStream::new(async_compression::tokio::bufread::GzipEncoder::new( diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs index ad799d688e226..f9e314008fbba 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs @@ -1516,10 +1516,9 @@ impl VisitAstPath for Analyzer<'_> { decl: &'ast FnDecl, ast_path: &mut AstNodePath>, ) { - let old = replace( - &mut self.cur_fn_return_values, - Some(get_fn_init_return_vals(decl.function.body.as_ref())), - ); + let old = self + .cur_fn_return_values + .replace(get_fn_init_return_vals(decl.function.body.as_ref())); let old_ident = self.cur_fn_ident; self.cur_fn_ident = decl.function.span.lo.0; decl.visit_children_with_ast_path(self, ast_path); @@ -1540,10 +1539,9 @@ impl VisitAstPath for Analyzer<'_> { expr: &'ast FnExpr, ast_path: &mut AstNodePath>, ) { - let old = replace( - &mut self.cur_fn_return_values, - Some(get_fn_init_return_vals(expr.function.body.as_ref())), - ); + let old = self + .cur_fn_return_values + .replace(get_fn_init_return_vals(expr.function.body.as_ref())); let old_ident = self.cur_fn_ident; self.cur_fn_ident = expr.function.span.lo.0; expr.visit_children_with_ast_path(self, ast_path); diff --git a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs index 59a50d22ac969..99575c4c7b2db 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs @@ -1641,14 +1641,7 @@ async fn handle_call) + Send + Sync>( return Ok(()); } - let (args, hints) = explain_args(&args); - handler.span_warn_with_code( - span, - &format!("new Worker({args}) is not statically analyse-able{hints}",), - DiagnosticId::Error( - errors::failed_to_analyse::ecmascript::DYNAMIC_IMPORT.to_string(), - ), - ); + // Ignore (e.g. dynamic parameter or string literal), just as Webpack does return Ok(()); } _ => {} diff --git a/turbopack/crates/turbopack-ecmascript/src/swc_comments.rs b/turbopack/crates/turbopack-ecmascript/src/swc_comments.rs index 4c9000aa06aed..e42c497047d90 100644 --- a/turbopack/crates/turbopack-ecmascript/src/swc_comments.rs +++ b/turbopack/crates/turbopack-ecmascript/src/swc_comments.rs @@ -191,8 +191,8 @@ impl Comments for ImmutableComments { } pub struct CowComments<'a> { - leading: RefCell>>>, - trailing: RefCell>>>, + leading: RefCell>>, + trailing: RefCell>>, } impl<'a> CowComments<'a> { @@ -202,14 +202,14 @@ impl<'a> CowComments<'a> { comments .leading .iter() - .map(|(&key, value)| (key, Cow::Borrowed(value))) + .map(|(&key, value)| (key, Cow::Borrowed(&value[..]))) .collect(), ), trailing: RefCell::new( comments .trailing .iter() - .map(|(&key, value)| (key, Cow::Borrowed(value))) + .map(|(&key, value)| (key, Cow::Borrowed(&value[..]))) .collect(), ), } @@ -274,7 +274,7 @@ impl Comments for CowComments<'_> { &self, pos: swc_core::common::BytePos, ) -> Option> { - self.leading.borrow().get(&pos).map(|v| (**v).clone()) + self.leading.borrow().get(&pos).map(|v| (**v).to_vec()) } fn add_trailing( @@ -315,7 +315,7 @@ impl Comments for CowComments<'_> { &self, pos: swc_core::common::BytePos, ) -> Option> { - self.trailing.borrow().get(&pos).map(|v| (**v).clone()) + self.trailing.borrow().get(&pos).map(|v| (**v).to_vec()) } fn add_pure_comment(&self, _pos: swc_core::common::BytePos) { diff --git a/turbopack/crates/turbopack-ecmascript/src/utils.rs b/turbopack/crates/turbopack-ecmascript/src/utils.rs index a8ccc105be5ab..2e83eb5a654dd 100644 --- a/turbopack/crates/turbopack-ecmascript/src/utils.rs +++ b/turbopack/crates/turbopack-ecmascript/src/utils.rs @@ -114,11 +114,8 @@ where impl std::io::Write for DisplayWriter<'_, '_> { fn write(&mut self, bytes: &[u8]) -> std::result::Result { self.f - .write_str( - std::str::from_utf8(bytes) - .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?, - ) - .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?; + .write_str(std::str::from_utf8(bytes).map_err(std::io::Error::other)?) + .map_err(std::io::Error::other)?; Ok(bytes.len()) } diff --git a/turbopack/crates/turbopack-node/src/lib.rs b/turbopack/crates/turbopack-node/src/lib.rs index 672736fca9388..606c9022e2b4e 100644 --- a/turbopack/crates/turbopack-node/src/lib.rs +++ b/turbopack/crates/turbopack-node/src/lib.rs @@ -1,7 +1,6 @@ #![feature(min_specialization)] #![feature(arbitrary_self_types)] #![feature(arbitrary_self_types_pointers)] -#![feature(extract_if)] use std::{iter::once, thread::available_parallelism}; diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/ignore-worker.cjs b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/ignore-worker.cjs new file mode 100644 index 0000000000000..2dfd34db8c102 --- /dev/null +++ b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/ignore-worker.cjs @@ -0,0 +1,2 @@ +// Shouldn't cause a worker to be created, but will still be pulled in by `new URL()` +module.exports = 123 diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/index.js b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/index.js index be07538408e45..f92a4088c8a18 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/index.js +++ b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/index.js @@ -11,9 +11,8 @@ import(/* turbopackIgnore: true */ "./ignore.mjs"); require(/* webpackIgnore: true */ "./ignore.cjs"); require(/* turbopackIgnore: true */ "./ignore.cjs"); -// and for workers -new Worker(/* webpackIgnore: true */ "./ignore.mjs"); -new Worker(/* turbopackIgnore: true */ "./ignore.cjs"); +new Worker(/* turbopackIgnore: true */ new URL("./ignore-worker.cjs", import.meta.url)) +new Worker(/* webpackIgnore: true */ new URL("./ignore-worker.cjs", import.meta.url)) export function foo(plugin) { return require(/* turbopackIgnore: true */ plugin) diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_f0a767e9._.js b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_0dd84fce._.js similarity index 97% rename from turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_f0a767e9._.js rename to turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_0dd84fce._.js index 0e788ecb48d5f..e6b674c0d17b7 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_f0a767e9._.js +++ b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_0dd84fce._.js @@ -1,4 +1,4 @@ -(globalThis.TURBOPACK = globalThis.TURBOPACK || []).push(["output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_f0a767e9._.js", { +(globalThis.TURBOPACK = globalThis.TURBOPACK || []).push(["output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_0dd84fce._.js", { "[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/vercel.mjs [test] (ecmascript, async loader)": ((__turbopack_context__) => { diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_f0a767e9._.js.map b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_0dd84fce._.js.map similarity index 100% rename from turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_f0a767e9._.js.map rename to turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_0dd84fce._.js.map diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_e1849155._.js b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_7f940ff3._.js similarity index 78% rename from turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_e1849155._.js rename to turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_7f940ff3._.js index 84eebf6c3bab8..7c4ff91fee634 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_e1849155._.js +++ b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_7f940ff3._.js @@ -1,4 +1,4 @@ -(globalThis.TURBOPACK = globalThis.TURBOPACK || []).push(["output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_e1849155._.js", { +(globalThis.TURBOPACK = globalThis.TURBOPACK || []).push(["output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_7f940ff3._.js", { "[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/vercel.cjs [test] (ecmascript)": (function(__turbopack_context__) { @@ -20,6 +20,11 @@ __turbopack_context__.v(__turbopack_context__.b([ "output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_cjs_3041f295._.js" ])); }}), +"[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/ignore-worker.cjs (static in ecmascript)": ((__turbopack_context__) => { + +var { g: global, __dirname } = __turbopack_context__; +{ +__turbopack_context__.v("/static/ignore-worker.c7cb9893.cjs");}}), "[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/index.js [test] (ecmascript)": ((__turbopack_context__) => { "use strict"; @@ -43,13 +48,12 @@ import(/* turbopackIgnore: true */ "./ignore.mjs"); // this should work for cjs requires too require(/* webpackIgnore: true */ "./ignore.cjs"); require(/* turbopackIgnore: true */ "./ignore.cjs"); -// and for workers -new Worker(/* webpackIgnore: true */ "./ignore.mjs"); -new Worker(/* turbopackIgnore: true */ "./ignore.cjs"); +new Worker(new __turbopack_context__.U(__turbopack_context__.r("[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/ignore-worker.cjs (static in ecmascript)"))); +new Worker(new __turbopack_context__.U(__turbopack_context__.r("[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/ignore-worker.cjs (static in ecmascript)"))); function foo(plugin) { return require(/* turbopackIgnore: true */ plugin); } }}), }]); -//# sourceMappingURL=4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_e1849155._.js.map \ No newline at end of file +//# sourceMappingURL=4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_7f940ff3._.js.map \ No newline at end of file diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_e1849155._.js.map b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_7f940ff3._.js.map similarity index 59% rename from turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_e1849155._.js.map rename to turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_7f940ff3._.js.map index e4b4b27f5d280..0bb350528d36d 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_e1849155._.js.map +++ b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_7f940ff3._.js.map @@ -3,5 +3,5 @@ "sources": [], "sections": [ {"offset": {"line": 6, "column": 0}, "map": {"version":3,"sources":["turbopack:///[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/vercel.cjs"],"sourcesContent":["module.exports = \"turbopack\";\n"],"names":[],"mappings":"AAAA,OAAO,OAAO,GAAG"}}, - {"offset": {"line": 27, "column": 0}, "map": {"version":3,"sources":["turbopack:///[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/index.js"],"sourcesContent":["import(\"./vercel.mjs\").then(console.log);\nimport(/* webpackIgnore: false */ \"./vercel.mjs\").then(console.log);\nconsole.log(require(\"./vercel.cjs\"));\nnew Worker(/* turbopackIgnore: false */ new URL(\"./vercel.cjs\", import.meta.url));\n\n// turbopack shouldn't attempt to bundle these, and they should be preserved in the output\nimport(/* webpackIgnore: true */ \"./ignore.mjs\");\nimport(/* turbopackIgnore: true */ \"./ignore.mjs\");\n\n// this should work for cjs requires too\nrequire(/* webpackIgnore: true */ \"./ignore.cjs\");\nrequire(/* turbopackIgnore: true */ \"./ignore.cjs\");\n\n// and for workers\nnew Worker(/* webpackIgnore: true */ \"./ignore.mjs\");\nnew Worker(/* turbopackIgnore: true */ \"./ignore.cjs\");\n\nexport function foo(plugin) {\n return require(/* turbopackIgnore: true */ plugin)\n}\n"],"names":[],"mappings":";;;;;;;;AAAA,yLAAuB,IAAI,CAAC,QAAQ,GAAG;AACvC,yLAAkD,IAAI,CAAC,QAAQ,GAAG;AAClE,QAAQ,GAAG;AACX,IAAI;AAEJ,0FAA0F;AAC1F,MAAM,CAAC,uBAAuB,GAAG;AACjC,MAAM,CAAC,yBAAyB,GAAG;AAEnC,wCAAwC;AACxC,QAAQ,uBAAuB,GAAG;AAClC,QAAQ,yBAAyB,GAAG;AAEpC,kBAAkB;AAClB,IAAI,OAAO,uBAAuB,GAAG;AACrC,IAAI,OAAO,yBAAyB,GAAG;AAEhC,SAAS,IAAI,MAAM;IACxB,OAAO,QAAQ,yBAAyB,GAAG;AAC7C"}}] + {"offset": {"line": 32, "column": 0}, "map": {"version":3,"sources":["turbopack:///[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/index.js"],"sourcesContent":["import(\"./vercel.mjs\").then(console.log);\nimport(/* webpackIgnore: false */ \"./vercel.mjs\").then(console.log);\nconsole.log(require(\"./vercel.cjs\"));\nnew Worker(/* turbopackIgnore: false */ new URL(\"./vercel.cjs\", import.meta.url));\n\n// turbopack shouldn't attempt to bundle these, and they should be preserved in the output\nimport(/* webpackIgnore: true */ \"./ignore.mjs\");\nimport(/* turbopackIgnore: true */ \"./ignore.mjs\");\n\n// this should work for cjs requires too\nrequire(/* webpackIgnore: true */ \"./ignore.cjs\");\nrequire(/* turbopackIgnore: true */ \"./ignore.cjs\");\n\nnew Worker(/* turbopackIgnore: true */ new URL(\"./ignore-worker.cjs\", import.meta.url))\nnew Worker(/* webpackIgnore: true */ new URL(\"./ignore-worker.cjs\", import.meta.url))\n\nexport function foo(plugin) {\n return require(/* turbopackIgnore: true */ plugin)\n}\n"],"names":[],"mappings":";;;;;;;;AAAA,yLAAuB,IAAI,CAAC,QAAQ,GAAG;AACvC,yLAAkD,IAAI,CAAC,QAAQ,GAAG;AAClE,QAAQ,GAAG;AACX,IAAI;AAEJ,0FAA0F;AAC1F,MAAM,CAAC,uBAAuB,GAAG;AACjC,MAAM,CAAC,yBAAyB,GAAG;AAEnC,wCAAwC;AACxC,QAAQ,uBAAuB,GAAG;AAClC,QAAQ,yBAAyB,GAAG;AAEpC,IAAI;AACJ,IAAI;AAEG,SAAS,IAAI,MAAM;IACxB,OAAO,QAAQ,yBAAyB,GAAG;AAC7C"}}] } \ No newline at end of file diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_03bfbde6.js b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_a67357ee.js similarity index 57% rename from turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_03bfbde6.js rename to turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_a67357ee.js index c7b89bd2ebbe7..2249b5ad7ff06 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_03bfbde6.js +++ b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_a67357ee.js @@ -1,6 +1,6 @@ (globalThis.TURBOPACK = globalThis.TURBOPACK || []).push([ - "output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_03bfbde6.js", + "output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_a67357ee.js", {}, - {"otherChunks":["output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_f0a767e9._.js","output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_e1849155._.js"],"runtimeModuleIds":["[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/index.js [test] (ecmascript)"]} + {"otherChunks":["output/4c35f_tests_snapshot_imports_ignore-comments_input_vercel_mjs_0dd84fce._.js","output/4e721_crates_turbopack-tests_tests_snapshot_imports_ignore-comments_input_7f940ff3._.js"],"runtimeModuleIds":["[project]/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/input/index.js [test] (ecmascript)"]} ]); // Dummy runtime \ No newline at end of file diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_03bfbde6.js.map b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_a67357ee.js.map similarity index 100% rename from turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_03bfbde6.js.map rename to turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/output/b1abf_turbopack-tests_tests_snapshot_imports_ignore-comments_input_index_a67357ee.js.map diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/static/ignore-worker.c7cb9893.cjs b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/static/ignore-worker.c7cb9893.cjs new file mode 100644 index 0000000000000..de83f1419f091 --- /dev/null +++ b/turbopack/crates/turbopack-tests/tests/snapshot/imports/ignore-comments/static/ignore-worker.c7cb9893.cjs @@ -0,0 +1,2 @@ +// Shouldn't cause a worker to be created, but will still be pulled in by `new URL()` +module.exports = 123 \ No newline at end of file diff --git a/turbopack/crates/turbopack-trace-server/Cargo.toml b/turbopack/crates/turbopack-trace-server/Cargo.toml index 423133b510cc2..799dab84c7614 100644 --- a/turbopack/crates/turbopack-trace-server/Cargo.toml +++ b/turbopack/crates/turbopack-trace-server/Cargo.toml @@ -15,6 +15,7 @@ bench = false anyhow = { workspace = true, features = ["backtrace"] } either = { workspace = true } flate2 = { version = "1.0.28" } +hashbrown = { workspace = true, features = ["raw"] } indexmap = { workspace = true, features = ["serde"] } itertools = { workspace = true } postcard = { workspace = true } diff --git a/turbopack/crates/turbopack-trace-server/src/bottom_up.rs b/turbopack/crates/turbopack-trace-server/src/bottom_up.rs index 26ebf157b88ee..76397fc0ea558 100644 --- a/turbopack/crates/turbopack-trace-server/src/bottom_up.rs +++ b/turbopack/crates/turbopack-trace-server/src/bottom_up.rs @@ -1,6 +1,6 @@ use std::{env, sync::Arc}; -use rustc_hash::FxHashMap; +use hashbrown::HashMap; use crate::{ span::{SpanBottomUp, SpanIndex}, @@ -10,7 +10,7 @@ use crate::{ pub struct SpanBottomUpBuilder { // These values won't change after creation: pub self_spans: Vec, - pub children: FxHashMap, + pub children: HashMap, pub example_span: SpanIndex, } @@ -18,7 +18,7 @@ impl SpanBottomUpBuilder { pub fn new(example_span: SpanIndex) -> Self { Self { self_spans: vec![], - children: FxHashMap::default(), + children: HashMap::default(), example_span, } } @@ -42,7 +42,7 @@ pub fn build_bottom_up_graph<'a>( .ok() .and_then(|s| s.parse().ok()) .unwrap_or(usize::MAX); - let mut roots = FxHashMap::default(); + let mut roots: HashMap = HashMap::default(); // unfortunately there is a rustc bug that fails the typechecking here // when using Either. This error appears diff --git a/turbopack/crates/turbopack-trace-server/src/lib.rs b/turbopack/crates/turbopack-trace-server/src/lib.rs index 0afff5689eb13..ef164659a8a6e 100644 --- a/turbopack/crates/turbopack-trace-server/src/lib.rs +++ b/turbopack/crates/turbopack-trace-server/src/lib.rs @@ -1,5 +1,4 @@ #![feature(iter_intersperse)] -#![feature(hash_raw_entry)] #![feature(box_patterns)] use std::{hash::BuildHasherDefault, path::PathBuf, sync::Arc}; diff --git a/turbopack/crates/turbopack-trace-server/src/main.rs b/turbopack/crates/turbopack-trace-server/src/main.rs index 1cb04575731d3..6e49291fca768 100644 --- a/turbopack/crates/turbopack-trace-server/src/main.rs +++ b/turbopack/crates/turbopack-trace-server/src/main.rs @@ -1,5 +1,4 @@ #![feature(iter_intersperse)] -#![feature(hash_raw_entry)] #![feature(box_patterns)] use std::{hash::BuildHasherDefault, sync::Arc}; diff --git a/turbopack/crates/turbopack-trace-server/src/span.rs b/turbopack/crates/turbopack-trace-server/src/span.rs index 20d025862b3af..0cca4825fbb42 100644 --- a/turbopack/crates/turbopack-trace-server/src/span.rs +++ b/turbopack/crates/turbopack-trace-server/src/span.rs @@ -3,7 +3,7 @@ use std::{ sync::{Arc, OnceLock}, }; -use rustc_hash::FxHashMap; +use hashbrown::HashMap; use crate::timestamp::Timestamp; @@ -64,7 +64,7 @@ pub struct SpanTimeData { pub struct SpanExtra { pub graph: OnceLock>, pub bottom_up: OnceLock>>, - pub search_index: OnceLock>>, + pub search_index: OnceLock>>, } #[derive(Default)] diff --git a/turbopack/crates/turbopack-trace-server/src/span_ref.rs b/turbopack/crates/turbopack-trace-server/src/span_ref.rs index 300c0c15032cc..8f42f30527a70 100644 --- a/turbopack/crates/turbopack-trace-server/src/span_ref.rs +++ b/turbopack/crates/turbopack-trace-server/src/span_ref.rs @@ -5,8 +5,9 @@ use std::{ vec, }; +use hashbrown::HashMap; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; use crate::{ bottom_up::build_bottom_up_graph, @@ -414,9 +415,9 @@ impl<'a> SpanRef<'a> { }) } - fn search_index(&self) -> &FxHashMap> { + fn search_index(&self) -> &HashMap> { self.extra().search_index.get_or_init(|| { - let mut index: FxHashMap> = FxHashMap::default(); + let mut index: HashMap> = HashMap::default(); let mut queue = VecDeque::with_capacity(8); queue.push_back(*self); while let Some(span) = queue.pop_front() { diff --git a/turbopack/crates/turbopack-trace-server/src/store.rs b/turbopack/crates/turbopack-trace-server/src/store.rs index e520e187aaa0c..b37892907b3c2 100644 --- a/turbopack/crates/turbopack-trace-server/src/store.rs +++ b/turbopack/crates/turbopack-trace-server/src/store.rs @@ -1,7 +1,6 @@ use std::{ cmp::{max, min}, env, - mem::replace, num::NonZeroUsize, sync::{atomic::AtomicU64, OnceLock}, }; @@ -266,7 +265,7 @@ impl Store { outdated_spans.insert(span_index); let span = &mut self.spans[span_index.get()]; - let old_parent = replace(&mut span.parent, Some(parent)); + let old_parent = span.parent.replace(parent); let old_parent = if let Some(parent) = old_parent { outdated_spans.insert(parent); &mut self.spans[parent.get()]