feat(generators): ui byte-identical snapshot + dispatch entry + e2e test
- Adds ui.snapshot.json (28 entries) computed from packages/core-ui - Wires CORE_PACKAGE_GENERATORS["ui"]: guard + emitTemplateTree + addToTranspilePackages + printUiNextSteps (web-tanstack/storybook wiring is printed, not generated — resists templating) - Adds "ui" to the core-package generator choices list - Adds core-package-ui.e2e.test.ts (byte-identical reconstruction) — passes in 14.6 s Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
114
turbo/generators/__snapshots__/core-package/ui.snapshot.json
Normal file
114
turbo/generators/__snapshots__/core-package/ui.snapshot.json
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"path": "AGENTS.md",
|
||||||
|
"sha256": "ea4a4eeea6cb08364fb7d9cf5c5757147442df1e9b1907b9118fcd313cd1c4a7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "eslint.config.js",
|
||||||
|
"sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "package.json",
|
||||||
|
"sha256": "8ffd2e9c8adb1e0eab863a9abc81bb90a05afdbd625d1348e0f2ba7874d0680f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/button/button.stories.tsx",
|
||||||
|
"sha256": "5ea74a9a4b2431b2a4dbae456bcce6d051842caad19e83ca8e15d99f79d36b5d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/button/button.test.tsx",
|
||||||
|
"sha256": "e768f2c1ebd7e120d543a95c67a2a703a85c5ad6847b7e7f8420fa472771fb82"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/button/button.tsx",
|
||||||
|
"sha256": "4d62a3a9703721f64d397e468cdd90225b1d6dd739fd8c17b90962c8e9a7b2c3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/button/index.ts",
|
||||||
|
"sha256": "fb0a14a0c15536e05a58d6d03f41afcee85fb6a24c5ca6910cb172ae26fbcc34"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/index.ts",
|
||||||
|
"sha256": "9ec29d14a7a7729b4eca9ba95b4016812a4c1c2cc9a46510d5f2d019744852b3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/input/index.ts",
|
||||||
|
"sha256": "490ba122bc3161dc59d24b274372608b8b319f5c0270699fcdcc3fb6d5d4cffd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/input/input.stories.tsx",
|
||||||
|
"sha256": "c95b51b7b3c4ad42f7b118a9a6569ad6d6a3200f6bf1f2d8f029a92bc307c53e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/input/input.test.tsx",
|
||||||
|
"sha256": "b0fdb584cc9def4c1c7394e6f524c5d614ac74284a50d298ee48254dd4a80162"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/input/input.tsx",
|
||||||
|
"sha256": "bd2f477b3a79a29549d18db9605e1a34290fa31ea97d6292d6be77d2e95729f4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/label/index.ts",
|
||||||
|
"sha256": "d9a205b2fd3133823a74c265e09b01dcd5068ce21b82217386d6166bbec68fb6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/label/label.test.tsx",
|
||||||
|
"sha256": "ac8135a7a05a61e34ec9346148d9305190b5f8ec3dd3a5d15df2b2841e24caac"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/atoms/label/label.tsx",
|
||||||
|
"sha256": "6c6f13125fbc96b55806b9ff2230472e8d24ba45c7cd39e58573460691e23885"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/index.ts",
|
||||||
|
"sha256": "56303fd5e5d7fe574855887defcb974d2f86667dc926ea690f3d3757bbcc4617"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/lib/utils.ts",
|
||||||
|
"sha256": "d1f1e0d62cb8d8d1e04c26e14de842d8a151f75812d81b046c65b5d1fe8e4b27"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/molecules/form-field/form-field.stories.tsx",
|
||||||
|
"sha256": "914c842de560d80091970e3e1ae15c214c396893cc735b6f25460620528e5157"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/molecules/form-field/form-field.test.tsx",
|
||||||
|
"sha256": "a63244a6a00f7fdba01223c0697a9cfe93a52917e7d09b577e9ffa6547b1399c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/molecules/form-field/form-field.tsx",
|
||||||
|
"sha256": "f51a6c4e9550fccaff90a932b9ad3906e8973c82faf0237cf07e1c864536f5f6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/molecules/form-field/index.ts",
|
||||||
|
"sha256": "7fe955a7b2c65b1732ee799812be4ac3150346c5bdc91ea6afe0ea7c1034d2cc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/molecules/index.ts",
|
||||||
|
"sha256": "eeb24cfbb8b6d2c1a5f5c144eae4db37f6829ad0f62eb163f428a666bc742774"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/organisms/index.ts",
|
||||||
|
"sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/styles/globals.css",
|
||||||
|
"sha256": "58c6e64ac0b26f3897f9a76f86a35dfff1acc7bae8e7e761bca0c8035763cfd2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/templates/index.ts",
|
||||||
|
"sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tsconfig.json",
|
||||||
|
"sha256": "7bbc28ad21b936ca80c7a3ec21094d34012d624ca1d4e63a6f3c3120abba551f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "turbo.json",
|
||||||
|
"sha256": "ae544305202c960399110a31fe976e2c38577956c5aa738989f5d885f156d130"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "vitest.config.ts",
|
||||||
|
"sha256": "053df54e347b147d85632286d44e6587aa6c3ae39677fbc1a8599e89ae48a81c"
|
||||||
|
}
|
||||||
|
]
|
||||||
52
turbo/generators/__tests__/core-package-ui.e2e.test.ts
Normal file
52
turbo/generators/__tests__/core-package-ui.e2e.test.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { mkdtempSync, cpSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { execSync } from "node:child_process";
|
||||||
|
import { join, resolve } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { computeSnapshot } from "../lib/snapshot";
|
||||||
|
import expectedSnapshot from "../__snapshots__/core-package/ui.snapshot.json";
|
||||||
|
|
||||||
|
// Repo root is 2 levels up from turbo/generators/__tests__
|
||||||
|
const REPO_ROOT = resolve(fileURLToPath(import.meta.url), "..", "..", "..", "..");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip "@repo/core-ui" from a package.json file in the tmp tree.
|
||||||
|
* Required when simulating a fresh scaffold: core-ui does not exist yet
|
||||||
|
* but the snapshot was captured before removal. Apps in the current tree
|
||||||
|
* still list it as a dependency.
|
||||||
|
*/
|
||||||
|
function stripCoreUiDep(pkgJsonPath: string): void {
|
||||||
|
const raw = readFileSync(pkgJsonPath, "utf8");
|
||||||
|
const parsed = JSON.parse(raw) as Record<string, Record<string, string>>;
|
||||||
|
for (const section of ["dependencies", "devDependencies", "peerDependencies"] as const) {
|
||||||
|
if (parsed[section]?.["@repo/core-ui"]) {
|
||||||
|
delete parsed[section]["@repo/core-ui"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeFileSync(pkgJsonPath, JSON.stringify(parsed, null, 2) + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("e2e: core-package ui", () => {
|
||||||
|
it("byte-identical reconstruction matches snapshot", { timeout: 120_000 }, () => {
|
||||||
|
const tmp = mkdtempSync(join(tmpdir(), "e2e-ui-"));
|
||||||
|
cpSync(REPO_ROOT, tmp, {
|
||||||
|
recursive: true,
|
||||||
|
filter: (src) =>
|
||||||
|
!src.includes("node_modules") &&
|
||||||
|
!src.includes(".turbo") &&
|
||||||
|
!src.includes("packages/core-ui"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Strip @repo/core-ui from app package.json files so pnpm install
|
||||||
|
// succeeds without the package being present (simulating the post-removal state).
|
||||||
|
stripCoreUiDep(join(tmp, "apps", "web-next", "package.json"));
|
||||||
|
stripCoreUiDep(join(tmp, "apps", "web-tanstack", "package.json"));
|
||||||
|
stripCoreUiDep(join(tmp, "apps", "storybook", "package.json"));
|
||||||
|
|
||||||
|
execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, { stdio: "ignore" });
|
||||||
|
execSync(`cd ${tmp} && pnpm turbo gen core-package --args ui`, { stdio: "ignore" });
|
||||||
|
const result = computeSnapshot(join(tmp, "packages/core-ui"));
|
||||||
|
expect(result).toEqual(expectedSnapshot);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -654,6 +654,18 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js";
|
|||||||
},
|
},
|
||||||
printTrpcNextSteps,
|
printTrpcNextSteps,
|
||||||
],
|
],
|
||||||
|
ui: () => [
|
||||||
|
() => {
|
||||||
|
assertOptionalPackageNotPresent("core-ui");
|
||||||
|
return "Guard passed — packages/core-ui does not exist yet.";
|
||||||
|
},
|
||||||
|
...emitTemplateTree("core-package/ui", "packages/core-ui"),
|
||||||
|
() => {
|
||||||
|
addToTranspilePackages("apps/web-next/next.config.mjs", "@repo/core-ui");
|
||||||
|
return "Added @repo/core-ui to transpilePackages.";
|
||||||
|
},
|
||||||
|
printUiNextSteps,
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
plop.setGenerator("core-package", {
|
plop.setGenerator("core-package", {
|
||||||
@@ -663,7 +675,7 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js";
|
|||||||
type: "list",
|
type: "list",
|
||||||
name: "name",
|
name: "name",
|
||||||
message: "Which optional core package?",
|
message: "Which optional core package?",
|
||||||
choices: ["realtime", "events", "trpc"],
|
choices: ["realtime", "events", "trpc", "ui"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
actions: (answers) => {
|
actions: (answers) => {
|
||||||
@@ -1187,6 +1199,37 @@ function printTrpcNextSteps(): string {
|
|||||||
].join("\n");
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function printUiNextSteps(): string {
|
||||||
|
return [
|
||||||
|
"─────────────────────────────────────────────────────────────",
|
||||||
|
"@repo/core-ui scaffolded.",
|
||||||
|
"",
|
||||||
|
"Next steps (manual wiring — not generated):",
|
||||||
|
"",
|
||||||
|
" 1. pnpm install # link the new workspace package",
|
||||||
|
"",
|
||||||
|
" 2. apps/web-next/package.json:",
|
||||||
|
' - add "@repo/core-ui": "workspace:*" to dependencies',
|
||||||
|
"",
|
||||||
|
" 3. apps/storybook/.storybook/main.ts:",
|
||||||
|
' - add "../../../packages/core-ui/src/**/*.stories.@(ts|tsx)" to the stories array',
|
||||||
|
"",
|
||||||
|
" 4. apps/storybook/.storybook/preview.ts:",
|
||||||
|
' - add: import "@repo/core-ui/styles/globals.css";',
|
||||||
|
"",
|
||||||
|
" 5. apps/storybook/package.json:",
|
||||||
|
' - add "@repo/core-ui": "workspace:*" to dependencies',
|
||||||
|
"",
|
||||||
|
" 6. apps/web-tanstack/package.json:",
|
||||||
|
' - add "@repo/core-ui": "workspace:*" to dependencies',
|
||||||
|
" - when TanStack Start gains a real vite.config.ts, add @repo/core-ui",
|
||||||
|
" to optimizeDeps.include",
|
||||||
|
"",
|
||||||
|
" 7. pnpm typecheck && pnpm lint && pnpm test",
|
||||||
|
"─────────────────────────────────────────────────────────────",
|
||||||
|
].join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
function printEventsNextSteps(): string {
|
function printEventsNextSteps(): string {
|
||||||
return [
|
return [
|
||||||
"─────────────────────────────────────────────────────────────",
|
"─────────────────────────────────────────────────────────────",
|
||||||
|
|||||||
Reference in New Issue
Block a user