refactor(nix): use native Bun APIs and propagate errors (#12694)

This commit is contained in:
Jérôme Benoit
2026-02-09 02:43:10 +01:00
committed by GitHub
parent a598ecac1f
commit 79879b43ce
2 changed files with 26 additions and 46 deletions

View File

@@ -1,27 +1,32 @@
import { lstat, mkdir, readdir, rm, symlink } from "fs/promises" import { lstat, mkdir, readdir, rm, symlink } from "fs/promises"
import { join, relative } from "path" import { join, relative } from "path"
type SemverLike = {
valid: (value: string) => string | null
rcompare: (left: string, right: string) => number
}
type Entry = { type Entry = {
dir: string dir: string
version: string version: string
label: string
} }
async function isDirectory(path: string) {
try {
const info = await lstat(path)
return info.isDirectory()
} catch {
return false
}
}
const isValidSemver = (v: string) => Bun.semver.satisfies(v, "x.x.x")
const root = process.cwd() const root = process.cwd()
const bunRoot = join(root, "node_modules/.bun") const bunRoot = join(root, "node_modules/.bun")
const linkRoot = join(bunRoot, "node_modules") const linkRoot = join(bunRoot, "node_modules")
const directories = (await readdir(bunRoot)).sort() const directories = (await readdir(bunRoot)).sort()
const versions = new Map<string, Entry[]>() const versions = new Map<string, Entry[]>()
for (const entry of directories) { for (const entry of directories) {
const full = join(bunRoot, entry) const full = join(bunRoot, entry)
const info = await lstat(full) if (!(await isDirectory(full))) {
if (!info.isDirectory()) {
continue continue
} }
const parsed = parseEntry(entry) const parsed = parseEntry(entry)
@@ -29,37 +34,23 @@ for (const entry of directories) {
continue continue
} }
const list = versions.get(parsed.name) ?? [] const list = versions.get(parsed.name) ?? []
list.push({ dir: full, version: parsed.version, label: entry }) list.push({ dir: full, version: parsed.version })
versions.set(parsed.name, list) versions.set(parsed.name, list)
} }
const semverModule = (await import(join(bunRoot, "node_modules/semver"))) as
| SemverLike
| {
default: SemverLike
}
const semver = "default" in semverModule ? semverModule.default : semverModule
const selections = new Map<string, Entry>() const selections = new Map<string, Entry>()
for (const [slug, list] of versions) { for (const [slug, list] of versions) {
list.sort((a, b) => { list.sort((a, b) => {
const left = semver.valid(a.version) const aValid = isValidSemver(a.version)
const right = semver.valid(b.version) const bValid = isValidSemver(b.version)
if (left && right) { if (aValid && bValid) return -Bun.semver.order(a.version, b.version)
const delta = semver.rcompare(left, right) if (aValid) return -1
if (delta !== 0) { if (bValid) return 1
return delta
}
}
if (left && !right) {
return -1
}
if (!left && right) {
return 1
}
return b.version.localeCompare(a.version) return b.version.localeCompare(a.version)
}) })
selections.set(slug, list[0]) const first = list[0]
if (first) selections.set(slug, first)
} }
await rm(linkRoot, { recursive: true, force: true }) await rm(linkRoot, { recursive: true, force: true })
@@ -77,10 +68,7 @@ for (const [slug, entry] of Array.from(selections.entries()).sort((a, b) => a[0]
await mkdir(parent, { recursive: true }) await mkdir(parent, { recursive: true })
const linkPath = join(parent, leaf) const linkPath = join(parent, leaf)
const desired = join(entry.dir, "node_modules", slug) const desired = join(entry.dir, "node_modules", slug)
const exists = await lstat(desired) if (!(await isDirectory(desired))) {
.then((info) => info.isDirectory())
.catch(() => false)
if (!exists) {
continue continue
} }
const relativeTarget = relative(parent, desired) const relativeTarget = relative(parent, desired)

View File

@@ -8,7 +8,7 @@ type PackageManifest = {
const root = process.cwd() const root = process.cwd()
const bunRoot = join(root, "node_modules/.bun") const bunRoot = join(root, "node_modules/.bun")
const bunEntries = (await safeReadDir(bunRoot)).sort() const bunEntries = (await readdir(bunRoot)).sort()
let rewritten = 0 let rewritten = 0
for (const entry of bunEntries) { for (const entry of bunEntries) {
@@ -45,11 +45,11 @@ for (const entry of bunEntries) {
} }
} }
console.log(`[normalize-bun-binaries] rewrote ${rewritten} links`) console.log(`[normalize-bun-binaries] rebuilt ${rewritten} links`)
async function collectPackages(modulesRoot: string) { async function collectPackages(modulesRoot: string) {
const found: string[] = [] const found: string[] = []
const topLevel = (await safeReadDir(modulesRoot)).sort() const topLevel = (await readdir(modulesRoot)).sort()
for (const name of topLevel) { for (const name of topLevel) {
if (name === ".bin" || name === ".bun") { if (name === ".bin" || name === ".bun") {
continue continue
@@ -59,7 +59,7 @@ async function collectPackages(modulesRoot: string) {
continue continue
} }
if (name.startsWith("@")) { if (name.startsWith("@")) {
const scoped = (await safeReadDir(full)).sort() const scoped = (await readdir(full)).sort()
for (const child of scoped) { for (const child of scoped) {
const scopedDir = join(full, child) const scopedDir = join(full, child)
if (await isDirectory(scopedDir)) { if (await isDirectory(scopedDir)) {
@@ -121,14 +121,6 @@ async function isDirectory(path: string) {
} }
} }
async function safeReadDir(path: string) {
try {
return await readdir(path)
} catch {
return []
}
}
function normalizeBinName(name: string) { function normalizeBinName(name: string) {
const slash = name.lastIndexOf("/") const slash = name.lastIndexOf("/")
if (slash >= 0) { if (slash >= 0) {