feat(generators): wire events entry + e2e test
Add EVENTS_RULE_BLOCK constant, events entry in CORE_PACKAGE_GENERATORS dispatch table, events choice in prompts, and printEventsNextSteps helper. Events entry emits 15 template files + transpilePackages splice + ESLint no-restricted-syntax splice (E1 + J blocks). Add byte-identical e2e test that strips @repo/core-events deps before pnpm install, scaffolds via gen core-package events, and diffs against the snapshot. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -525,6 +525,65 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js";
|
||||
},
|
||||
},`;
|
||||
|
||||
const EVENTS_RULE_BLOCK = ` {
|
||||
files: ["**/*.{ts,tsx,mjs,cjs,js}"],
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector:
|
||||
"ExportNamedDeclaration[source.value=/\\/events\\/handlers\\//]",
|
||||
message:
|
||||
"Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
"ExportAllDeclaration[source.value=/\\/events\\/handlers\\//]",
|
||||
message:
|
||||
"Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
"MemberExpression[object.type='MemberExpression'][object.object.type='Identifier'][object.object.name='payload'][object.property.type='Identifier'][object.property.name='jobs']",
|
||||
message:
|
||||
"Direct \`payload.jobs.*\` access is not allowed here. Use IJobQueue (from @repo/core-shared/jobs) instead. Allowed only in **/integrations/cms/jobs/** and **/core-shared/src/jobs/**.",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// J — \`payload.jobs.*\` is allowed only in the integration layer.
|
||||
// In these paths, no-restricted-syntax is narrowed to keep E1 active but
|
||||
// drop the payload.jobs check.
|
||||
// Note: "**/core-shared/src/jobs/**" does not match from within a package-local
|
||||
// ESLint run because ESLint resolves globs relative to the config file location.
|
||||
// The pattern is kept for documentation; in practice, the PayloadJobQueue class
|
||||
// uses \`this.payload.jobs.*\` which the selector already ignores (it only catches
|
||||
// bare \`payload.jobs.*\`). Any new file added there that does use bare \`payload.jobs.*\`
|
||||
// would need this allowlist to be expressed as "**/jobs/payload-*" or similar.
|
||||
{
|
||||
files: [
|
||||
"**/integrations/cms/jobs/**",
|
||||
"**/core-shared/src/jobs/**",
|
||||
],
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector:
|
||||
"ExportNamedDeclaration[source.value=/\\/events\\/handlers\\//]",
|
||||
message:
|
||||
"Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
"ExportAllDeclaration[source.value=/\\/events\\/handlers\\//]",
|
||||
message:
|
||||
"Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
|
||||
},
|
||||
],
|
||||
},
|
||||
},`;
|
||||
|
||||
const CORE_PACKAGE_GENERATORS: Record<string, () => PlopTypes.ActionType[]> =
|
||||
{
|
||||
realtime: () => [
|
||||
@@ -563,6 +622,26 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js";
|
||||
},
|
||||
printRealtimeNextSteps,
|
||||
],
|
||||
events: () => [
|
||||
() => {
|
||||
assertOptionalPackageNotPresent("core-events");
|
||||
return "Guard passed — packages/core-events does not exist yet.";
|
||||
},
|
||||
...emitTemplateTree("core-package/events", "packages/core-events"),
|
||||
() => {
|
||||
addToTranspilePackages("apps/web-next/next.config.mjs", "@repo/core-events");
|
||||
return "Added @repo/core-events to transpilePackages.";
|
||||
},
|
||||
() => {
|
||||
splicePluginRulesAt(
|
||||
"packages/core-eslint/base.js",
|
||||
"events-rules",
|
||||
EVENTS_RULE_BLOCK,
|
||||
);
|
||||
return "Added events rule block to base.js.";
|
||||
},
|
||||
printEventsNextSteps,
|
||||
],
|
||||
};
|
||||
|
||||
plop.setGenerator("core-package", {
|
||||
@@ -572,7 +651,7 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js";
|
||||
type: "list",
|
||||
name: "name",
|
||||
message: "Which optional core package?",
|
||||
choices: ["realtime"],
|
||||
choices: ["realtime", "events"],
|
||||
},
|
||||
],
|
||||
actions: (answers) => {
|
||||
@@ -1067,6 +1146,28 @@ function printHandlerNextSteps(a: {
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function printEventsNextSteps(): string {
|
||||
return [
|
||||
"─────────────────────────────────────────────────────────────",
|
||||
"@repo/core-events scaffolded.",
|
||||
"",
|
||||
"Next steps (manual wiring — not generated):",
|
||||
"",
|
||||
" 1. pnpm install # link the new workspace package",
|
||||
"",
|
||||
" 2. apps/web-next/src/server/bind-production.ts:",
|
||||
' - import { InMemoryEventBus, PayloadJobsEventBus, type IEventBus } from "@repo/core-events";',
|
||||
" - construct bus in resolveEventsAndJobsProduction + resolveEventsAndJobsDevSeed",
|
||||
" - add bus to BindProductionContext generic arg and ctx object",
|
||||
" - update BindAllDeps to include bus field",
|
||||
"",
|
||||
" 3. Add @repo/core-events to each feature package.json that publishes or subscribes events",
|
||||
"",
|
||||
" 4. pnpm typecheck && pnpm lint && pnpm test",
|
||||
"─────────────────────────────────────────────────────────────",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function printRealtimeNextSteps(): string {
|
||||
return [
|
||||
"─────────────────────────────────────────────────────────────",
|
||||
|
||||
Reference in New Issue
Block a user