Make Camino examples and scaffolds complete Yarn and Nix packages
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user