cms-renderer 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/trpc.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * tRPC Client Setup
3
+ *
4
+ * Creates the tRPC React hooks for client components.
5
+ * Uses tRPC v11 patterns with TanStack Query v5.
6
+ */
7
+
8
+ import type { AppRouter } from '@repo/cms-schema/trpc';
9
+ import { type CreateTRPCReact, createTRPCReact } from '@trpc/react-query';
10
+
11
+ /**
12
+ * tRPC React client.
13
+ *
14
+ * Use this in client components to call tRPC procedures.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * 'use client';
19
+ * import { trpc } from '@/lib/trpc';
20
+ *
21
+ * function NavigationClient() {
22
+ * const { data, isLoading } = trpc.blocks.getNavigation.useQuery();
23
+ * if (isLoading) return <div>Loading...</div>;
24
+ * return <nav>{data?.links.map(link => ...)}</nav>;
25
+ * }
26
+ * ```
27
+ */
28
+ export const trpc: CreateTRPCReact<AppRouter, unknown> = createTRPCReact<AppRouter>();
package/lib/types.ts ADDED
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Block Rendering Types
3
+ *
4
+ * Type definitions for the ComponentMap pattern.
5
+ * Block types map to React components via the blockComponents registry.
6
+ */
7
+
8
+ import type { ComponentType } from 'react';
9
+
10
+ // -----------------------------------------------------------------------------
11
+ // Block Type Discriminant
12
+ // -----------------------------------------------------------------------------
13
+
14
+ /**
15
+ * Valid block type strings.
16
+ * Must match the schema_name field in block data from the tRPC API.
17
+ */
18
+ export type BlockType =
19
+ | 'navigation'
20
+ | 'header'
21
+ | 'article'
22
+ | 'hero-block'
23
+ | 'features-block'
24
+ | 'cta-block'
25
+ | 'logo-trust-block';
26
+
27
+ // -----------------------------------------------------------------------------
28
+ // Content Types (inferred from seed data)
29
+ // -----------------------------------------------------------------------------
30
+
31
+ /**
32
+ * Navigation link button structure.
33
+ */
34
+ export interface NavigationButton {
35
+ label: string;
36
+ href: string;
37
+ ariaLabel: string;
38
+ }
39
+
40
+ /**
41
+ * Navigation link with type and button.
42
+ */
43
+ export interface NavigationLink {
44
+ button: NavigationButton;
45
+ type: 'Default' | 'Flyout';
46
+ }
47
+
48
+ /**
49
+ * Level 3 navigation item (leaf node).
50
+ */
51
+ export interface Level3Link {
52
+ link: NavigationLink;
53
+ }
54
+
55
+ /**
56
+ * Level 2 navigation item with optional Level 3 children.
57
+ */
58
+ export interface Level2Link {
59
+ link: NavigationLink;
60
+ children?: Level3Link[];
61
+ }
62
+
63
+ /**
64
+ * Level 1 navigation item with optional Level 2 children.
65
+ */
66
+ export interface Level1Link {
67
+ link: NavigationLink;
68
+ children?: Level2Link[];
69
+ }
70
+
71
+ /**
72
+ * Navigation block content.
73
+ */
74
+ export interface NavigationContent {
75
+ logo?: {
76
+ url: string;
77
+ alt: string;
78
+ };
79
+ ariaLabel: string;
80
+ links: Level1Link[];
81
+ }
82
+
83
+ /**
84
+ * Header block content.
85
+ */
86
+ export interface HeaderContent {
87
+ headline: string;
88
+ subheadline?: string;
89
+ backgroundImage?: {
90
+ url: string;
91
+ alt: string;
92
+ };
93
+ ctaButton?: {
94
+ label: string;
95
+ href: string;
96
+ };
97
+ alignment: 'left' | 'center' | 'right';
98
+ }
99
+
100
+ /**
101
+ * Article block content.
102
+ */
103
+ export interface ArticleContent {
104
+ headline: string;
105
+ author?: string;
106
+ publishedAt?: string;
107
+ body: string;
108
+ tags?: readonly string[] | string[];
109
+ status?: string;
110
+ }
111
+
112
+ /**
113
+ * Hero block content (from CMS schema).
114
+ */
115
+ export type HeroBlockContent = import('@repo/cms-schema/blocks').HeroBlockContent;
116
+
117
+ /**
118
+ * Features block content (from CMS schema).
119
+ */
120
+ export type FeaturesBlockContent = import('@repo/cms-schema/blocks').FeaturesBlockContent;
121
+
122
+ /**
123
+ * CTA block content (from CMS schema).
124
+ */
125
+ export type CTABlockContent = import('@repo/cms-schema/blocks').CTABlockContent;
126
+
127
+ /**
128
+ * Logo Trust block content (from CMS schema).
129
+ */
130
+ export type LogoTrustBlockContent = import('@repo/cms-schema/blocks').LogoTrustBlockContent;
131
+
132
+ // -----------------------------------------------------------------------------
133
+ // Block Data Union
134
+ // -----------------------------------------------------------------------------
135
+
136
+ /**
137
+ * Discriminated union of all block types.
138
+ * Use the `type` field to narrow to specific content types.
139
+ *
140
+ * Each block also carries its stable CMS `id`, which should be used as the
141
+ * React `key` when rendering lists of blocks.
142
+ *
143
+ * @example
144
+ * ```tsx
145
+ * function renderBlock(block: BlockData) {
146
+ * if (block.type === 'header') {
147
+ * // TypeScript knows block.content is HeaderContent
148
+ * return <h1>{block.content.headline}</h1>;
149
+ * }
150
+ * }
151
+ * ```
152
+ */
153
+ export type BlockData =
154
+ | { id: string; type: 'navigation'; content: NavigationContent }
155
+ | { id: string; type: 'header'; content: HeaderContent }
156
+ | { id: string; type: 'article'; content: ArticleContent }
157
+ | { id: string; type: 'hero-block'; content: HeroBlockContent }
158
+ | { id: string; type: 'features-block'; content: FeaturesBlockContent }
159
+ | { id: string; type: 'cta-block'; content: CTABlockContent }
160
+ | { id: string; type: 'logo-trust-block'; content: LogoTrustBlockContent };
161
+
162
+ // -----------------------------------------------------------------------------
163
+ // Component Types
164
+ // -----------------------------------------------------------------------------
165
+
166
+ /**
167
+ * Props for a block component.
168
+ * Each block component receives its typed content.
169
+ */
170
+ export interface BlockComponentProps<T> {
171
+ content: T;
172
+ }
173
+
174
+ /**
175
+ * A React component that renders a specific block type.
176
+ */
177
+ export type BlockComponent<T> = ComponentType<BlockComponentProps<T>>;
178
+
179
+ /**
180
+ * Registry of block type to component mappings.
181
+ * This is the core of the ComponentMap pattern.
182
+ */
183
+ export type BlockComponentRegistry = {
184
+ [K in BlockType]: BlockComponent<
185
+ K extends 'navigation'
186
+ ? NavigationContent
187
+ : K extends 'header'
188
+ ? HeaderContent
189
+ : K extends 'article'
190
+ ? ArticleContent
191
+ : K extends 'hero-block'
192
+ ? HeroBlockContent
193
+ : K extends 'features-block'
194
+ ? FeaturesBlockContent
195
+ : K extends 'cta-block'
196
+ ? CTABlockContent
197
+ : K extends 'logo-trust-block'
198
+ ? LogoTrustBlockContent
199
+ : never
200
+ >;
201
+ };
package/next.config.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { resolve } from 'node:path';
2
+ import type { NextConfig } from 'next';
3
+
4
+ const nextConfig: NextConfig = {
5
+ turbopack: {
6
+ root: resolve(import.meta.dirname, '../..'),
7
+ },
8
+ transpilePackages: ['@repo/cms-schema', '@repo/markdown-wasm'],
9
+ serverExternalPackages: ['md4w'],
10
+ images: {
11
+ remotePatterns: [
12
+ {
13
+ protocol: 'https',
14
+ hostname: '**',
15
+ },
16
+ {
17
+ protocol: 'http',
18
+ hostname: 'localhost',
19
+ },
20
+ ],
21
+ },
22
+ // Prevent browser caching of dynamic pages
23
+ async headers() {
24
+ return [
25
+ {
26
+ // Apply to all pages
27
+ source: '/:path*',
28
+ headers: [
29
+ {
30
+ key: 'Cache-Control',
31
+ value: 'no-store, must-revalidate',
32
+ },
33
+ ],
34
+ },
35
+ ];
36
+ },
37
+ };
38
+
39
+ export default nextConfig;
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "cms-renderer",
3
+ "version": "0.0.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "exports": {
7
+ "./components/*": "./components/*",
8
+ "./components/blocks/*": "./components/blocks/*",
9
+ "./lib/*": "./lib/*",
10
+ "./server/*": "./server/*",
11
+ "./styles/*": "./styles/*"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "scripts": {
17
+ "lint": "biome check .",
18
+ "check-types": "tsc --noEmit",
19
+ "clean": "rm -rf .next .turbo node_modules"
20
+ },
21
+ "dependencies": {
22
+ "@auteur/trpc-utils": "0.0.0",
23
+ "@repo/supabase-utils": "0.0.0",
24
+ "@hookform/resolvers": "^5.1.0",
25
+ "@lexical/code": "0.39.0",
26
+ "@lexical/link": "0.39.0",
27
+ "@lexical/list": "0.39.0",
28
+ "@lexical/markdown": "0.39.0",
29
+ "@lexical/react": "0.39.0",
30
+ "@lexical/rich-text": "0.39.0",
31
+ "@lexical/selection": "0.39.0",
32
+ "@lexical/utils": "0.39.0",
33
+ "@repo/cms-schema": "0.0.0",
34
+ "@repo/markdown-wasm": "0.0.0",
35
+ "@supabase/ssr": "0.8.0",
36
+ "@supabase/supabase-js": "2.90.1",
37
+ "@tanstack/react-query": "5.90.16",
38
+ "@testing-library/react": "16.3.1",
39
+ "@trpc/client": "11.8.1",
40
+ "@trpc/react-query": "11.8.1",
41
+ "@trpc/server": "11.8.1",
42
+ "next": "^16.1.1",
43
+ "react": "^19.1.0",
44
+ "react-dom": "^19.1.0",
45
+ "superjson": "2.2.6",
46
+ "zod": "4.3.5"
47
+ },
48
+ "devDependencies": {
49
+ "@happy-dom/global-registrator": "20.1.0",
50
+ "@repo/typescript-config": "0.0.0",
51
+ "@tailwindcss/postcss": "4",
52
+ "@testing-library/jest-dom": "6.9.1",
53
+ "@types/node": "^25",
54
+ "@types/react": "^19",
55
+ "@types/react-dom": "^19",
56
+ "chalk": "5.6.2",
57
+ "jest-diff": "30.2.0",
58
+ "tailwindcss": "4",
59
+ "typescript": "^5.9.3"
60
+ }
61
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ },
5
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "@repo/typescript-config/nextjs.json",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "baseUrl": ".",
6
+ "paths": {
7
+ "@/*": ["./*"]
8
+ }
9
+ },
10
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
11
+ "exclude": ["node_modules", "tests", "**/__tests__/**", "**/*.test.ts", "**/*.test.tsx", "e2e"]
12
+ }