refactor(marketing-pages)!: delete marketing-pages demo feature
Veect retrofit (ADR-027): fourth slice of the demo-content removal. Deletes packages/marketing-pages whole and prunes every composition edge in one commit: core-api router mount + dep, core-cms collection/global composition + dep + regenerated Payload types, web-next bindAll (prod + dev-seed) + tests + about page + Tailwind source + transpilePackages + dep, cms/core-cms payload config test assertions, marketing-page e2e spec, tsconfig paths, fallow ignore entry, anchor-guard + generator e2e feature lists, compliance data-map + retention-policy regeneration, lockfile prune, and feature-list doc entries (CLAUDE.md, AGENTS.md, glossary, app/feature AGENTS.md). Event teardown: marketing-pages was the sole consumer of auth.user.signed-up (welcome-email handler + job). The handler, its Payload tasks, and the bus subscription all lived inside the package's own binders, so they die with it — no other package wires the subscription. Auth's manifest `publishes` stays untouched: a publisher with zero consumers is legal (pnpm conformance only fails on orphan consumers, verified green). The app-level sign-up-welcome-email test asserted the marketing-pages mailer stays empty without a bus; it is deleted with the feature, and the now-unused test-only bind-state helpers in web-next bind-production.ts go with it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
This commit is contained in:
@@ -5,12 +5,12 @@ describe("CMS app payload.config", () => {
|
||||
it("registers all feature collections", async () => {
|
||||
const resolved = await config;
|
||||
const slugs = resolved.collections?.map((c) => c.slug) ?? [];
|
||||
expect(slugs).toEqual(expect.arrayContaining(["users", "pages"]));
|
||||
expect(slugs).toEqual(expect.arrayContaining(["users"]));
|
||||
});
|
||||
|
||||
it("registers all feature globals", async () => {
|
||||
const resolved = await config;
|
||||
const slugs = resolved.globals?.map((g) => g.slug) ?? [];
|
||||
expect(slugs).toEqual(expect.arrayContaining(["site-settings", "header"]));
|
||||
expect(slugs).toEqual(expect.arrayContaining(["header"]));
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -26,7 +26,7 @@ pnpm dev --filter @repo/web-next # Next.js on port 3000
|
||||
| ----------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| `src/app/layout.tsx` | Root layout — wraps app with `<Providers>` |
|
||||
| `src/app/providers.tsx` | Client component wrapper (add tRPC/React Query here after scaffolding `@repo/core-trpc`) |
|
||||
| `src/app/page.tsx` | Home page — navigation + marketing content |
|
||||
| `src/app/page.tsx` | Home page |
|
||||
| `e2e/` | Playwright end-to-end tests |
|
||||
|
||||
## tRPC Setup (optional)
|
||||
@@ -104,7 +104,7 @@ Run: `pnpm test:e2e` starts the dev server and runs all `.spec.ts` files.
|
||||
|
||||
## Cross-References
|
||||
|
||||
- **Feature packages:** `packages/{auth,marketing-pages,navigation}/`
|
||||
- **Feature packages:** `packages/{auth,navigation}/`
|
||||
- **tRPC composition:** `packages/core-api/AGENTS.md`
|
||||
- **tRPC client + provider (optional):** scaffold `@repo/core-trpc` first, then see `turbo/generators/templates/core-package/trpc/AGENTS.md.hbs`
|
||||
- **UI components (optional):** scaffold with `pnpm turbo gen core-package ui`, then see `turbo/generators/templates/core-package/ui/AGENTS.md.hbs`
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test("home page renders site name + nav + article list", async ({ page }) => {
|
||||
test("home page renders heading + nav", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
// Page renders and shows site name
|
||||
// Page renders and shows the heading
|
||||
await expect(page.locator("h1").first()).toBeVisible();
|
||||
// Site name from siteSettings (mock seed: "My App")
|
||||
await expect(page.locator("body")).toContainText(/My App/i);
|
||||
// Nav element is present on the page
|
||||
const nav = page.locator("nav");
|
||||
await expect(nav).toHaveCount(1);
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test("/about renders the about marketing page", async ({ page }) => {
|
||||
await page.goto("/about");
|
||||
// Either renders the seeded page (h1 = "About us") or "not yet published" message
|
||||
// — both are HTTP 200, so the test only checks it doesn't 500.
|
||||
const status = (await page.context().request.get("/about")).status();
|
||||
expect(status).toBe(200);
|
||||
await expect(page.locator("body")).toBeVisible();
|
||||
});
|
||||
@@ -12,7 +12,6 @@ const nextConfig = {
|
||||
"@repo/core-dsr",
|
||||
"@repo/core-shared",
|
||||
"@repo/core-ui",
|
||||
"@repo/marketing-pages",
|
||||
"@repo/navigation",
|
||||
"@repo/core-trpc",
|
||||
],
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
"@repo/core-cms": "workspace:*",
|
||||
"@repo/core-shared": "workspace:*",
|
||||
"@repo/core-trpc": "workspace:^",
|
||||
"@repo/marketing-pages": "workspace:*",
|
||||
"@repo/navigation": "workspace:*",
|
||||
"@sentry/nextjs": "^10.51.0",
|
||||
"@tailwindcss/postcss": "^4.3.0",
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
// Cross-feature event bus proof-of-life (DISABLED — @repo/core-events removed).
|
||||
//
|
||||
// @repo/core-events is now optional. When absent, ctx.bus is undefined, and
|
||||
// bus?.subscribe(...) / bus?.publish(...) calls are no-ops. Cross-feature event
|
||||
// fanout does not occur until core-events is scaffolded via:
|
||||
//
|
||||
// pnpm turbo gen core-package events
|
||||
//
|
||||
// After scaffolding, restore this test and re-wire the bus in bind-production.ts
|
||||
// (see the comment in bindAll()). Until then, signing up does NOT trigger a
|
||||
// welcome email — the mailer queue stays empty.
|
||||
//
|
||||
// Replaced with a reduced test that asserts the no-bus behavior.
|
||||
|
||||
import "reflect-metadata";
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
import { bindAllDevSeed, __resetBindStateForTests } from "@/server/bind-production";
|
||||
import { authContainer } from "@repo/auth/di/container";
|
||||
import { AUTH_SYMBOLS } from "@repo/auth/di/symbols";
|
||||
import type { ISignUpController } from "@repo/auth";
|
||||
import { marketingPagesContainer } from "@repo/marketing-pages/di/container";
|
||||
import { MARKETING_PAGES_SYMBOLS } from "@repo/marketing-pages/di/symbols";
|
||||
import { RecordingMailerService } from "@repo/marketing-pages/services/recording-mailer";
|
||||
|
||||
describe("e2e: sign-up with no event bus (core-events not scaffolded)", () => {
|
||||
beforeEach(() => {
|
||||
__resetBindStateForTests();
|
||||
});
|
||||
|
||||
it("sign-up succeeds and mailer stays empty (no cross-feature fanout without bus)", async () => {
|
||||
await bindAllDevSeed();
|
||||
|
||||
const mailer = marketingPagesContainer.get<RecordingMailerService>(
|
||||
MARKETING_PAGES_SYMBOLS.IMailerService,
|
||||
);
|
||||
const signUp = authContainer.get<ISignUpController>(AUTH_SYMBOLS.ISignUpController);
|
||||
|
||||
await signUp({
|
||||
username: "testuser",
|
||||
password: "secret_password",
|
||||
confirmPassword: "secret_password",
|
||||
});
|
||||
|
||||
// Without a bus, bus?.subscribe() is a no-op so the event handler never
|
||||
// fires and the mailer receives nothing.
|
||||
await new Promise((r) => setImmediate(r));
|
||||
|
||||
expect(mailer.sent).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { PageContent } from "@repo/marketing-pages/ui";
|
||||
import { bindAll } from "../../server/bind-production";
|
||||
|
||||
export default async function AboutPage() {
|
||||
await bindAll();
|
||||
|
||||
return (
|
||||
<main className="px-6 py-8">
|
||||
<PageContent slug="about" />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -7,16 +7,10 @@ vi.mock("payload", () => ({
|
||||
vi.mock("@repo/auth/di/bind-production", () => ({
|
||||
bindProductionAuth: vi.fn(),
|
||||
}));
|
||||
vi.mock("@repo/marketing-pages/di/bind-production", () => ({
|
||||
bindProductionMarketingPages: vi.fn(),
|
||||
}));
|
||||
vi.mock("@repo/navigation/di/bind-production", () => ({
|
||||
bindProductionNavigation: vi.fn(),
|
||||
}));
|
||||
vi.mock("@repo/auth/di/bind-dev-seed", () => ({ bindDevSeedAuth: vi.fn() }));
|
||||
vi.mock("@repo/marketing-pages/di/bind-dev-seed", () => ({
|
||||
bindDevSeedMarketingPages: vi.fn(),
|
||||
}));
|
||||
vi.mock("@repo/navigation/di/bind-dev-seed", () => ({
|
||||
bindDevSeedNavigation: vi.fn(),
|
||||
}));
|
||||
@@ -39,19 +33,16 @@ describe("bindAllProduction", () => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("binds all three feature production repos", async () => {
|
||||
it("binds both feature production repos", async () => {
|
||||
const { bindAllProduction } = await import("./bind-production");
|
||||
const { bindProductionAuth } =
|
||||
await import("@repo/auth/di/bind-production");
|
||||
const { bindProductionMarketingPages } =
|
||||
await import("@repo/marketing-pages/di/bind-production");
|
||||
const { bindProductionNavigation } =
|
||||
await import("@repo/navigation/di/bind-production");
|
||||
|
||||
await bindAllProduction();
|
||||
|
||||
expect(bindProductionAuth).toHaveBeenCalledOnce();
|
||||
expect(bindProductionMarketingPages).toHaveBeenCalledOnce();
|
||||
expect(bindProductionNavigation).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
@@ -18,10 +18,8 @@ import {
|
||||
} from "@repo/core-shared/jobs";
|
||||
import { NoopRateLimit } from "@repo/core-shared/rate-limit";
|
||||
import { bindProductionAuth } from "@repo/auth/di/bind-production";
|
||||
import { bindProductionMarketingPages } from "@repo/marketing-pages/di/bind-production";
|
||||
import { bindProductionNavigation } from "@repo/navigation/di/bind-production";
|
||||
import { bindDevSeedAuth } from "@repo/auth/di/bind-dev-seed";
|
||||
import { bindDevSeedMarketingPages } from "@repo/marketing-pages/di/bind-dev-seed";
|
||||
import { bindDevSeedNavigation } from "@repo/navigation/di/bind-dev-seed";
|
||||
|
||||
let bindPromise: Promise<void> | null = null;
|
||||
@@ -96,7 +94,6 @@ export async function bindAllProduction(): Promise<void> {
|
||||
};
|
||||
|
||||
bindProductionAuth(ctx);
|
||||
bindProductionMarketingPages(ctx);
|
||||
bindProductionNavigation(ctx);
|
||||
}
|
||||
|
||||
@@ -117,7 +114,6 @@ export async function bindAllDevSeed(): Promise<void> {
|
||||
};
|
||||
|
||||
await bindDevSeedAuth(ctx);
|
||||
await bindDevSeedMarketingPages(ctx);
|
||||
await bindDevSeedNavigation(ctx);
|
||||
}
|
||||
|
||||
@@ -153,19 +149,3 @@ export function bindAll(): Promise<void> {
|
||||
|
||||
return bindPromise;
|
||||
}
|
||||
|
||||
/** Test-only resets — not exported via package. Used by bind-production.test.ts. */
|
||||
export function __resetBindStateForTests(): void {
|
||||
bindPromise = null;
|
||||
resolvedTracer = null;
|
||||
resolvedLogger = null;
|
||||
resolvedQueue = null;
|
||||
}
|
||||
|
||||
/** Test-only accessor for resolved instrumentation. */
|
||||
export function __getInstrumentationForTests(): {
|
||||
tracer: ITracer | null;
|
||||
logger: ILogger | null;
|
||||
} {
|
||||
return { tracer: resolvedTracer, logger: resolvedLogger };
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@import "tailwindcss";
|
||||
@source "../../../../packages/core-ui/src";
|
||||
@source "../../../../packages/navigation/src";
|
||||
@source "../../../../packages/marketing-pages/src";
|
||||
@source "../../../../packages/auth/src";
|
||||
@source "../";
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user