mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
d3169de7e5
Instead of hardcoding the list, we now automatically detect any `.browser` sibling and create a compiler alias for that module. Together with a linting rule, we'll ensure no future regressions are handled automatically (detection of conditional require and missing .browser sibling). This isn't the smoothest DX yet. Ideally we'd generate the list at Next.js compile time (i.e. before publishing) to avoid having to rerun a script (I'll follow-up). Generating the list during build time (next dev or next build) is just another tiny cost users would have to pay.
98 lines
3.5 KiB
JavaScript
98 lines
3.5 KiB
JavaScript
// @ts-check
|
|
/**
|
|
* Generates the list of Next.js modules that have a browser-specific variant,
|
|
* i.e. a `<module>.ts` (the default, used on the server and any unbundled
|
|
* runtime) paired with a sibling `<module>.browser.ts`. The browser bundle is
|
|
* aliased to the `.browser` variant by both bundlers:
|
|
*
|
|
* - webpack: packages/next/src/build/create-compiler-aliases.ts
|
|
* - Turbopack: crates/next-core/src/next_import_map.rs
|
|
*
|
|
* Both consumers read the generated lists produced here so the set stays in
|
|
* sync with the filesystem. CI runs this script followed by `git diff
|
|
* --exit-code` to ensure the committed lists are up to date.
|
|
*/
|
|
import { execFileSync } from 'child_process'
|
|
import { writeFileSync } from 'fs'
|
|
import { join, dirname } from 'path'
|
|
import { fileURLToPath } from 'url'
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
const repoRoot = join(__dirname, '..')
|
|
const srcRel = 'packages/next/src'
|
|
const tsOut = join(repoRoot, srcRel, 'build/browser-variant-modules.ts')
|
|
const rsOut = join(repoRoot, 'crates/next-core/src/browser_variant_modules.rs')
|
|
|
|
/** Git-tracked files (posix paths relative to the repo root) under a pathspec. */
|
|
function trackedFiles(pathspec) {
|
|
return execFileSync('git', ['ls-files', '--', pathspec], {
|
|
cwd: repoRoot,
|
|
encoding: 'utf8',
|
|
})
|
|
.split('\n')
|
|
.filter(Boolean)
|
|
}
|
|
|
|
/**
|
|
* Find `<module>.browser.{ts,tsx}` files that have a base `<module>.{ts,tsx}`
|
|
* sibling, returning posix module paths relative to `packages/next/src` without
|
|
* an extension (e.g. `client/components/unstable-rethrow`). Enumerated from
|
|
* git so generated output, ignored files, and build artifacts never leak in.
|
|
*/
|
|
function collectModules() {
|
|
const tracked = trackedFiles(srcRel)
|
|
const trackedSet = new Set(tracked)
|
|
/** @type {string[]} */
|
|
const modules = []
|
|
for (const file of tracked) {
|
|
const match = file.match(/^(.*)\.browser\.tsx?$/)
|
|
if (!match) continue
|
|
const stem = match[1]
|
|
if (!trackedSet.has(`${stem}.ts`) && !trackedSet.has(`${stem}.tsx`))
|
|
continue
|
|
modules.push(stem.slice(srcRel.length + 1))
|
|
}
|
|
return modules.sort()
|
|
}
|
|
|
|
function renderTs(modules) {
|
|
const entries = modules.map((m) => ` '${m}',`).join('\n')
|
|
return `// This file is @generated by \`pnpm generate-browser-variant-aliases\`.
|
|
// Do not edit by hand.
|
|
//
|
|
// Modules with a \`.browser\` sibling. The default module is aliased to its
|
|
// \`.browser\` variant for the browser bundle in
|
|
// \`packages/next/src/build/create-compiler-aliases.ts\`. Paths are relative to
|
|
// \`packages/next/dist\` (extension omitted).
|
|
export const browserVariantModules = [
|
|
${entries}
|
|
] as const
|
|
`
|
|
}
|
|
|
|
function renderRust(modules) {
|
|
const entries = modules.map((m) => ` "${m}",`).join('\n')
|
|
return `//! This file is @generated by \`pnpm generate-browser-variant-aliases\`.
|
|
//! Do not edit by hand.
|
|
//!
|
|
//! Modules with a \`.browser\` sibling. The default module is aliased to its
|
|
//! \`.browser\` variant for the browser bundle in
|
|
//! \`get_next_client_resolved_map\` (see \`next_import_map.rs\`). Paths are
|
|
//! relative to \`next/dist\` (extension omitted).
|
|
|
|
// rustfmt::skip keeps this list one-per-line (and matching the generator
|
|
// output) instead of collapsing short lists onto a single line.
|
|
#[rustfmt::skip]
|
|
pub static BROWSER_VARIANT_MODULES: &[&str] = &[
|
|
${entries}
|
|
];
|
|
`
|
|
}
|
|
|
|
const modules = collectModules()
|
|
writeFileSync(tsOut, renderTs(modules))
|
|
writeFileSync(rsOut, renderRust(modules))
|
|
console.log(
|
|
`Generated ${modules.length} browser-variant module(s):\n ${modules.join('\n ')}`
|
|
)
|