diff --git a/packages/next-swc/crates/core/Cargo.toml b/packages/next-swc/crates/core/Cargo.toml index 057f502c92fb1..f40c10d5c46e5 100644 --- a/packages/next-swc/crates/core/Cargo.toml +++ b/packages/next-swc/crates/core/Cargo.toml @@ -27,8 +27,7 @@ swc_common = { version = "0.17.19", features = ["concurrent", "sourcemap"] } swc_ecma_loader = { version = "0.29.0", features = ["node", "lru"] } swc_ecmascript = { version = "0.143.0", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] } swc_cached = "0.1.1" -tracing = { version = "0.1.32", features = ["release_max_level_off"] } - +tracing = { version = "0.1.32", features = ["release_max_level_info"] } [dev-dependencies] swc_ecma_transforms_testing = "0.77.0" diff --git a/packages/next-swc/crates/emotion/Cargo.toml b/packages/next-swc/crates/emotion/Cargo.toml index df6db10f58799..56bcf318f1fc4 100644 --- a/packages/next-swc/crates/emotion/Cargo.toml +++ b/packages/next-swc/crates/emotion/Cargo.toml @@ -21,6 +21,8 @@ sourcemap = "6.0.1" swc_atoms = "0.2.11" swc_common = { version = "0.17.19", features = ["concurrent", "sourcemap"] } swc_ecmascript = { version = "0.143.0", features = ["codegen", "utils", "visit"] } +swc_trace_macro = "0.1.1" +tracing = { version = "0.1.32", features = ["release_max_level_info"] } [dev-dependencies] swc_ecma_transforms_testing = "0.77.0" diff --git a/packages/next/build/swc/index.d.ts b/packages/next/build/swc/index.d.ts index 779b53084a455..f291612476522 100644 --- a/packages/next/build/swc/index.d.ts +++ b/packages/next/build/swc/index.d.ts @@ -5,3 +5,4 @@ export function minify(src: string, options: any): Promise export function minifySync(src: string, options: any): string export function bundle(options: any): Promise export function parse(src: string, options: any): any +export function initCustomTraceSubscriber(traceFileName?: string): void diff --git a/packages/next/build/swc/index.js b/packages/next/build/swc/index.js index 8c99c2d9e31ea..bc15a25ef731c 100644 --- a/packages/next/build/swc/index.js +++ b/packages/next/build/swc/index.js @@ -192,6 +192,8 @@ function loadNative() { }, getTargetTriple: bindings.getTargetTriple, + initCustomTraceSubscriber: bindings.initCustomTraceSubscriber, + teardownTraceSubscriber: bindings.teardownTraceSubscriber, } return nativeBindings } @@ -251,3 +253,35 @@ export function getBinaryMetadata() { target: bindings?.getTargetTriple?.(), } } + +/** + * Initialize trace subscriber to emit traces. + * + * Returns an internal object to guard async flush emission if subscriber is initialized, caller should manually + * tear it down via `teardownTraceSubscriber`. + */ +export const initCustomTraceSubscriber = (() => { + let guard + + return (filename) => { + if (!guard) { + // Wasm binary doesn't support trace emission + let bindings = loadNative() + guard = bindings.initCustomTraceSubscriber(filename) + } + + return guard + } +})() + +export const teardownTraceSubscriber = (() => { + let bindings + + return (guard) => { + if (!bindings && !!guard) { + // Wasm binary doesn't support trace emission + bindings = loadNative() + return bindings.teardownTraceSubscriber(guard) + } + } +})() diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 9166fefdf4834..10a0ece62a842 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -88,6 +88,22 @@ const devtoolRevertWarning = execOnce( let loggedSwcDisabled = false let loggedIgnoredCompilerOptions = false +let swcTraceFlushGuard: unknown = null + +/** + * Teardown swc's trace subscriber if there's an initialized flush guard exists. + * + * This is workaround to amend behavior with process.exit + * (https://github.com/vercel/next.js/blob/4db8c49cc31e4fc182391fae6903fb5ef4e8c66e/packages/next/bin/next.ts#L134=) + * seems preventing napi's cleanup hook execution (https://github.com/swc-project/swc/blob/main/crates/node/src/util.rs#L48-L51=), + * + * instead parent process manually drops guard when process gets signal to exit. + */ +process.on('exit', () => { + if (swcTraceFlushGuard) { + require('./swc')?.teardownTraceSubscriber?.(swcTraceFlushGuard) + } +}) function getOptimizedAliases(): { [pkg: string]: string } { const stubWindowFetch = path.join(__dirname, 'polyfills', 'fetch', 'index.js') @@ -439,6 +455,20 @@ export default async function getBaseWebpackConfig( } const getBabelOrSwcLoader = (isMiddleware: boolean) => { + if ( + useSWCLoader && + config?.experimental?.swcTrace?.enabled && + !swcTraceFlushGuard + ) { + // This will init subscribers once only in a single process lifecycle, + // even though it can be called multiple times. + // Subscriber need to be initialized _before_ any actual swc's call (transform, etcs) + // to collect correct trace spans when they are called. + swcTraceFlushGuard = require('./swc')?.initCustomTraceSubscriber?.( + config?.experimental?.swcTrace?.traceFileName + ) + } + return useSWCLoader ? { loader: 'next-swc-loader', diff --git a/packages/next/server/config-shared.ts b/packages/next/server/config-shared.ts index 029d399571559..48936d0246c5d 100644 --- a/packages/next/server/config-shared.ts +++ b/packages/next/server/config-shared.ts @@ -128,6 +128,10 @@ export interface ExperimentalConfig { skipDefaultConversion?: boolean } > + swcTrace?: { + enabled: boolean + traceFileName?: string + } } /**