---
title: "SWC Compiler"
description: "The SWC compiler integration in Next.js serves as the high-performance native compilation and minification engine, replacing traditional JavaScript-based toolchains for parsing, transforming, and b..."
last_updated: "2026-10-12T06:38:56.85659+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/build-system/swc-compiler"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/next/src/build/swc/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts)
- [packages/next/taskfile-swc.js](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/taskfile-swc.js)
- [packages/next/src/build/swc/options.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/options.ts)
- [packages/next/taskfile.js](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/taskfile.js)
- [packages/next/src/build/webpack/loaders/next-swc-loader.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/webpack/loaders/next-swc-loader.ts)
- [packages/next/src/build/swc/generated-wasm.d.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/generated-wasm.d.ts)
- [packages/next/src/build/swc/install-bindings.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/install-bindings.ts)
- [packages/next/src/cli/next-info.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/cli/next-info.ts)
- [packages/next-mdx/mdx-rs-loader.js](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-mdx/mdx-rs-loader.js)
- [packages/next/src/build/webpack/loaders/lightningcss-loader/src/loader.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/webpack/loaders/lightningcss-loader/src/loader.ts)
- [packages/next/src/build/swc/jest-transformer.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/jest-transformer.ts)
- [packages/next/src/build/next-config-ts/require-hook.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/next-config-ts/require-hook.ts)
- [packages/next/src/lib/download-swc.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/lib/download-swc.ts)
- [packages/next-swc/package.json](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-swc/package.json)
- [packages/next-codemod/bin/transform.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-codemod/bin/transform.ts)
- [packages/next/src/build/next-config-ts/transpile-config.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/next-config-ts/transpile-config.ts)
- [packages/next/src/build/swc/loaderWorkerPool.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/loaderWorkerPool.ts)
- [packages/next/src/server/dev/dev-validation-worker-pool.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/dev/dev-validation-worker-pool.ts)
- [packages/next/src/build/babel/preset.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/babel/preset.ts)
- [packages/next/src/build/swc/generated-native.d.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/generated-native.d.ts)
- [packages/next/src/build/swc/types.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/types.ts)
- [packages/next/src/build/webpack/loaders/next-app-loader/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/webpack/loaders/next-app-loader/index.ts)
</details>

## Overview

The SWC compiler integration in Next.js serves as the high-performance native compilation and minification engine, replacing traditional JavaScript-based toolchains for parsing, transforming, and bundling application source code. Built around Rust-powered Node-API (NAPI-RS) bindings and WebAssembly fallbacks, the compiler subsystem powers core tasks including JavaScript and TypeScript transpilation, React JSX/TSX transformations, CSS processing via Lightning CSS, MDX compilation, and incremental building with Turbopack. By handling computationally intensive abstract syntax tree (AST) operations natively, it solves performance bottlenecks associated with large-scale JavaScript toolchains while maintaining seamless interoperability with [Webpack loaders](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/build-system/webpack-loaders) and testing environments.

