Curated, product-agnostic snapshot of the post-story-04 tree: demo content deleted, auth-only reference feature, web-next shell, all gates green. Product-specific docs, ADRs 027-029, PRDs/epics/archive, editor library traces, and product naming are curated out; generic template repairs (coverage provider devDeps, root test:coverage script, live lint fixes, root-only release-please) are kept. See TEMPLATE.md for provenance, curation list, and usage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
154 lines
5.5 KiB
TypeScript
154 lines
5.5 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import {
|
|
assertOptionalPackageNotPresent,
|
|
addToTranspilePackages,
|
|
splicePluginRulesAt,
|
|
addBoundariesEntry,
|
|
emitTemplateTree,
|
|
} from "./core-package-utils.js";
|
|
|
|
describe("assertOptionalPackageNotPresent", () => {
|
|
it("throws if packages/<name>/ exists in cwd", () => {
|
|
const tmp = mkdtempSync(join(tmpdir(), "core-pkg-"));
|
|
mkdirSync(join(tmp, "packages", "core-foo"), { recursive: true });
|
|
expect(() => assertOptionalPackageNotPresent("core-foo", tmp)).toThrow(
|
|
/already exists/,
|
|
);
|
|
});
|
|
|
|
it("returns silently if packages/<name>/ is absent", () => {
|
|
const tmp = mkdtempSync(join(tmpdir(), "core-pkg-"));
|
|
expect(() =>
|
|
assertOptionalPackageNotPresent("core-foo", tmp),
|
|
).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe("addToTranspilePackages", () => {
|
|
it("inserts package name alphabetically into transpilePackages array", () => {
|
|
const tmp = mkdtempSync(join(tmpdir(), "core-pkg-"));
|
|
const cfgPath = join(tmp, "next.config.mjs");
|
|
writeFileSync(
|
|
cfgPath,
|
|
`const nextConfig = {
|
|
transpilePackages: [
|
|
"@repo/core-cms",
|
|
"@repo/core-shared",
|
|
],
|
|
};
|
|
`,
|
|
);
|
|
addToTranspilePackages(cfgPath, "@repo/core-realtime");
|
|
const result = readFileSync(cfgPath, "utf8");
|
|
// Order should be: core-cms, core-realtime, core-shared
|
|
const order = result.match(/@repo\/core-\w+/g) ?? [];
|
|
expect(order).toEqual([
|
|
"@repo/core-cms",
|
|
"@repo/core-realtime",
|
|
"@repo/core-shared",
|
|
]);
|
|
});
|
|
|
|
it("is idempotent — duplicate insertion is a no-op", () => {
|
|
const tmp = mkdtempSync(join(tmpdir(), "core-pkg-"));
|
|
const cfgPath = join(tmp, "next.config.mjs");
|
|
writeFileSync(
|
|
cfgPath,
|
|
`const nextConfig = {
|
|
transpilePackages: ["@repo/core-realtime"],
|
|
};
|
|
`,
|
|
);
|
|
addToTranspilePackages(cfgPath, "@repo/core-realtime");
|
|
const result = readFileSync(cfgPath, "utf8");
|
|
expect(result.match(/@repo\/core-realtime/g)?.length).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("splicePluginRulesAt", () => {
|
|
it("inserts rule block at the named anchor", () => {
|
|
const tmp = mkdtempSync(join(tmpdir(), "core-pkg-"));
|
|
const path = join(tmp, "base.js");
|
|
writeFileSync(path, `before\n// <gen:realtime-rules>\nafter\n`);
|
|
splicePluginRulesAt(path, "realtime-rules", "INSERTED_BLOCK");
|
|
const result = readFileSync(path, "utf8");
|
|
expect(result).toContain("// <gen:realtime-rules>\nINSERTED_BLOCK\nafter");
|
|
});
|
|
|
|
it("is idempotent — re-inserting same block at anchor is a no-op", () => {
|
|
const tmp = mkdtempSync(join(tmpdir(), "core-pkg-"));
|
|
const path = join(tmp, "base.js");
|
|
writeFileSync(path, `// <gen:realtime-rules>\nINSERTED_BLOCK\nrest\n`);
|
|
splicePluginRulesAt(path, "realtime-rules", "INSERTED_BLOCK");
|
|
const result = readFileSync(path, "utf8");
|
|
expect(result.match(/INSERTED_BLOCK/g)?.length).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("addBoundariesEntry", () => {
|
|
it("inserts entry before the packages/core-* wildcard", () => {
|
|
const tmp = mkdtempSync(join(tmpdir(), "core-pkg-"));
|
|
const path = join(tmp, "base.js");
|
|
writeFileSync(
|
|
path,
|
|
`"boundaries/elements": [
|
|
{ type: "core-composition", pattern: "packages/core-cms" },
|
|
{ type: "core", pattern: "packages/core-*" },
|
|
{ type: "feature", pattern: "packages/!(core-*)" },
|
|
],`,
|
|
);
|
|
addBoundariesEntry(path, "packages/core-realtime", { mode: "folder" });
|
|
const result = readFileSync(path, "utf8");
|
|
// The new entry must appear BEFORE the packages/core-* wildcard
|
|
const newIdx = result.indexOf(`pattern: "packages/core-realtime"`);
|
|
const wildcardIdx = result.indexOf(`pattern: "packages/core-*"`);
|
|
expect(newIdx).toBeGreaterThan(0);
|
|
expect(newIdx).toBeLessThan(wildcardIdx);
|
|
});
|
|
|
|
it("is idempotent — re-inserting same entry is a no-op", () => {
|
|
const tmp = mkdtempSync(join(tmpdir(), "core-pkg-"));
|
|
const path = join(tmp, "base.js");
|
|
writeFileSync(
|
|
path,
|
|
`"boundaries/elements": [
|
|
{ type: "core", pattern: "packages/core-realtime", mode: "folder" },
|
|
{ type: "core", pattern: "packages/core-*" },
|
|
],`,
|
|
);
|
|
addBoundariesEntry(path, "packages/core-realtime", { mode: "folder" });
|
|
const result = readFileSync(path, "utf8");
|
|
expect(result.match(/packages\/core-realtime/g)?.length).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("emitTemplateTree", () => {
|
|
it("produces an `add` plop action per .hbs file in the template directory", () => {
|
|
// emitTemplateTree reads from turbo/generators/templates/ — the test uses a
|
|
// temp template directory injected via the `templatesRoot` arg.
|
|
const tmpTemplates = mkdtempSync(join(tmpdir(), "tpl-"));
|
|
mkdirSync(join(tmpTemplates, "core-package", "demo", "src"), {
|
|
recursive: true,
|
|
});
|
|
writeFileSync(
|
|
join(tmpTemplates, "core-package", "demo", "package.json.hbs"),
|
|
"{}",
|
|
);
|
|
writeFileSync(
|
|
join(tmpTemplates, "core-package", "demo", "src", "index.ts.hbs"),
|
|
"export {};",
|
|
);
|
|
const actions = emitTemplateTree("core-package/demo", "packages/demo", {
|
|
templatesRoot: tmpTemplates,
|
|
});
|
|
expect(actions).toHaveLength(2);
|
|
expect(actions[0]!.type).toBe("add");
|
|
const paths = actions.map((a) => (a as { path: string }).path);
|
|
expect(paths).toContain("packages/demo/package.json");
|
|
expect(paths).toContain("packages/demo/src/index.ts");
|
|
});
|
|
});
|