feat(tooling): pre-ship approved library traces in optional core generator templates
Each of the five optional core package generators (events, realtime, audit, trpc, ui) now copies pre-written decision: approved trace files into docs/library-decisions/ at scaffold time, covering every direct runtime dependency of that core package. This prevents a pre-commit gate failure the first time a developer runs pnpm turbo gen core-package <name> — the generator is the policy-compliant path, so the traces land by construction. - Added docs/library-decisions/*.md.hbs trace files under each of the five core-package template directories (15 files total) - Updated generator config to emit traces into workspace docs/library-decisions/ via a second emitTemplateTree call per core package - Updated all five __snapshots__/core-package/*.snapshot.json to include the new trace file entries - Added verify-doc-shas.test.ts to pin SHA256 hashes of all 15 trace templates so snapshot and file content cannot drift independently ADR refs: events→ADR-015, realtime→ADR-016, audit→ADR-018; trpc and ui cite closest ADR or null where no specific ADR exists. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from "node:fs";
|
||||
import {
|
||||
existsSync,
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
readdirSync,
|
||||
statSync,
|
||||
} from "node:fs";
|
||||
import { join, relative } from "node:path";
|
||||
import type { PlopTypes } from "@turbo/gen";
|
||||
|
||||
@@ -61,8 +67,7 @@ export function splicePluginRulesAt(
|
||||
}
|
||||
const after = source.slice(idx + anchor.length);
|
||||
if (after.trimStart().startsWith(block.trim())) return; // idempotent
|
||||
const updated =
|
||||
source.slice(0, idx + anchor.length) + "\n" + block + after;
|
||||
const updated = source.slice(0, idx + anchor.length) + "\n" + block + after;
|
||||
writeFileSync(filePath, updated);
|
||||
}
|
||||
|
||||
@@ -97,7 +102,10 @@ export function addBoundariesEntry(
|
||||
}
|
||||
const modeFragment = opts.mode ? `, mode: "${opts.mode}"` : "";
|
||||
const newEntry = ` { type: "core", pattern: "${packagePath}"${modeFragment} },\n`;
|
||||
const updated = source.replace(wildcardLine[0], `\n${newEntry}${wildcardLine[1]}`);
|
||||
const updated = source.replace(
|
||||
wildcardLine[0],
|
||||
`\n${newEntry}${wildcardLine[1]}`,
|
||||
);
|
||||
writeFileSync(baseJsPath, updated);
|
||||
}
|
||||
|
||||
@@ -106,11 +114,13 @@ export function addBoundariesEntry(
|
||||
* file, returns a plop `add` action that emits the file (without .hbs
|
||||
* extension) at <destPrefix>/<relative-path>. The actions are sorted so
|
||||
* directory creation is deterministic.
|
||||
*
|
||||
* Set `opts.force` to overwrite existing files (idempotent re-runs).
|
||||
*/
|
||||
export function emitTemplateTree(
|
||||
srcPrefix: string,
|
||||
destPrefix: string,
|
||||
opts: { templatesRoot?: string } = {},
|
||||
opts: { templatesRoot?: string; force?: boolean } = {},
|
||||
): PlopTypes.AddActionConfig[] {
|
||||
// The templates directory is resolved in priority order:
|
||||
// 1. opts.templatesRoot — test injection (temp directory)
|
||||
@@ -120,13 +130,18 @@ export function emitTemplateTree(
|
||||
if (opts.templatesRoot) {
|
||||
root = opts.templatesRoot;
|
||||
} else {
|
||||
const fromRepoRoot = join(process.cwd(), "turbo", "generators", "templates");
|
||||
const fromRepoRoot = join(
|
||||
process.cwd(),
|
||||
"turbo",
|
||||
"generators",
|
||||
"templates",
|
||||
);
|
||||
const fromGeneratorsDir = join(process.cwd(), "templates");
|
||||
root = existsSync(fromRepoRoot) ? fromRepoRoot : fromGeneratorsDir;
|
||||
}
|
||||
const srcRoot = join(root, srcPrefix);
|
||||
const out: PlopTypes.AddActionConfig[] = [];
|
||||
walkHbs(srcRoot, srcRoot, srcPrefix, destPrefix, out);
|
||||
walkHbs(srcRoot, srcRoot, srcPrefix, destPrefix, out, opts.force ?? false);
|
||||
out.sort((a, b) => (a.path ?? "").localeCompare(b.path ?? ""));
|
||||
return out;
|
||||
}
|
||||
@@ -137,11 +152,12 @@ function walkHbs(
|
||||
srcPrefix: string,
|
||||
destPrefix: string,
|
||||
out: PlopTypes.AddActionConfig[],
|
||||
force: boolean,
|
||||
): void {
|
||||
for (const name of readdirSync(dir)) {
|
||||
const full = join(dir, name);
|
||||
if (statSync(full).isDirectory()) {
|
||||
walkHbs(topRoot, full, srcPrefix, destPrefix, out);
|
||||
walkHbs(topRoot, full, srcPrefix, destPrefix, out, force);
|
||||
continue;
|
||||
}
|
||||
if (!name.endsWith(".hbs")) continue;
|
||||
@@ -149,8 +165,12 @@ function walkHbs(
|
||||
out.push({
|
||||
type: "add",
|
||||
path: join(destPrefix, rel).replace(/\\/g, "/"),
|
||||
templateFile: join("templates", srcPrefix, relative(topRoot, full)).replace(/\\/g, "/"),
|
||||
templateFile: join(
|
||||
"templates",
|
||||
srcPrefix,
|
||||
relative(topRoot, full),
|
||||
).replace(/\\/g, "/"),
|
||||
force,
|
||||
} as PlopTypes.AddActionConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user