refactor(nix): use native Bun APIs and propagate errors (#12694)
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user