nixos: vendor local package definitions
This commit is contained in:
105
nixos/packages/t3code/canonicalize-node-modules.ts
Normal file
105
nixos/packages/t3code/canonicalize-node-modules.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { lstat, mkdir, readdir, rm, symlink } from "fs/promises";
|
||||
import { join, relative } from "path";
|
||||
|
||||
type Entry = {
|
||||
dir: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
async function isDirectory(path: string) {
|
||||
try {
|
||||
const info = await lstat(path);
|
||||
return info.isDirectory();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const isValidSemver = (version: string) => Bun.semver.satisfies(version, "x.x.x");
|
||||
|
||||
const bunRoot = join(process.cwd(), "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);
|
||||
if (!(await isDirectory(full))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const parsed = parseEntry(entry);
|
||||
if (!parsed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const list = versions.get(parsed.name) ?? [];
|
||||
list.push({ dir: full, version: parsed.version });
|
||||
versions.set(parsed.name, list);
|
||||
}
|
||||
|
||||
const selections = new Map<string, Entry>();
|
||||
|
||||
for (const [slug, list] of versions) {
|
||||
list.sort((a, b) => {
|
||||
const aValid = isValidSemver(a.version);
|
||||
const bValid = isValidSemver(b.version);
|
||||
|
||||
if (aValid && bValid) {
|
||||
return -Bun.semver.order(a.version, b.version);
|
||||
}
|
||||
if (aValid) {
|
||||
return -1;
|
||||
}
|
||||
if (bValid) {
|
||||
return 1;
|
||||
}
|
||||
return b.version.localeCompare(a.version);
|
||||
});
|
||||
|
||||
const first = list[0];
|
||||
if (first) {
|
||||
selections.set(slug, first);
|
||||
}
|
||||
}
|
||||
|
||||
await rm(linkRoot, { recursive: true, force: true });
|
||||
await mkdir(linkRoot, { recursive: true });
|
||||
|
||||
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);
|
||||
if (!(await isDirectory(desired))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const relativeTarget = relative(parent, desired);
|
||||
await rm(linkPath, { recursive: true, force: true });
|
||||
await symlink(relativeTarget.length == 0 ? "." : relativeTarget, linkPath);
|
||||
}
|
||||
|
||||
function parseEntry(label: string) {
|
||||
const marker = label.startsWith("@") ? label.indexOf("@", 1) : label.indexOf("@");
|
||||
if (marker <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const name = label.slice(0, marker).replace(/\+/g, "/");
|
||||
const version = label.slice(marker + 1);
|
||||
|
||||
if (!name || !version) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { name, version };
|
||||
}
|
||||
Reference in New Issue
Block a user