mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
9a7733ed83
## What? Adds a `maybe-build-native.mjs` script to `packages/next-swc/` that conditionally rebuilds native SWC bindings during `pnpm build`. The script is integrated into the turborepo pipeline as the `build` task for `@next/swc`. ## Why? Developers often forget to run `pnpm swc-build-native` after pulling Rust changes, leading to confusing runtime errors. Conversely, rebuilding native binaries when nothing changed wastes significant time. This automates the decision so `pnpm build` always does the right thing. ## How? The `maybe-build-native.mjs` script runs as `@next/swc`'s `build` task and: 1. **Skips in CI** — exits immediately when the `CI` env var is set, since CI uses prebuilt `@next/swc-*` npm packages 2. **Finds the last version bump** — uses `git log -G` to locate the commit that last changed `"version":` in `packages/next/package.json` 3. **Checks for Rust changes** — compares `*.rs` files against the working tree (committed + staged + unstaged) since that version bump commit 4. **Rebuilds if needed** — runs `build-native` and copies/formats the generated TypeScript definitions 5. **Clears stale binaries** — if no Rust changes are detected, removes any leftover `.node` files from `native/` so the prebuilt npm packages are used instead Turborepo's `turbo.json` is configured with Rust-specific inputs (`crates/`, `Cargo.*`, `rust-toolchain`) and `CI` in `env` so CI and local builds get separate cache keys. ### Files changed - `packages/next-swc/maybe-build-native.mjs` — new script with the conditional build logic - `packages/next-swc/package.json` — added `"build": "node maybe-build-native.mjs"` - `packages/next-swc/turbo.json` — added `build` task with Rust inputs, `CI` env, and `native/*.node` outputs
84 lines
2.4 KiB
JavaScript
84 lines
2.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { promises as fs } from 'node:fs'
|
|
import path from 'node:path'
|
|
import url from 'node:url'
|
|
import execa from 'execa'
|
|
import { NEXT_DIR, logCommand } from './pack-util'
|
|
|
|
const nextSwcDir = path.join(NEXT_DIR, 'packages/next-swc')
|
|
|
|
export default async function buildNative(
|
|
buildNativeArgs: string[]
|
|
): Promise<void> {
|
|
const buildCommand = ['pnpm', 'run', 'build-native', ...buildNativeArgs]
|
|
logCommand('Build native bindings', buildCommand)
|
|
await execa(buildCommand[0], buildCommand.slice(1), {
|
|
cwd: nextSwcDir,
|
|
// Without a shell, `pnpm run build-native` returns a 0 exit code on SIGINT?
|
|
shell: true,
|
|
env: {
|
|
NODE_ENV: process.env.NODE_ENV,
|
|
CARGO_TERM_COLOR: 'always',
|
|
TTY: '1',
|
|
},
|
|
stdio: 'inherit',
|
|
})
|
|
|
|
await writeTypes()
|
|
}
|
|
|
|
// Check if this file is being run directly
|
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).toString()) {
|
|
buildNative(process.argv.slice(2)).catch((err) => {
|
|
console.error(err)
|
|
process.exit(1)
|
|
})
|
|
}
|
|
|
|
async function writeTypes() {
|
|
const generatedTypesPath = path.join(
|
|
NEXT_DIR,
|
|
'packages/next-swc/native/index.d.ts'
|
|
)
|
|
const vendoredTypesPath = path.join(
|
|
NEXT_DIR,
|
|
'packages/next/src/build/swc/generated-native.d.ts'
|
|
)
|
|
const generatedTypesMarker = '// GENERATED-TYPES-BELOW\n'
|
|
const generatedNotice =
|
|
'// DO NOT MANUALLY EDIT THESE TYPES\n' +
|
|
'// You can regenerate this file by running `pnpm swc-build-native` in the root of the repo.\n\n'
|
|
|
|
const generatedTypes = await fs.readFile(generatedTypesPath, 'utf8')
|
|
let vendoredTypes = await fs.readFile(vendoredTypesPath, 'utf8')
|
|
|
|
const existingContent = vendoredTypes
|
|
vendoredTypes = vendoredTypes.split(generatedTypesMarker)[0]
|
|
vendoredTypes =
|
|
vendoredTypes + generatedTypesMarker + generatedNotice + generatedTypes
|
|
|
|
const prettifyCommand = ['prettier', '--stdin-filepath', vendoredTypesPath]
|
|
logCommand('Prettify generated types', prettifyCommand)
|
|
const prettierResult = await execa(
|
|
prettifyCommand[0],
|
|
prettifyCommand.slice(1),
|
|
{
|
|
cwd: NEXT_DIR,
|
|
input: vendoredTypes,
|
|
preferLocal: true,
|
|
}
|
|
)
|
|
vendoredTypes = prettierResult.stdout
|
|
if (!vendoredTypes.endsWith('\n')) {
|
|
vendoredTypes += '\n'
|
|
}
|
|
|
|
if (vendoredTypes === existingContent) {
|
|
return
|
|
}
|
|
|
|
logCommand('Write generated types', `write file`)
|
|
await fs.writeFile(vendoredTypesPath, vendoredTypes)
|
|
}
|