summaryrefslogtreecommitdiffhomepage
path: root/nix/scripts/canonicalize-node-modules.ts
diff options
context:
space:
mode:
authorAlbert O'Shea <[email protected]>2025-11-18 17:46:49 +1100
committerGitHub <[email protected]>2025-11-18 00:46:49 -0600
commit5e13527416e183c7ea6d1baa3528b5c30108372e (patch)
tree6d839be0118b84974eacc670a93823974787faba /nix/scripts/canonicalize-node-modules.ts
parentaba94c658f5c0987443196a5e850fdf7293d5006 (diff)
downloadopencode-5e13527416e183c7ea6d1baa3528b5c30108372e.tar.gz
opencode-5e13527416e183c7ea6d1baa3528b5c30108372e.zip
feat: nix support for the nix folks (#3924)
Co-authored-by: opencode <[email protected]> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Diffstat (limited to 'nix/scripts/canonicalize-node-modules.ts')
-rw-r--r--nix/scripts/canonicalize-node-modules.ts94
1 files changed, 94 insertions, 0 deletions
diff --git a/nix/scripts/canonicalize-node-modules.ts b/nix/scripts/canonicalize-node-modules.ts
new file mode 100644
index 000000000..791d96dfe
--- /dev/null
+++ b/nix/scripts/canonicalize-node-modules.ts
@@ -0,0 +1,94 @@
+import { lstat, mkdir, readdir, rm, symlink } from "fs/promises"
+import { join, relative } from "path"
+
+type SemverLike = {
+ valid: (value: string) => string | null
+ rcompare: (left: string, right: string) => number
+}
+
+type Entry = {
+ dir: string
+ version: string
+ label: string
+}
+
+const root = process.cwd()
+const bunRoot = join(root, "node_modules/.bun")
+const linkRoot = join(bunRoot, "node_modules")
+const directories = (await readdir(bunRoot)).sort()
+const versions = new Map<string, Entry[]>()
+
+for (const entry of directories) {
+ const full = join(bunRoot, entry)
+ const info = await lstat(full)
+ if (!info.isDirectory()) {
+ continue
+ }
+ const marker = entry.lastIndexOf("@")
+ if (marker <= 0) {
+ continue
+ }
+ const slug = entry.slice(0, marker).replace(/\+/g, "/")
+ const version = entry.slice(marker + 1)
+ const list = versions.get(slug) ?? []
+ list.push({ dir: full, version, label: entry })
+ versions.set(slug, 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>()
+
+for (const [slug, list] of versions) {
+ list.sort((a, b) => {
+ const left = semver.valid(a.version)
+ const right = semver.valid(b.version)
+ if (left && right) {
+ const delta = semver.rcompare(left, right)
+ if (delta !== 0) {
+ return delta
+ }
+ }
+ if (left && !right) {
+ return -1
+ }
+ if (!left && right) {
+ return 1
+ }
+ return b.version.localeCompare(a.version)
+ })
+ selections.set(slug, list[0])
+}
+
+await rm(linkRoot, { recursive: true, force: true })
+await mkdir(linkRoot, { recursive: true })
+
+const rewrites: string[] = []
+
+for (const [slug, entry] of Array.from(selections.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
+ const parts = slug.split("/")
+ const leaf = parts.pop()
+ if (!leaf) {
+ continue
+ }
+ const parent = join(linkRoot, ...parts)
+ await mkdir(parent, { recursive: true })
+ const linkPath = join(parent, leaf)
+ const desired = join(entry.dir, "node_modules", slug)
+ const relativeTarget = relative(parent, desired)
+ const resolved = relativeTarget.length === 0 ? "." : relativeTarget
+ await rm(linkPath, { recursive: true, force: true })
+ await symlink(resolved, linkPath)
+ rewrites.push(slug + " -> " + resolved)
+}
+
+rewrites.sort()
+console.log("[canonicalize-node-modules] rebuilt", rewrites.length, "links")
+for (const line of rewrites.slice(0, 20)) {
+ console.log(" ", line)
+}
+if (rewrites.length > 20) {
+ console.log(" ...")
+} \ No newline at end of file