mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
## What is `pack-next`? This is a script we use for developing turbopack. It builds tarballs for all the next.js packages, including `next-swc`, and then provides instructions about how to modify a project's `package.json` to install from that tarball. ## What does this PR do? If you provide a `--project ~/path/to/directory` argument to `pnpm pack-next`, we'll auto-update the `package.json` there for you, instead of telling you how to do it manually! This also understands the React 19 RC peer dependencies, which is something that was missing from the generated instructions. ## Supported Package Managers I've tested this with `npm` and `pnpm`. - `bun` fails with `ENOTDIR` for unknown reasons (maybe it's trying to handle the local tarball as a directory instead of a tarball?). - `yarn` fails due to an upstream issue: https://github.com/yarnpkg/yarn/issues/6339 This does generate the entries for yarn's `resolutions` feature, assuming the upstream issue is eventually fixed. ## Why Rust and `cargo xtask`? https://github.com/matklad/cargo-xtask I'd love for these Turbopack-centric scripts to be written in Rust with xtask, since they're mostly touched by the turbopack team. However, it's also hard to justify putting too much effort into these development scripts, so I don't want to rewrite the whole thing right now. I figured I'd write the new functionality in Rust and call it from the old JS script. ## Test Plan Perform a fresh checkout of shadcn/ui, and then: ``` pnpm pack-next --project ~/shadcn-ui/apps/www/ ``` ``` Found workspace! Patching "/home/bgw.linux/shadcn-ui/package.json" with overrides. Successfully patched "/home/bgw.linux/shadcn-ui/package.json" to use local tarball files! Run `pnpm i` or `npm i` to install the overrides ``` Check the resulting changes to `~/shadcn-ui`: ```diff diff --git a/package.json b/package.json index be47ad7..262c13b 100644 --- a/package.json +++ b/package.json @@ -63,12 +63,31 @@ "tsx": "^4.1.4", "turbo": "^1.9.9", "vite-tsconfig-paths": "^4.2.0", - "vitest": "^0.31.1" + "vitest": "^0.31.1", + "@next/swc": "file:/home/bgw.linux/next.js/tarballs/next-swc.tar" }, "devDependencies": { "@types/node": "^20.11.27", "@types/react": "^18.2.65", "@types/react-dom": "^18.2.22", "typescript": "^5.5.3" + }, + "overrides": { + "next": "file:/home/bgw.linux/next.js/tarballs/next.tar", + "@next/mdx": "file:/home/bgw.linux/next.js/tarballs/next-mdx.tar", + "@next/env": "file:/home/bgw.linux/next.js/tarballs/next-env.tar", + "@next/bundle-analyzer": "file:/home/bgw.linux/next.js/tarballs/next-bundle-analyzer.tar", + "@next/swc": "file:/home/bgw.linux/next.js/tarballs/next-swc.tar", + "react": "19.0.0-rc-7771d3a7-20240827", + "react-dom": "19.0.0-rc-7771d3a7-20240827" + }, + "resolutions": { + "next": "file:/home/bgw.linux/next.js/tarballs/next.tar", + "@next/mdx": "file:/home/bgw.linux/next.js/tarballs/next-mdx.tar", + "@next/env": "file:/home/bgw.linux/next.js/tarballs/next-env.tar", + "@next/bundle-analyzer": "file:/home/bgw.linux/next.js/tarballs/next-bundle-analyzer.tar", + "@next/swc": "file:/home/bgw.linux/next.js/tarballs/next-swc.tar", + "react": "19.0.0-rc-7771d3a7-20240827", + "react-dom": "19.0.0-rc-7771d3a7-20240827" } } ```
183 lines
5.6 KiB
JavaScript
Executable File
183 lines
5.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
const {
|
|
NEXT_DIR,
|
|
booleanArg,
|
|
exec,
|
|
execAsyncWithOutput,
|
|
glob,
|
|
namedValueArg,
|
|
packageFiles,
|
|
} = require('./pack-util.cjs')
|
|
const fs = require('fs')
|
|
const fsPromises = require('fs/promises')
|
|
|
|
const args = process.argv.slice(2)
|
|
|
|
const TARBALLS = `${NEXT_DIR}/tarballs`
|
|
const NEXT_PACKAGES = `${NEXT_DIR}/packages`
|
|
const noBuild = booleanArg(args, '--no-build')
|
|
const projectPath = namedValueArg(args, '--project')
|
|
|
|
;(async () => {
|
|
// the debuginfo on macos is much smaller, so we don't typically need to strip
|
|
const DEFAULT_PACK_NEXT_COMPRESS =
|
|
process.platform === 'darwin' ? 'none' : 'strip'
|
|
const PACK_NEXT_COMPRESS =
|
|
process.env.PACK_NEXT_COMPRESS || DEFAULT_PACK_NEXT_COMPRESS
|
|
|
|
fs.mkdirSync(TARBALLS, { recursive: true })
|
|
|
|
if (!noBuild) {
|
|
exec('Install Next.js build dependencies', 'pnpm i')
|
|
exec('Build Next.js', 'pnpm run build')
|
|
}
|
|
|
|
if (PACK_NEXT_COMPRESS !== 'strip') {
|
|
// HACK: delete any pre-existing binaries to force napi-rs to rewrite it
|
|
let binaries = await nextSwcBinaries()
|
|
await Promise.all(binaries.map((bin) => fsPromises.rm(bin)))
|
|
}
|
|
|
|
await require('./build-native.cjs')
|
|
|
|
const NEXT_TARBALL = `${TARBALLS}/next.tar`
|
|
const NEXT_SWC_TARBALL = `${TARBALLS}/next-swc.tar`
|
|
const NEXT_MDX_TARBALL = `${TARBALLS}/next-mdx.tar`
|
|
const NEXT_ENV_TARBALL = `${TARBALLS}/next-env.tar`
|
|
const NEXT_BA_TARBALL = `${TARBALLS}/next-bundle-analyzer.tar`
|
|
|
|
async function nextSwcBinaries() {
|
|
return await glob('next-swc/native/*.node', {
|
|
cwd: NEXT_PACKAGES,
|
|
absolute: true,
|
|
})
|
|
}
|
|
|
|
// We use neither:
|
|
// * npm pack, as it doesn't include native modules in the tarball
|
|
// * pnpm pack, as it tries to include target directories and compress them,
|
|
// which takes forever.
|
|
// Instead, we generate non-compressed tarballs.
|
|
async function packWithTar(packagePath, tarballPath, extraArgs = []) {
|
|
const paths = await packageFiles(packagePath)
|
|
|
|
const command = [
|
|
'tar',
|
|
'-c',
|
|
// https://apple.stackexchange.com/a/444073
|
|
...(process.platform === 'darwin' ? ['--no-mac-metadata'] : []),
|
|
'-f',
|
|
tarballPath,
|
|
...extraArgs,
|
|
'--',
|
|
...paths.map((p) => `./${p}`),
|
|
]
|
|
|
|
await execAsyncWithOutput(`Pack ${packagePath}`, command, {
|
|
cwd: packagePath,
|
|
})
|
|
}
|
|
|
|
// Special-case logic for packing next-swc.
|
|
//
|
|
// pnpm emits `ERR_FS_FILE_TOO_LARGE` if the tarfile is >2GiB due to limits
|
|
// in libuv (https://github.com/libuv/libuv/pull/1501). This is common with
|
|
// next-swc due to the large amount of debugging symbols. We can fix this one
|
|
// of two ways: strip or compression.
|
|
//
|
|
// We default to stripping (usually faster), but on Linux, we can compress
|
|
// instead with objcopy, keeping debug symbols intact. This is controlled by
|
|
// `PACK_NEXT_COMPRESS`.
|
|
async function packNextSwc() {
|
|
const packagePath = `${NEXT_PACKAGES}/next-swc`
|
|
switch (PACK_NEXT_COMPRESS) {
|
|
case 'strip':
|
|
await execAsyncWithOutput('Stripping next-swc native binary', [
|
|
'strip',
|
|
...(process.platform === 'darwin' ? ['-x', '-'] : ['--']),
|
|
await nextSwcBinaries(),
|
|
])
|
|
await packWithTar(packagePath, NEXT_SWC_TARBALL)
|
|
break
|
|
case 'objcopy-zstd':
|
|
if (process.platform !== 'linux') {
|
|
throw new Error('objcopy-zstd is only supported on Linux')
|
|
}
|
|
await Promise.all(
|
|
(await nextSwcBinaries()).map((bin) =>
|
|
execAsyncWithOutput(
|
|
'Compressing debug symbols in next-swc native binary',
|
|
['objcopy', '--compress-debug-sections=zstd', '--', bin]
|
|
)
|
|
)
|
|
)
|
|
await packWithTar(packagePath, NEXT_SWC_TARBALL)
|
|
break
|
|
case 'none':
|
|
await packWithTar(packagePath, NEXT_SWC_TARBALL)
|
|
break
|
|
default:
|
|
throw new Error(
|
|
"PACK_NEXT_COMPRESS must be one of 'strip', 'objcopy-zstd', or 'none'"
|
|
)
|
|
}
|
|
}
|
|
|
|
// build all tarfiles in parallel
|
|
await Promise.all([
|
|
packNextSwc(),
|
|
...[
|
|
[`${NEXT_PACKAGES}/next`, NEXT_TARBALL],
|
|
[`${NEXT_PACKAGES}/next-mdx`, NEXT_MDX_TARBALL],
|
|
[`${NEXT_PACKAGES}/next-env`, NEXT_ENV_TARBALL],
|
|
[`${NEXT_PACKAGES}/next-bundle-analyzer`, NEXT_BA_TARBALL],
|
|
].map(([packagePath, tarballPath]) =>
|
|
packWithTar(packagePath, tarballPath)
|
|
),
|
|
])
|
|
|
|
if (projectPath != null) {
|
|
await execAsyncWithOutput(`Update package.json for ${projectPath}`, [
|
|
'cargo',
|
|
'xtask',
|
|
'patch-package-json',
|
|
projectPath,
|
|
`--next-tarball=${NEXT_TARBALL}`,
|
|
`--next-mdx-tarball=${NEXT_MDX_TARBALL}`,
|
|
`--next-env-tarball=${NEXT_ENV_TARBALL}`,
|
|
`--next-bundle-analyzer-tarball=${NEXT_BA_TARBALL}`,
|
|
`--next-swc-tarball=${NEXT_SWC_TARBALL}`,
|
|
])
|
|
} else {
|
|
console.log('Add the following overrides to your workspace package.json:')
|
|
console.log(` "pnpm": {`)
|
|
console.log(` "overrides": {`)
|
|
console.log(` "next": ${JSON.stringify(`file:${NEXT_TARBALL}`)},`)
|
|
console.log(
|
|
` "@next/mdx": ${JSON.stringify(`file:${NEXT_MDX_TARBALL}`)},`
|
|
)
|
|
console.log(
|
|
` "@next/env": ${JSON.stringify(`file:${NEXT_ENV_TARBALL}`)},`
|
|
)
|
|
console.log(
|
|
` "@next/bundle-analyzer": ${JSON.stringify(
|
|
`file:${NEXT_BA_TARBALL}`
|
|
)}`
|
|
)
|
|
console.log(` }`)
|
|
console.log(` }`)
|
|
console.log()
|
|
console.log(
|
|
'Add the following dependencies to your workspace package.json:'
|
|
)
|
|
console.log(` "dependencies": {`)
|
|
console.log(
|
|
` "@next/swc": ${JSON.stringify(`file:${NEXT_SWC_TARBALL}`)},`
|
|
)
|
|
console.log(` ...`)
|
|
console.log(` }`)
|
|
console.log()
|
|
}
|
|
})()
|