Make Camino examples and scaffolds complete Yarn and Nix packages

This commit is contained in:
Timothy J. Aveni
2026-09-22 15:31:42 -07:00
parent 82a4542746
commit 152f58a3c6
9 changed files with 193 additions and 32 deletions
+52
View File
@@ -0,0 +1,52 @@
import { execFile as callback } from "node:child_process";
import { promisify } from "node:util";
const execFile = promisify(callback);
export const nixLiteral = (value: string): string => JSON.stringify(value).replace(/\$\{/g, "\\${");
/** Recognize the deliberately small scaffold edit surface, never evaluate Nix.
* Authors may replace it with arbitrary Nix; then scaffolding declines the edit. */
export async function componentEntries(source: string): Promise<Record<string, { entry: string; export: string }>> {
const { stdout } = await execFile("nix-instantiate", ["--parse", "--expr", source], { maxBuffer: 1024 * 1024 });
const tokens = stdout.match(/"(?:\\.|[^"\\])*"|[A-Za-z_][A-Za-z0-9_-]*|[{}=;]/g) ?? [];
if (stdout.replace(/"(?:\\.|[^"\\])*"|[A-Za-z_][A-Za-z0-9_-]*|[{}=;]|\s+/g, ""))
throw Error("components.nix is outside the supported literal-entry shape");
let cursor = 0;
const expect = (token: string) => {
if (tokens[cursor++] !== token) throw Error("components.nix is outside the supported literal-entry shape");
};
const string = () => {
const token = tokens[cursor++];
if (!token?.startsWith('"')) throw Error("Component entries must use literal strings");
const json = token.replace(/(\\*)\$\{/g, (_match, escapes: string) => {
if (escapes.length % 2 === 0) throw Error("Component entries must use literal strings");
return escapes.slice(1) + "${";
});
return JSON.parse(json) as string;
};
const entries: Record<string, { entry: string; export: string }> = Object.create(null);
expect("{");
while (tokens[cursor] !== "}") {
const id = tokens[cursor]?.startsWith('"') ? string() : tokens[cursor++];
if (!id) throw Error("Missing component identity");
if (Object.hasOwn(entries, id)) throw Error("Duplicate component entry");
expect("=");
expect("{");
const properties: Record<string, string> = Object.create(null);
while (tokens[cursor] !== "}") {
const key = tokens[cursor]?.startsWith('"') ? string() : tokens[cursor++];
if (!key || !["entry", "export"].includes(key) || Object.hasOwn(properties, key))
throw Error("Unsupported component entry property");
expect("=");
properties[key] = string();
expect(";");
}
expect("}");
expect(";");
if (!properties.entry || !properties.export) throw Error("Component entry needs entry and export");
entries[id] = { entry: properties.entry, export: properties.export };
}
expect("}");
if (cursor !== tokens.length || !/\n}\s*$/.test(source))
throw Error("components.nix is outside the supported scaffold shape");
return entries;
}