Make Camino examples and scaffolds complete Yarn and Nix packages
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { componentEntries } from "../src/capability-language/component-nix.js";
|
||||
test("component edit surface parses literal Nix without evaluating arbitrary expressions", async () => {
|
||||
assert.deepEqual(
|
||||
{
|
||||
...(await componentEntries(
|
||||
`{\n # retained author comment\n "component:card" = { export = "Card"; entry = "src/card.tsx"; };\n}\n`,
|
||||
)),
|
||||
},
|
||||
{ "component:card": { entry: "src/card.tsx", export: "Card" } },
|
||||
);
|
||||
await assert.rejects(
|
||||
componentEntries('let x = builtins.abort "must not evaluate"; in x'),
|
||||
/supported literal-entry shape|literal strings|supported scaffold shape/,
|
||||
);
|
||||
await assert.rejects(
|
||||
componentEntries(
|
||||
'{\n "component:card" = { entry = "${builtins.abort "must not evaluate"}"; export = "Card"; };\n}\n',
|
||||
),
|
||||
/literal strings|supported literal-entry shape/,
|
||||
);
|
||||
await assert.rejects(
|
||||
componentEntries('{\n "component:card" = { entry = "src/card.tsx"; export = "Card"; extra = true; };\n}\n'),
|
||||
/Unsupported component entry property/,
|
||||
);
|
||||
});
|
||||
@@ -5,6 +5,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { execFile as callback } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import { componentEntries } from "../src/capability-language/component-nix.js";
|
||||
import { scaffoldRecipe } from "../src/capability-language/scaffold-recipes.js";
|
||||
import { planStructure, applyStructure } from "../src/capability-language/structural-plan.js";
|
||||
const execFile = promisify(callback);
|
||||
@@ -31,12 +32,23 @@ test("checked component scaffolding composes with execution handlers without rep
|
||||
id: "component:card",
|
||||
declaration: 'module Card id "component:card" implements View.render;',
|
||||
});
|
||||
assert.deepEqual(JSON.parse(await fs.readFile(path.join(root, "quixos.components.json"), "utf8")), {
|
||||
"component:card": { entry: "src/components/Card.tsx", export: "Card" },
|
||||
});
|
||||
assert.deepEqual(
|
||||
{ ...(await componentEntries(await fs.readFile(path.join(root, "components.nix"), "utf8"))) },
|
||||
{
|
||||
"component:card": { entry: "src/components/Card.tsx", export: "Card" },
|
||||
},
|
||||
);
|
||||
const card = path.join(root, "src/components/Card.tsx");
|
||||
await fs.appendFile(card, "\n// authored extension\n");
|
||||
const retained = await fs.readFile(card, "utf8");
|
||||
const flake = path.join(root, "flake.nix");
|
||||
await fs.appendFile(flake, "\n# retained authored toolchain configuration\n");
|
||||
const retainedFlake = await fs.readFile(flake, "utf8");
|
||||
const entriesFile = path.join(root, "components.nix");
|
||||
await fs.writeFile(
|
||||
entriesFile,
|
||||
(await fs.readFile(entriesFile, "utf8")).replace("{\n", "{\n # retained entry comment\n"),
|
||||
);
|
||||
await apply("function", {
|
||||
name: "echo",
|
||||
id: "echo",
|
||||
@@ -48,8 +60,10 @@ test("checked component scaffolding composes with execution handlers without rep
|
||||
declaration: 'module Details id "component:details" implements View.render;',
|
||||
});
|
||||
assert.equal(await fs.readFile(card, "utf8"), retained);
|
||||
assert.equal(await fs.readFile(flake, "utf8"), retainedFlake);
|
||||
assert.match(await fs.readFile(entriesFile, "utf8"), /retained entry comment/);
|
||||
assert.match(await fs.readFile(path.join(root, "src/server.ts"), "utf8"), /echo/);
|
||||
assert.match(await fs.readFile(path.join(root, "flake.nix"), "utf8"), /react \? null/);
|
||||
assert.match(await fs.readFile(path.join(root, "flake.nix"), "utf8"), /import \.\/package\.nix/);
|
||||
await assert.rejects(
|
||||
apply("component", {
|
||||
name: "Card",
|
||||
@@ -67,6 +81,17 @@ test("checked component scaffolding composes with execution handlers without rep
|
||||
}),
|
||||
/exactly one/,
|
||||
);
|
||||
const beforeUnsupported = await fs.readFile(packageFile, "utf8");
|
||||
await fs.writeFile(entriesFile, '{\n custom = builtins.abort "must not evaluate";\n}\n');
|
||||
await assert.rejects(
|
||||
apply("component", {
|
||||
name: "Unsupported",
|
||||
id: "component:unsupported",
|
||||
declaration: 'module Unsupported id "component:unsupported" implements View.render;',
|
||||
}),
|
||||
/literal-entry shape/,
|
||||
);
|
||||
assert.equal(await fs.readFile(packageFile, "utf8"), beforeUnsupported);
|
||||
});
|
||||
|
||||
test("controller scaffolding preserves existing registrations and rejects duplicate identities", async (context) => {
|
||||
|
||||
@@ -47,7 +47,7 @@ test("execution scaffolds route typed handlers by declared effect and target wit
|
||||
assert.match(await read("src/impl/asyncWork.ts"), /RemoteImplementation/);
|
||||
assert.match(await read("src/impl/both.ts"), /PortableImplementation/);
|
||||
assert.match(await read("flake.nix"), /checkedArtifacts/);
|
||||
assert.match(await read("flake.nix"), /entries.browser/);
|
||||
assert.match(await read("package.nix"), /entries.browser/);
|
||||
await assert.rejects(add("both", "transaction targets [browser, server]"), /unique/);
|
||||
await assert.rejects(add("invalid", "snapshot targets [remote]"), /browser\/server/);
|
||||
await assert.rejects(apply("function", { name: "missing", id: "export:missing" }), /explicit execution/);
|
||||
|
||||
@@ -61,4 +61,28 @@ test("structural plans validate the graph, journal originals, and reject stale e
|
||||
await applyStructure(draft);
|
||||
assert.match(await fs.readFile(path.join(root, "workspace.qx"), "utf8"), /import interface NotImplementedYet/);
|
||||
await assert.rejects(() => planStructure(root, { ...provisional, validation: "resource-graph" }));
|
||||
const plugin = ".yarn/plugins/yarn-plugin-nixify.cjs";
|
||||
await applyStructure(
|
||||
await planStructure(root, {
|
||||
kind: "workspace",
|
||||
validation: "syntax",
|
||||
files: [
|
||||
{ file: plugin, create: "module.exports = {};\n" },
|
||||
{ file: "yarn.lock", create: "__metadata:\n version: 8\n" },
|
||||
{ file: "flake.lock", create: '{"version":7}\n' },
|
||||
],
|
||||
}),
|
||||
);
|
||||
assert.equal(await fs.readFile(path.join(root, plugin), "utf8"), "module.exports = {};\n");
|
||||
for (const file of [".yarn/../escape.cjs", ".yarn/cache/entry.cjs", ".git/hooks/hook.cjs"]) {
|
||||
await assert.rejects(
|
||||
() =>
|
||||
planStructure(root, {
|
||||
kind: "workspace",
|
||||
validation: "syntax",
|
||||
files: [{ file, create: "" }],
|
||||
}),
|
||||
/Unsafe scaffold path/,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user