Sources: [packages/next/src/build/swc/index.ts:1307-1324](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts#L1307-L1324), [packages/next/src/build/webpack/loaders/next-swc-loader.ts:31-34](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/webpack/loaders/next-swc-loader.ts#L31-L34), [packages/next/src/build/swc/generated-native.d.ts:74-78](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/generated-native.d.ts#L74-L78)

## Binary Discovery and Loading Lifecycle

### Architecture Target Resolution and Native Binding Loading

The SWC compiler initializes by attempting to load native Node-API bindings compiled for the host operating system and CPU architecture. The loader resolves environment-specific binaries through NAPI-RS packages (`@next/swc-<triple>`), querying the loaded binary's target metadata via `getTargetTriple`. 

```typescript
export declare function getTargetTriple(): string
```
Sources: [packages/next/src/build/swc/generated-native.d.ts:108-108](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/generated-native.d.ts#L108-L108)

```typescript
export function getBinaryMetadata() {
  return {
    target: loadedBindings?.getTargetTriple?.(),
  }
}
```
Sources: [packages/next/src/build/swc/index.ts:1864-1868](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts#L1864-L1868)

### WebAssembly Fallback Mechanics

When native Node-API bindings cannot be loaded or resolved, the loader falls back to WebAssembly implementations. The loading sequence checks for a local test directory via `NEXT_TEST_WASM_DIR` before iterating through production WebAssembly distribution packages.

```typescript
async function loadWasmRawBindings(importPath = ''): Promise<RawWasmBindings> {
  let attempts = []

  // Used by `run-tests` to force use of a locally-built wasm binary. This environment variable is
  // unstable and subject to change.
  const testWasmDir = process.env.NEXT_TEST_WASM_DIR

  if (testWasmDir) {
    // assume these are node.js bindings and don't need a call to `.default()`
    const rawBindings = await import(
      pathToFileURL(path.join(testWasmDir, 'wasm.js')).toString()
    )
    infoLog(`next-swc build: wasm build ${testWasmDir}`)
    return rawBindings
  } else {
    for (let pkg of ['@next/swc-wasm-nodejs', '@next/swc-wasm-web']) {
```
Sources: [packages/next/src/build/swc/index.ts:1328-1343](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts#L1328-L1343)

```markdown
> [!NOTE]
> The WebAssembly initialization path distinguishes between Node.js and browser or web environments. When loading `@next/swc-wasm-web`, the loader explicitly invokes the exported `default()` initialization method to set up memory instances before binding execution.
```
Sources: [packages/next/src/build/swc/index.ts:1355-1361](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts#L1355-L1361)

The fallback mechanism records detailed failure diagnostics when modules are missing or encounter load errors, collecting failure attempts until a valid binding instance is established or all options are exhausted.

```typescript
} catch (e: any) {
  // Only log attempts for loading wasm when loading as fallback
  if (importPath) {
    if (e?.code === 'ERR_MODULE_NOT_FOUND') {
      attempts.push(`Attempted to load ${pkg}, but it was not installed`)
    } else {
      attempts.push(
        `Attempted to load ${pkg}, but an error occurred: ${e.message ?? e}`
      )
    }
```
Sources: [packages/next/src/build/swc/index.ts:1364-1373](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts#L1364-L1373)

### Package Caching and Binary Download Management

If required packages are absent from local directories, the extraction subsystem downloads compressed distribution tarballs from the active registry into a designated cache directory (`next-swc`).

```typescript
const cacheDirectory = getCacheDirectory(
  'next-swc',
  process.env['NEXT_SWC_PATH']
)

const extractFromTar = () =>
  x({
    file: path.join(cacheDirectory, tarFileName),
    cwd: outputDirectory,
    strip: 1,
  })

if (!fs.existsSync(path.join(cacheDirectory, tarFileName))) {
  Log.info(`Downloading swc package ${pkgName}... to ${cacheDirectory}`)
  await fs.promises.mkdir(cacheDirectory, { recursive: true })
  const tempFile = path.join(
    cacheDirectory,
    `${tarFileName}.temp-${Date.now()}`
  )

  const registry = getRegistry()

  const downloadUrl = `${registry}${pkgName}/-/${tarFileName}`

  await fetch(downloadUrl).then((res) => {
```
Sources: [packages/next/src/lib/download-swc.ts:17-41](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/lib/download-swc.ts#L17-L41)

| Download Function | Target Package Variant | Destination Directory Parameter |
| :--- | :--- | :--- |
| `downloadNativeNextSwc` | `@next/swc-${triple}` | `bindingsDirectory` |
| `downloadWasmSwc` | `@next/swc-wasm-${variant}` | `wasmDirectory` |

Sources: [packages/next/src/lib/download-swc.ts:108-146](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/lib/download-swc.ts#L108-L146)

The cache manager restricts resource bloat by enforcing a strict retention ceiling, automatically pruning older version directories exceeding the configured storage limit.

```typescript
const MAX_VERSIONS_TO_CACHE = 8
```
Sources: [packages/next/src/lib/download-swc.ts:10-10](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/lib/download-swc.ts#L10-L10)

```typescript
const cacheFiles = await fs.promises.readdir(cacheDirectory)

if (cacheFiles.length > MAX_VERSIONS_TO_CACHE) {
  cacheFiles.sort((a, b) => {
    if (a.length < b.length) return -1
    return a.localeCompare(b)
  })

  // prune oldest versions in cache
  for (let i = 0; i++; i < cacheFiles.length - MAX_VERSIONS_TO_CACHE) {
    await fs.promises
      .unlink(path.join(cacheDirectory, cacheFiles[i]))
      .catch(() => {})
  }
}
```
Sources: [packages/next/src/lib/download-swc.ts:91-105](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/lib/download-swc.ts#L91-L105)

## Synchronous Installation and On-Demand Fetching

### Deferred Bundle Loading

The SWC installation workflow avoids eager loading of compiler modules by deferring package requirements. Loading the main index module transitively pulls in configuration dependencies that evaluate `process.env.NODE_ENV` at require time. By routing installation requests through an isolated installer script, the process guarantees environment variables are configured before React or bundler modules initialize.

```typescript
/**
 * This module provides a way to install SWC bindings without eagerly loading the entire swc/index module.
 *
 * The swc/index module can transitively load other modules (like webpack-config) that import React,
 * and React's entry point checks process.env.NODE_ENV at require time to decide whether to load
 * the development or production bundle. By deferring the require of swc/index until this function
 * is called, we ensure NODE_ENV is set before React is loaded.
 */

/**
 * Loads and caches the native bindings. This is idempotent and should be called early so bindings
 * can be accessed synchronously later.
 *
 * @param useWasmBinary - Whether to use WASM bindings instead of native bindings
 */
export async function installBindings(
  useWasmBinary: boolean = false
): Promise<void> {
  // Lazy require to avoid loading swc/index (and transitively webpack-config/React)
  // before NODE_ENV is set
  const { loadBindings } = require('./index') as typeof import('./index')
  await loadBindings(useWasmBinary)
}
```
Sources: [packages/next/src/build/swc/install-bindings.ts:1-23](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/install-bindings.ts#L1-L23)

> [!NOTE]
> The `installBindings` helper is fully idempotent and caches native bindings internally so that subsequent synchronous compiler accesses complete without asynchronous overhead.
Sources: [packages/next/src/build/swc/install-bindings.ts:11-18](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/install-bindings.ts#L11-L18)

### On-Demand Binary Extraction and Pruning

When native bindings or WebAssembly packages are missing from local directories, the extraction subsystem streams tarball archives from the package registry into temporary cache locations before unpacking them. 

```typescript
if (!fs.existsSync(path.join(cacheDirectory, tarFileName))) {
  Log.info(`Downloading swc package ${pkgName}... to ${cacheDirectory}`)
  await fs.promises.mkdir(cacheDirectory, { recursive: true })
  const tempFile = path.join(
    cacheDirectory,
    `${tarFileName}.temp-${Date.now()}`
  )

  const registry = getRegistry()

  const downloadUrl = `${registry}${pkgName}/-/${tarFileName}`

  await fetch(downloadUrl).then((res) => {
    const { ok, body } = res
    if (!ok || !body) {
      Log.error(`Failed to download swc package from ${downloadUrl}`)
    }

    if (!ok) {
      throw new Error(`request failed with status ${res.status}`)
    }
    if (!body) {
      throw new Error('request failed with empty body')
    }
    const cacheWriteStream = fs.createWriteStream(tempFile)
    return body.pipeTo(
      new WritableStream({
        write(chunk) {
          return new Promise<void>((resolve, reject) =>
            cacheWriteStream.write(chunk, (error) => {
              if (error) {
                reject(error)
                return
              }

              resolve()
            })
          )
        },
        close() {
          return new Promise<void>((resolve, reject) =>
            cacheWriteStream.close((error) => {
              if (error) {
                reject(error)
                return
              }

              resolve()
            })
          )
        },
      })
    )
  })

  await fs.promises.access(tempFile) // ensure the temp file existed
  await fs.promises.rename(tempFile, path.join(cacheDirectory, tarFileName))
} else {
  Log.info(`Using cached swc package ${pkgName}...`)
}
```
Sources: [packages/next/src/lib/download-swc.ts:29-88](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/lib/download-swc.ts#L29-L88)

To prevent unbounded disk utilization in the cache directory, the manager evaluates cached version counts and prunes stale artifacts when entries exceed the retention threshold.

```typescript
const cacheFiles = await fs.promises.readdir(cacheDirectory)

if (cacheFiles.length > MAX_VERSIONS_TO_CACHE) {
  cacheFiles.sort((a, b) => {
    if (a.length < b.length) return -1
    return a.localeCompare(b)
  })

  // prune oldest versions in cache
  for (let i = 0; i++; i < cacheFiles.length - MAX_VERSIONS_TO_CACHE) {
    await fs.promises
      .unlink(path.join(cacheDirectory, cacheFiles[i]))
      .catch(() => {})
  }
}
```
Sources: [packages/next/src/lib/download-swc.ts:91-105](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/lib/download-swc.ts#L91-L105)

### Supported NAPI Targets

The [build pipeline](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/build-system/build-pipeline) defines target triplets and architectures for native compilation and packaging within the NAPI configuration manifest.

| Target Architecture & OS Triple | Description |
| :--- | :--- |
| `x86_64-apple-darwin` | 64-bit macOS (Intel) |
| `x86_64-pc-windows-msvc` | 64-bit Windows (MSVC) |
| `x86_64-unknown-linux-gnu` | 64-bit Linux (GLIBC) |
| `i686-pc-windows-msvc` | 32-bit Windows (MSVC) |
| `aarch64-unknown-linux-gnu` | ARM64 Linux (GLIBC) |
| `aarch64-apple-darwin` | ARM64 macOS (Apple Silicon) |
| `x86_64-unknown-linux-musl` | 64-bit Linux (MUSL) |
| `aarch64-unknown-linux-musl` | ARM64 Linux (MUSL) |
| `aarch64-pc-windows-msvc` | ARM64 Windows (MSVC) |
| `wasm32-wasip1-threads` | WebAssembly WASIp1 with threads |

Sources: [packages/next-swc/package.json:25-38](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-swc/package.json#L25-L38)

## Compilation Options and Transform Normalization

### Parser and Base Options Construction

SWC compiler options are assembled through `getBaseSWCOptions`, which inspects configuration files, compiler flags, and bundle layers to construct unified parser and transformation targets. The parser configuration automatically distinguishes between TypeScript and ECMAScript syntax based on file extensions while enabling dynamic imports and import attributes.

```typescript
export function getParserOptions({ filename, jsConfig, ...rest }: any) {
  const isTSFile = filename.endsWith('.ts')
  const hasTsSyntax = isTypeScriptFile(filename)
  const enableDecorators = Boolean(
    jsConfig?.compilerOptions?.experimentalDecorators
  )
  return {
    ...rest,
    syntax: hasTsSyntax ? 'typescript' : 'ecmascript',
    dynamicImport: true,
    decorators: enableDecorators,
    // Exclude regular TypeScript files from React transformation to prevent e.g. generic parameters and angle-bracket type assertion from being interpreted as JSX tags.
    [hasTsSyntax ? 'tsx' : 'jsx']: !isTSFile,
    importAssertions: true,
  }
}
```
Sources: [packages/next/src/build/swc/options.ts:39-54](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/options.ts#L39-L54)

> [!WARNING]
> Regular TypeScript files exclude React transformation inside the parser config to prevent generic parameters and angle-bracket type assertions from being misparsed as JSX tags.
Sources: [packages/next/src/build/swc/options.ts:50-51](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/options.ts#L50-L51)

### React and Styling Transform Normalization

The transformation pipeline normalizes compiler settings for React, Emotion, and styled-components depending on whether code runs in server or client layers. When Emotion options are configured and the bundle does not target the React server layer, the import source defaults to `@emotion/react`.

```typescript
react: {
  importSource:
    jsConfig?.compilerOptions?.jsxImportSource ??
    (compilerOptions?.emotion && !isReactServerLayer
      ? '@emotion/react'
      : 'react'),
  runtime: 'automatic',
  pragmaFrag: 'React.Fragment',
  throwIfNamespace: true,
  development: !!development,
  useBuiltins: true,
  refresh: !!hasReactRefresh,
},
```
Sources: [packages/next/src/build/swc/options.ts:153-165](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/options.ts#L153-L165)

CSS-in-JS transformations for Emotion and styled-components are omitted on the server layer when building Server Components, avoiding client-only integration overhead during server compilation.

```typescript
// Disable css-in-js libs (without client-only integration) transform on server layer for server components
...(!isReactServerLayer && {
  emotion: getEmotionOptions(compilerOptions?.emotion, development),
  styledComponents: getStyledComponentsOptions(
    compilerOptions?.styledComponents,
    development
  ),
}),
```
Sources: [packages/next/src/build/swc/options.ts:215-222](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/options.ts#L215-L222)

Emotion label generation and auto-formatting options normalize user-provided configurations through explicit conditional branches.

| Emotion Config Property | Default / Behavior |
| :--- | :--- |
| `enabled` | `true` when emotion options are present |
| `autoLabel` | Determined by string value (`dev-only`, `always`, `never`) |
| `sourcemap` | Enabled in development mode when source maps are requested |
| `importMap` | Custom import mappings passed directly to the transform |

Sources: [packages/next/src/build/swc/options.ts:291-312](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/options.ts#L291-L312)

## Parallel Transformation and Worker Pools

### Loader Worker Pool Scheduling

SWC bindings register a worker scheduler via `runLoaderWorkerPool` to spawn and manage individual Node.js worker threads for parallel AST processing. Each worker pool is keyed by a composite identifier combining the current working directory and filename.

```typescript
function getPoolId(cwd: string, filename: string) {
  return `${cwd}:${filename}`
}

export async function runLoaderWorkerPool(
  bindings: typeof import('./generated-native'),
  bindingPath: string
) {
  bindings.registerWorkerScheduler(
    (creation) => {
      const {
        options: { filename, cwd },
      } = creation

      const poolId = getPoolId(cwd, filename)

      const worker = new Worker(/* turbopackIgnore: true*/ filename, {
        workerData: {
          bindingPath,
          cwd,
        },
      })

      // This will cause handing when run in jest worker, but not as a first level thread of nodejs thread
      // worker.unref()

      const workers =
        loaderWorkers[poolId] || (loaderWorkers[poolId] = new Map())

      workers.set(worker.threadId, worker)
    },
    (termination) => {
      const {
        options: { filename, cwd },
        workerId,
      } = termination

      const poolId = getPoolId(cwd, filename)
      const workers = loaderWorkers[poolId]
      workers.get(workerId)?.terminate()
      workers.delete(workerId)
    }
  )
```
Sources: [packages/next/src/build/swc/loaderWorkerPool.ts:5-47](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/loaderWorkerPool.ts#L5-L47)

> [!WARNING]
> Calling `worker.unref()` causes hanging when workers run inside a Jest environment; it remains commented out in the implementation to ensure proper lifecycle management under test runners.
Sources: [packages/next/src/build/swc/loaderWorkerPool.ts:28-29](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/loaderWorkerPool.ts#L28-L29)

### Development Validation Worker Pool

The development validation system maintains a dedicated worker pool instance via `installDevValidationWorker` to offload validation logic and isolate runtime module states from the main thread.

```typescript
const worker = new Worker(workerPath, {
  maxRetries: 0,
  numWorkers: 1,
  // Always worker-threads, regardless of `experimental.workerThreads`.
  // Unlike the `'use cache'` probe (which follows the flag), validation has
  // no reason to prefer a child process: it doesn't need process-level
  // isolation (a worker thread already has its own V8 heap and module
  // registry, so the reloaded route is isolated from the main thread), and
  // threads let a superseded validation be aborted mid-run through a shared
  // `SharedArrayBuffer`, which a separate process can't receive. Threads
  // also carry the transported Flight bytes as typed arrays via structured
  // clone, with no JSON round-trip to corrupt them.
  enableWorkerThreads: true,
  // Listing the method explicitly tells jest-worker to skip the discovery
  // `require()` it would otherwise do in the parent process to enumerate
  // the module's exports. This worker's top-level imports (`require-hook`,
  // `node-environment`) run runtime setup meant only for the isolated
  // worker thread, so they must not be evaluated in the parent.
  exposedMethods: [
    'runDevValidation',
    'applyHmrUpdate',
    'invalidateCaches',
  ],
  forkOptions: {
    env: {
      ...process.env,
      NODE_OPTIONS: workerNodeOptions,
    },
  },
}) as Worker & ValidationPool
```
Sources: [packages/next/src/server/dev/dev-validation-worker-pool.ts:157-186](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/dev/dev-validation-worker-pool.ts#L157-L186)

| Worker Configuration Property | Value / Behavior |
| :--- | :--- |
| `maxRetries` | `0` |
| `numWorkers` | `1` |
| `enableWorkerThreads` | `true` |
| `exposedMethods` | `runDevValidation`, `applyHmrUpdate`, `invalidateCaches` |

Sources: [packages/next/src/server/dev/dev-validation-worker-pool.ts:158-179](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/dev/dev-validation-worker-pool.ts#L158-L179)

> [!NOTE]
> Thread-based validation uses a `SharedArrayBuffer` to mirror abort signals across thread boundaries, allowing an in-flight validation run to be canceled mid-run at its next depth boundary.
Sources: [packages/next/src/server/dev/dev-validation-worker-pool.ts:234-243](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/dev/dev-validation-worker-pool.ts#L234-L243)

## Configuration and Jest Transpilation Hooks

### TypeScript Configuration Transpilation

The configuration transpilation pipeline transforms TypeScript configuration files such as `next.config.ts` into executable CommonJS modules using SWC. The `transpileConfig` function coordinates loading the local `tsconfig.json` compiler options, configuring parser syntax, and falling back or executing through native bindings.

```typescript
function resolveSWCOptions(
  cwd: string,
  compilerOptions: RelevantCompilerOptions
): SWCOptions {
  return {
    jsc: {
      parser: {
        syntax: 'typescript',
      },
      ...(compilerOptions.paths ? { paths: compilerOptions.paths } : {}),
      ...(compilerOptions.baseUrl
        ? // Needs to be an absolute path.
          { baseUrl: path.resolve(cwd, compilerOptions.baseUrl) }
        : compilerOptions.paths
          ? // If paths is given, baseUrl is required.
            { baseUrl: compilerOptions.pathsBasePath ?? cwd }
          : {}),
    },
    module: {
      type: 'commonjs',
    },
    isModule: 'unknown',
    env: {
      targets: {
        // Setting the Node.js version can reduce unnecessary code generation.
        node: process?.versions?.node ?? '20.19.0',
      },
    },
  } satisfies SWCOptions
}
```
Sources: [packages/next/src/build/next-config-ts/transpile-config.ts:14-43](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/next-config-ts/transpile-config.ts#L14-L43)

> [!NOTE]
> If `process.env.__NEXT_NODE_NATIVE_TS_LOADER_ENABLED` is set to `true`, the pipeline checks Node.js built-in TypeScript strip or transform features before falling back to SWC-based transpilation.
Sources: [packages/next/src/build/next-config-ts/transpile-config.ts:69-78](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/next-config-ts/transpile-config.ts#L69-L78)

When compiling configuration code, `handleCJS` inspects the resulting source for `require(` expressions. If present, it activates `registerHook` to dynamically intercept subsequent module imports during execution.

```typescript
// register require hook only if require exists
if (code.includes('require(')) {
  registerHook(swcOptions)
  hasRequire = true
}

```
Sources: [packages/next/src/build/next-config-ts/transpile-config.ts:133-138](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/next-config-ts/transpile-config.ts#L133-L138)

### Require Hook Management

The require hook utility overrides Node.js extension handlers (`.js`, `.ts`, `.cts`, `.mts`, `.cjs`, `.mjs`) to synchronously transpile required files on the fly.

```typescript
for (const ext of extensions) {
  const oldHook = require.extensions[ext] ?? oldJSHook
  require.extensions[ext] = function (mod: any, oldFilename) {
    const _compile = mod._compile

    mod._compile = function (code: string, filename: string) {
      const swc = transformSync(code, swcOptions)
      return _compile.call(this, swc.code, filename)
    }

    return oldHook(mod, oldFilename)
  }
}
```
Sources: [packages/next/src/build/next-config-ts/require-hook.ts:30-42](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/next-config-ts/transpile-config.ts#L30-L42)

| Extension | Hook Action |
| :--- | :--- |
| `.js` | Falls back to `oldJSHook`; if `ERR_REQUIRE_ESM` occurs, reads file content and compiles via SWC. |
| `.ts`, `.cts`, `.mts`, `.cjs`, `.mjs` | Overrides `mod._compile` to transform source text using `transformSync` before native compilation. |

Sources: [packages/next/src/build/next-config-ts/require-hook.ts:6-42](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/next-config-ts/require-hook.ts#L6-L42)

> [!WARNING]
> SWC is loaded lazily via `require('../swc')` inside `registerHook` to prevent loading development React binaries before `NODE_ENV` is initialized.
Sources: [packages/next/src/build/next-config-ts/require-hook.ts:10-12](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/next-config-ts/require-hook.ts#L10-L12)

### Jest Transformation Hook

The Jest transformer integrates SWC into test suites by exposing a `createTransformer` factory that constructs synchronous transform options for each test file.

```typescript
const createTransformer: TransformerCreator<
  SyncTransformer<JestTransformerConfig>,
  JestTransformerConfig
> = (inputOptions) => ({
  process(src, filename, jestOptions) {
    const jestConfig = getJestConfig(jestOptions)

    const swcTransformOpts = getJestSWCOptions({
      isServer:
        jestConfig.testEnvironment === 'node' ||
        jestConfig.testEnvironment.includes('jest-environment-node'),
      filename,
      configDir: inputOptions?.configDir,
      jsConfig: inputOptions?.jsConfig,
      resolvedBaseUrl: inputOptions?.resolvedBaseUrl,
      pagesDir: inputOptions?.pagesDir,
      serverComponents: inputOptions?.serverComponents,
      modularizeImports: inputOptions?.modularizeImports,
      swcPlugins: inputOptions?.swcPlugins,
      compilerOptions: inputOptions?.compilerOptions,
      imageConfig: inputOptions?.imageConfig,
      serverReferenceHashSalt: '',
      esm:
        isSupportEsm &&
        isEsm(Boolean(inputOptions?.isEsmProject), filename, jestConfig),
    })

    return transformSync(src, { ...swcTransformOpts, filename })
  },
})
```
Sources: [packages/next/src/build/swc/jest-transformer.ts:82-111](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/jest-transformer.ts#L82-L111)

The transformer evaluates the test environment and file extensions to determine ECMAScript module support before dispatching to `transformSync`.

```typescript
function isEsm(
  isEsmProject: boolean,
  filename: string,
  jestConfig: Config.ProjectConfig
): boolean {
  return (
    (/\.jsx?$/.test(filename) && isEsmProject) ||
    jestConfig.extensionsToTreatAsEsm?.some((ext: any) =>
      filename.endsWith(ext)
    )
  )
}
```
Sources: [packages/next/src/build/swc/jest-transformer.ts:69-80](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/jest-transformer.ts#L69-L80)

## Diagnostic Telemetry and Failure Recovery

### Load Failure Events and WebAssembly Fallback Tracking

When native Node-API bindings fail to load, `loadWasmRawBindings` tracks each failure attempt across packages like `@next/swc-wasm-nodejs` and `@next/swc-wasm-web`. If a module is missing, it records `ERR_MODULE_NOT_FOUND` events or accumulates fallback error strings before raising the complete attempt list.

```typescript
async function loadWasmRawBindings(importPath = ''): Promise<RawWasmBindings> {
  let attempts = []

  // Used by `run-tests` to force use of a locally-built wasm binary. This environment variable is
  // unstable and subject to change.
  const testWasmDir = process.env.NEXT_TEST_WASM_DIR

  if (testWasmDir) {
    // assume these are node.js bindings and don't need a call to `.default()`
    const rawBindings = await import(
      pathToFileURL(path.join(testWasmDir, 'wasm.js')).toString()
    )
    infoLog(`next-swc build: wasm build ${testWasmDir}`)
    return rawBindings
  } else {
    for (let pkg of ['@next/swc-wasm-nodejs', '@next/swc-wasm-web']) {
      try {
        let pkgPath = pkg

        if (importPath) {
          // the import path must be exact when not in node_modules
          pkgPath = path.join(importPath, pkg, 'wasm.js')
        }
        const importedRawBindings = await import(
          pathToFileURL(pkgPath).toString()
        )
        let rawBindings
        if (pkg === '@next/swc-wasm-web') {
          // https://rustwasm.github.io/docs/wasm-bindgen/examples/without-a-bundler.html
          // `default` must be called to initialize the module
          rawBindings = await importedRawBindings.default!()
        } else {
          rawBindings = importedRawBindings
        }
        infoLog(`next-swc build: wasm build ${pkg}`)
        return rawBindings
      } catch (e: any) {
        // Only log attempts for loading wasm when loading as fallback
        if (importPath) {
          if (e?.code === 'ERR_MODULE_NOT_FOUND') {
            attempts.push(`Attempted to load ${pkg}, but it was not installed`)
          } else {
            attempts.push(
              `Attempted to load ${pkg}, but an error occurred: ${e.message ?? e}`
            )
          }
        }
      }
    }
  }

  throw attempts
}

// helper for tryLoadWasmWithFallback / loadBindings.
async function loadWasm(importPath = '') {
  const rawBindings = await loadWasmRawBindings(importPath)

  function removeUndefined(obj: any): any {
    // serde-wasm-bindgen expect that `undefined` values map to `()` in rust, but we want to treat
    // those fields as non-existent, so remove them before passing them to rust.
    //
    // The native (non-wasm) bindings use `JSON.stringify`, which strips undefined values.
    if (typeof obj !== 'object' || obj === null) {
      return obj
    }
    if (Array.isArray(obj)) {
      return obj.map(removeUndefined)
    }
    const newObj: { [key: string]: any } = {}
    for (const [k, v] of Object.entries(obj)) {
      if (typeof v !== 'undefined') {
        newObj[k] = removeUndefined(v)
      }
    }
    return newObj
  }

  // Note wasm binary does not support async intefaces yet, all async
  // interface coereces to sync interfaces.
```
Sources: [packages/next/src/build/swc/index.ts:1328-1414](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts#L1328-L1414)

> [!WARNING]
> When `NEXT_TEST_WASM_DIR` is set, raw bindings are loaded directly from the local test directory via file URLs without invoking an initialization function, whereas `@next/swc-wasm-web` requires a explicit call to `.default()` to initialize the module.
Sources: [packages/next/src/build/swc/index.ts:1331-1361](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts#L1331-L1361)

### Lockfile Management and Edge Runtime Diagnostics

The native binding layer exposes explicit methods for file locking and runtime analysis, including `lockfileTryAcquire`, `lockfileTryAcquireSync`, `lockfileUnlock`, and `lockfileUnlockSync`. Additionally, `warnForEdgeRuntime` analyzes source code to produce `NapiSourceDiagnostic` warnings for production and development targets.

```typescript
rspack: {
  getModuleNamedExports: function (
    resourcePath: string
  ): Promise<string[]> {
    return bindings.getModuleNamedExports(resourcePath)
  },
  warnForEdgeRuntime: function (
    source: string,
    isProduction: boolean
  ): Promise<NapiSourceDiagnostic[]> {
    return bindings.warnForEdgeRuntime(source, isProduction)
  },
},
expandNextJsTemplate(
  content: Buffer,
  templatePath: string,
  nextPackageDirPath: string,
  replacements: Record<`VAR_${string}`, string>,
  injections: Record<string, string>,
  imports: Record<string, string | null>
): string {
  return bindings.expandNextJsTemplate(
    content,
    templatePath,
    nextPackageDirPath,
    replacements,
    injections,
    imports
  )
},
lockfileTryAcquire(filePath: string, content?: string | null) {
  return bindings.lockfileTryAcquire(filePath, content)
},
lockfileTryAcquireSync(filePath: string, content?: string | null) {
  return bindings.lockfileTryAcquireSync(filePath, content)
},
lockfileUnlock(lockfile: Lockfile) {
  return bindings.lockfileUnlock(lockfile)
},
lockfileUnlockSync(lockfile: Lockfile) {
  return bindings.lockfileUnlockSync(lockfile)
},
```
Sources: [packages/next/src/build/swc/index.ts:1762-1803](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts#L1762-L1803)

| Binding Method | Parameter Types | Purpose |
| :--- | :--- | :--- |
| `lockfileTryAcquire` | `filePath: string, content?: string | null` | Asynchronously attempts to acquire an exclusive lock on a lockfile. |
| `lockfileTryAcquireSync` | `filePath: string, content?: string | null` | Synchronously attempts to acquire an exclusive lock on a lockfile. |
| `lockfileUnlock` | `lockfile: Lockfile` | Asynchronously releases an acquired lockfile reference. |
| `lockfileUnlockSync` | `lockfile: Lockfile` | Synchronously releases an acquired lockfile reference. |
| `warnForEdgeRuntime` | `source: string, isProduction: boolean` | Scans source code for unsupported features and returns an array of `NapiSourceDiagnostic` objects. |

Sources: [packages/next/src/build/swc/index.ts:1768-1803](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts#L1768-L1803), [packages/next/src/build/swc/types.ts:103-109](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/types.ts#L103-L109)

## Related

- [Build Pipeline](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/build-system/build-pipeline)
- [Webpack Loaders](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/build-system/webpack-loaders)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/llms.txt) for all pages in this wiki.
