popeye-cli 1.5.0 → 1.6.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.
Files changed (161) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +50 -8
  3. package/dist/cli/commands/create.d.ts.map +1 -1
  4. package/dist/cli/commands/create.js +54 -4
  5. package/dist/cli/commands/create.js.map +1 -1
  6. package/dist/cli/interactive.d.ts +29 -0
  7. package/dist/cli/interactive.d.ts.map +1 -1
  8. package/dist/cli/interactive.js +90 -7
  9. package/dist/cli/interactive.js.map +1 -1
  10. package/dist/generators/all.d.ts +4 -1
  11. package/dist/generators/all.d.ts.map +1 -1
  12. package/dist/generators/all.js +36 -316
  13. package/dist/generators/all.js.map +1 -1
  14. package/dist/generators/doc-parser.d.ts +18 -3
  15. package/dist/generators/doc-parser.d.ts.map +1 -1
  16. package/dist/generators/doc-parser.js +81 -10
  17. package/dist/generators/doc-parser.js.map +1 -1
  18. package/dist/generators/frontend-design-analyzer.d.ts +30 -0
  19. package/dist/generators/frontend-design-analyzer.d.ts.map +1 -0
  20. package/dist/generators/frontend-design-analyzer.js +208 -0
  21. package/dist/generators/frontend-design-analyzer.js.map +1 -0
  22. package/dist/generators/shared-packages.d.ts +45 -0
  23. package/dist/generators/shared-packages.d.ts.map +1 -0
  24. package/dist/generators/shared-packages.js +456 -0
  25. package/dist/generators/shared-packages.js.map +1 -0
  26. package/dist/generators/templates/index.d.ts +4 -0
  27. package/dist/generators/templates/index.d.ts.map +1 -1
  28. package/dist/generators/templates/index.js +4 -0
  29. package/dist/generators/templates/index.js.map +1 -1
  30. package/dist/generators/templates/website-components.d.ts.map +1 -1
  31. package/dist/generators/templates/website-components.js +36 -11
  32. package/dist/generators/templates/website-components.js.map +1 -1
  33. package/dist/generators/templates/website-config.d.ts +15 -1
  34. package/dist/generators/templates/website-config.d.ts.map +1 -1
  35. package/dist/generators/templates/website-config.js +155 -13
  36. package/dist/generators/templates/website-config.js.map +1 -1
  37. package/dist/generators/templates/website-landing.d.ts +24 -0
  38. package/dist/generators/templates/website-landing.d.ts.map +1 -0
  39. package/dist/generators/templates/website-landing.js +276 -0
  40. package/dist/generators/templates/website-landing.js.map +1 -0
  41. package/dist/generators/templates/website-layout.d.ts +42 -0
  42. package/dist/generators/templates/website-layout.d.ts.map +1 -0
  43. package/dist/generators/templates/website-layout.js +408 -0
  44. package/dist/generators/templates/website-layout.js.map +1 -0
  45. package/dist/generators/templates/website-pricing.d.ts +11 -0
  46. package/dist/generators/templates/website-pricing.d.ts.map +1 -0
  47. package/dist/generators/templates/website-pricing.js +313 -0
  48. package/dist/generators/templates/website-pricing.js.map +1 -0
  49. package/dist/generators/templates/website-sections.d.ts +102 -0
  50. package/dist/generators/templates/website-sections.d.ts.map +1 -0
  51. package/dist/generators/templates/website-sections.js +444 -0
  52. package/dist/generators/templates/website-sections.js.map +1 -0
  53. package/dist/generators/templates/website.d.ts +10 -50
  54. package/dist/generators/templates/website.d.ts.map +1 -1
  55. package/dist/generators/templates/website.js +12 -788
  56. package/dist/generators/templates/website.js.map +1 -1
  57. package/dist/generators/website-content-scanner.d.ts +37 -0
  58. package/dist/generators/website-content-scanner.d.ts.map +1 -0
  59. package/dist/generators/website-content-scanner.js +165 -0
  60. package/dist/generators/website-content-scanner.js.map +1 -0
  61. package/dist/generators/website-context.d.ts +38 -2
  62. package/dist/generators/website-context.d.ts.map +1 -1
  63. package/dist/generators/website-context.js +179 -19
  64. package/dist/generators/website-context.js.map +1 -1
  65. package/dist/generators/website-debug.d.ts +68 -0
  66. package/dist/generators/website-debug.d.ts.map +1 -0
  67. package/dist/generators/website-debug.js +93 -0
  68. package/dist/generators/website-debug.js.map +1 -0
  69. package/dist/generators/website.d.ts +2 -0
  70. package/dist/generators/website.d.ts.map +1 -1
  71. package/dist/generators/website.js +66 -4
  72. package/dist/generators/website.js.map +1 -1
  73. package/dist/generators/workspace-root.d.ts +27 -0
  74. package/dist/generators/workspace-root.d.ts.map +1 -0
  75. package/dist/generators/workspace-root.js +100 -0
  76. package/dist/generators/workspace-root.js.map +1 -0
  77. package/dist/state/index.d.ts +8 -0
  78. package/dist/state/index.d.ts.map +1 -1
  79. package/dist/state/index.js +10 -0
  80. package/dist/state/index.js.map +1 -1
  81. package/dist/types/workflow.d.ts +6 -0
  82. package/dist/types/workflow.d.ts.map +1 -1
  83. package/dist/types/workflow.js +2 -0
  84. package/dist/types/workflow.js.map +1 -1
  85. package/dist/upgrade/handlers.d.ts +15 -0
  86. package/dist/upgrade/handlers.d.ts.map +1 -1
  87. package/dist/upgrade/handlers.js +52 -0
  88. package/dist/upgrade/handlers.js.map +1 -1
  89. package/dist/workflow/auto-fix-bundler.d.ts +37 -0
  90. package/dist/workflow/auto-fix-bundler.d.ts.map +1 -0
  91. package/dist/workflow/auto-fix-bundler.js +320 -0
  92. package/dist/workflow/auto-fix-bundler.js.map +1 -0
  93. package/dist/workflow/auto-fix.d.ts.map +1 -1
  94. package/dist/workflow/auto-fix.js +10 -3
  95. package/dist/workflow/auto-fix.js.map +1 -1
  96. package/dist/workflow/index.d.ts +1 -0
  97. package/dist/workflow/index.d.ts.map +1 -1
  98. package/dist/workflow/index.js +12 -0
  99. package/dist/workflow/index.js.map +1 -1
  100. package/dist/workflow/overview.d.ts.map +1 -1
  101. package/dist/workflow/overview.js +4 -0
  102. package/dist/workflow/overview.js.map +1 -1
  103. package/dist/workflow/plan-mode.d.ts +4 -3
  104. package/dist/workflow/plan-mode.d.ts.map +1 -1
  105. package/dist/workflow/plan-mode.js +69 -5
  106. package/dist/workflow/plan-mode.js.map +1 -1
  107. package/dist/workflow/website-strategy.d.ts +9 -0
  108. package/dist/workflow/website-strategy.d.ts.map +1 -1
  109. package/dist/workflow/website-strategy.js +73 -1
  110. package/dist/workflow/website-strategy.js.map +1 -1
  111. package/dist/workflow/website-updater.d.ts.map +1 -1
  112. package/dist/workflow/website-updater.js +15 -4
  113. package/dist/workflow/website-updater.js.map +1 -1
  114. package/package.json +1 -1
  115. package/src/cli/commands/create.ts +58 -4
  116. package/src/cli/interactive.ts +96 -7
  117. package/src/generators/all.ts +44 -332
  118. package/src/generators/doc-parser.ts +87 -10
  119. package/src/generators/frontend-design-analyzer.ts +261 -0
  120. package/src/generators/shared-packages.ts +500 -0
  121. package/src/generators/templates/index.ts +4 -0
  122. package/src/generators/templates/website-components.ts +36 -11
  123. package/src/generators/templates/website-config.ts +166 -13
  124. package/src/generators/templates/website-landing.ts +331 -0
  125. package/src/generators/templates/website-layout.ts +443 -0
  126. package/src/generators/templates/website-pricing.ts +330 -0
  127. package/src/generators/templates/website-sections.ts +541 -0
  128. package/src/generators/templates/website.ts +38 -851
  129. package/src/generators/website-content-scanner.ts +208 -0
  130. package/src/generators/website-context.ts +248 -20
  131. package/src/generators/website-debug.ts +130 -0
  132. package/src/generators/website.ts +71 -3
  133. package/src/generators/workspace-root.ts +113 -0
  134. package/src/state/index.ts +14 -0
  135. package/src/types/workflow.ts +6 -0
  136. package/src/upgrade/handlers.ts +65 -0
  137. package/src/workflow/auto-fix-bundler.ts +392 -0
  138. package/src/workflow/auto-fix.ts +11 -3
  139. package/src/workflow/index.ts +12 -0
  140. package/src/workflow/overview.ts +6 -0
  141. package/src/workflow/plan-mode.ts +81 -7
  142. package/src/workflow/website-strategy.ts +75 -1
  143. package/src/workflow/website-updater.ts +17 -6
  144. package/tests/cli/project-naming.test.ts +136 -0
  145. package/tests/generators/doc-parser.test.ts +121 -0
  146. package/tests/generators/frontend-design-analyzer.test.ts +90 -0
  147. package/tests/generators/quality-gate.test.ts +183 -0
  148. package/tests/generators/shared-packages.test.ts +83 -0
  149. package/tests/generators/website-components.test.ts +1 -1
  150. package/tests/generators/website-config.test.ts +84 -0
  151. package/tests/generators/website-content-scanner.test.ts +181 -0
  152. package/tests/generators/website-context.test.ts +109 -0
  153. package/tests/generators/website-debug.test.ts +77 -0
  154. package/tests/generators/website-landing.test.ts +188 -0
  155. package/tests/generators/website-pricing.test.ts +98 -0
  156. package/tests/generators/website-sections.test.ts +245 -0
  157. package/tests/generators/workspace-root.test.ts +105 -0
  158. package/tests/upgrade/handlers.test.ts +162 -0
  159. package/tests/workflow/auto-fix-bundler.test.ts +242 -0
  160. package/tests/workflow/plan-mode.test.ts +111 -1
  161. package/tests/workflow/website-strategy.test.ts +55 -0
@@ -1,790 +1,14 @@
1
1
  /**
2
- * Website content templates for Next.js marketing sites
3
- * Generates SEO-ready content pages with optional project context
4
- * and strategy-driven marketing content
5
- */
6
- // Strategy data is accessed via context.strategy (WebsiteContentContext includes it)
7
- /**
8
- * Generate root layout.tsx with metadata
9
- */
10
- export function generateWebsiteLayout(projectName, context) {
11
- const title = projectName
12
- .split('-')
13
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
14
- .join(' ');
15
- const strategy = context?.strategy;
16
- const displayName = context?.productName || title;
17
- const desc = strategy?.messaging.longDescription
18
- || context?.description
19
- || `${displayName} - Your modern web application`;
20
- // SEO keywords from strategy or defaults
21
- const keywords = strategy?.seoStrategy.primaryKeywords
22
- ? strategy.seoStrategy.primaryKeywords.map(k => `'${escapeJsx(k)}'`).join(', ')
23
- : `'${projectName}', 'web app', 'nextjs'`;
24
- return `import type { Metadata } from 'next';
25
- import { Inter } from 'next/font/google';
26
- import './globals.css';
27
-
28
- const inter = Inter({
29
- subsets: ['latin'],
30
- variable: '--font-inter',
31
- });
32
-
33
- const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
34
-
35
- export const metadata: Metadata = {
36
- metadataBase: new URL(BASE_URL),
37
- title: {
38
- default: '${escapeJsx(displayName)}',
39
- template: '%s | ${escapeJsx(displayName)}',
40
- },
41
- description: '${escapeJsx(desc)}',
42
- keywords: [${keywords}],
43
- authors: [{ name: '${escapeJsx(displayName)} Team' }],
44
- creator: '${escapeJsx(displayName)}',
45
- openGraph: {
46
- type: 'website',
47
- locale: 'en_US',
48
- url: BASE_URL,
49
- siteName: '${escapeJsx(displayName)}',
50
- title: '${escapeJsx(displayName)}',
51
- description: '${escapeJsx(desc)}',
52
- },
53
- twitter: {
54
- card: 'summary_large_image',
55
- title: '${escapeJsx(displayName)}',
56
- description: '${escapeJsx(desc)}',
57
- },
58
- robots: {
59
- index: true,
60
- follow: true,
61
- },
62
- };
63
-
64
- export default function RootLayout({
65
- children,
66
- }: {
67
- children: React.ReactNode;
68
- }) {
69
- return (
70
- <html lang="en" className={inter.variable}>
71
- <body className="min-h-screen bg-white antialiased">
72
- {children}
73
- </body>
74
- </html>
75
- );
76
- }
77
- `;
78
- }
79
- /**
80
- * Generate globals.css with optional brand colors
81
- */
82
- export function generateWebsiteGlobalsCss(context) {
83
- // Convert hex to HSL for CSS custom properties if brand color provided
84
- const primaryHsl = context?.brand?.primaryColor
85
- ? hexToHslString(context.brand.primaryColor)
86
- : '199 89% 48%';
87
- return `@tailwind base;
88
- @tailwind components;
89
- @tailwind utilities;
90
-
91
- @layer base {
92
- :root {
93
- --background: 0 0% 100%;
94
- --foreground: 222.2 84% 4.9%;
95
- --primary: ${primaryHsl};
96
- --primary-foreground: 210 40% 98%;
97
- }
98
-
99
- body {
100
- @apply bg-background text-foreground;
101
- }
102
- }
103
-
104
- @layer components {
105
- .container {
106
- @apply mx-auto max-w-7xl px-4 sm:px-6 lg:px-8;
107
- }
108
- }
109
- `;
110
- }
111
- /**
112
- * Generate landing page.tsx with optional context-driven content
113
- * When strategy is available, uses strategy messaging, trust signals, and CTAs
114
- */
115
- export function generateWebsiteLandingPage(projectName, context) {
116
- const title = projectName
117
- .split('-')
118
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
119
- .join(' ');
120
- const strategy = context?.strategy;
121
- const displayName = context?.productName || title;
122
- // Strategy-driven or context-driven hero
123
- const headline = strategy?.messaging.headline || displayName;
124
- const subheadline = strategy?.messaging.subheadline || '';
125
- const heroText = strategy?.messaging.longDescription
126
- ? escapeJsx(strategy.messaging.longDescription)
127
- : context?.description
128
- ? escapeJsx(context.description)
129
- : null;
130
- const features = context?.features && context.features.length > 0
131
- ? context.features.slice(0, 6)
132
- : null;
133
- // CTAs from strategy or defaults
134
- const primaryCtaText = strategy?.conversionStrategy.primaryCta.text || 'Get started';
135
- const primaryCtaHref = strategy?.conversionStrategy.primaryCta.href || '/pricing';
136
- const secondaryCtaText = strategy?.conversionStrategy.secondaryCta.text || 'Learn more';
137
- const secondaryCtaHref = strategy?.conversionStrategy.secondaryCta.href || '/docs';
138
- // Build hero paragraph
139
- const heroParagraph = heroText
140
- ? ` ${heroText}`
141
- : ` {/* TODO: populate from project specification */}`;
142
- // Build features array
143
- const featuresBlock = features
144
- ? features.map((f) => ` {\n title: '${escapeJsx(f.title)}',\n description: '${escapeJsx(f.description)}',\n }`).join(',\n')
145
- : ` {\n title: 'Feature 1',\n description: '/* TODO: populate from project specification */',\n },\n {\n title: 'Feature 2',\n description: '/* TODO: populate from project specification */',\n },\n {\n title: 'Feature 3',\n description: '/* TODO: populate from project specification */',\n }`;
146
- // Trust signals from strategy
147
- const trustSignals = strategy?.conversionStrategy.trustSignals || [];
148
- const trustSignalsBlock = trustSignals.length > 0
149
- ? trustSignals.map(s => ` '${escapeJsx(s)}'`).join(',\n')
150
- : '';
151
- // Social proof from strategy
152
- const socialProof = strategy?.conversionStrategy.socialProof || [];
153
- const socialProofBlock = socialProof.length > 0
154
- ? socialProof.map(s => ` '${escapeJsx(s)}'`).join(',\n')
155
- : '';
156
- // Build optional sections
157
- const trustSection = trustSignals.length > 0 ? `
158
- {/* Trust Signals */}
159
- <section className="border-y border-gray-100 bg-gray-50 py-12">
160
- <div className="container">
161
- <div className="flex flex-wrap items-center justify-center gap-x-8 gap-y-4">
162
- {[
163
- ${trustSignalsBlock}
164
- ].map((signal) => (
165
- <p key={signal} className="text-sm font-medium text-gray-600">{signal}</p>
166
- ))}
167
- </div>
168
- </div>
169
- </section>
170
- ` : '';
171
- const socialProofSection = socialProof.length > 0 ? `
172
- {/* Social Proof */}
173
- <section className="py-16 sm:py-24">
174
- <div className="container">
175
- <h2 className="text-center text-3xl font-bold tracking-tight text-gray-900">
176
- Trusted by teams everywhere
177
- </h2>
178
- <div className="mx-auto mt-12 grid max-w-4xl grid-cols-1 gap-8 md:grid-cols-2">
179
- {[
180
- ${socialProofBlock}
181
- ].map((quote, i) => (
182
- <blockquote key={i} className="rounded-2xl border border-gray-200 p-6">
183
- <p className="text-gray-700">&ldquo;{quote}&rdquo;</p>
184
- </blockquote>
185
- ))}
186
- </div>
187
- </div>
188
- </section>
189
- ` : '';
190
- // Metadata: strategy-driven or default
191
- const metaTitle = strategy?.seoStrategy.titleTemplates?.home || 'Welcome';
192
- const metaDesc = strategy?.seoStrategy.metaDescriptions?.home || `Welcome to ${displayName}`;
193
- return `import type { Metadata } from 'next';
194
- import Link from 'next/link';
195
- import Header from '@/components/Header';
196
- import Footer from '@/components/Footer';
197
- import JsonLd from '@/components/JsonLd';
198
-
199
- export const metadata: Metadata = {
200
- title: '${escapeJsx(metaTitle)}',
201
- description: '${escapeJsx(metaDesc)}',
202
- };
203
-
204
- const ORG_SCHEMA = {
205
- '@context': 'https://schema.org',
206
- '@type': 'Organization',
207
- name: '${escapeJsx(displayName)}',
208
- url: process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com',
209
- };
210
-
211
- const PRODUCT_SCHEMA = {
212
- '@context': 'https://schema.org',
213
- '@type': 'SoftwareApplication',
214
- name: '${escapeJsx(displayName)}',
215
- applicationCategory: 'BusinessApplication',
216
- operatingSystem: 'Web',
217
- };
218
-
219
- export default function HomePage() {
220
- return (
221
- <>
222
- <Header />
223
- <JsonLd schema={ORG_SCHEMA} />
224
- <JsonLd schema={PRODUCT_SCHEMA} />
225
- <main className="flex min-h-screen flex-col">
226
- {/* Hero Section */}
227
- <section className="relative overflow-hidden bg-gradient-to-b from-primary-50 to-white py-20 sm:py-32">
228
- <div className="container">
229
- <div className="mx-auto max-w-2xl text-center">
230
- <h1 className="text-4xl font-bold tracking-tight text-gray-900 sm:text-6xl">
231
- ${escapeJsx(headline)}
232
- </h1>
233
- ${subheadline ? ` <p className="mt-4 text-xl font-medium text-primary-600">\n ${escapeJsx(subheadline)}\n </p>` : ''}
234
- <p className="mt-6 text-lg leading-8 text-gray-600">
235
- ${heroParagraph}
236
- </p>
237
- <div className="mt-10 flex items-center justify-center gap-x-6">
238
- <Link
239
- href="${escapeJsx(primaryCtaHref)}"
240
- className="rounded-md bg-primary-600 px-6 py-3 text-sm font-semibold text-white shadow-sm hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600"
241
- >
242
- ${escapeJsx(primaryCtaText)}
243
- </Link>
244
- <Link
245
- href="${escapeJsx(secondaryCtaHref)}"
246
- className="text-sm font-semibold leading-6 text-gray-900 hover:text-primary-600"
247
- >
248
- ${escapeJsx(secondaryCtaText)} <span aria-hidden="true">-&gt;</span>
249
- </Link>
250
- </div>
251
- </div>
252
- </div>
253
- </section>
254
- ${trustSection}
255
- {/* Features Section */}
256
- <section id="features" className="py-20 sm:py-32">
257
- <div className="container">
258
- <div className="mx-auto max-w-2xl text-center">
259
- <h2 className="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">
260
- Everything you need
261
- </h2>
262
- <p className="mt-4 text-lg text-gray-600">
263
- {/* TODO: populate section subtitle from project specification */}
264
- </p>
265
- </div>
266
- <div className="mx-auto mt-16 max-w-5xl">
267
- <div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
268
- {[
269
- ${featuresBlock}
270
- ].map((feature) => (
271
- <div
272
- key={feature.title}
273
- className="rounded-2xl border border-gray-200 p-8"
274
- >
275
- <h3 className="text-lg font-semibold text-gray-900">
276
- {feature.title}
277
- </h3>
278
- <p className="mt-2 text-gray-600">{feature.description}</p>
279
- </div>
280
- ))}
281
- </div>
282
- </div>
283
- </div>
284
- </section>
285
- ${socialProofSection}
286
- {/* CTA Section */}
287
- <section className="bg-primary-600 py-16 sm:py-24">
288
- <div className="container text-center">
289
- <h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
290
- Ready to get started?
291
- </h2>
292
- <p className="mt-4 text-lg text-primary-100">
293
- ${strategy?.messaging.elevatorPitch ? escapeJsx(strategy.messaging.elevatorPitch) : 'Start building today.'}
294
- </p>
295
- <div className="mt-8">
296
- <Link
297
- href="${escapeJsx(primaryCtaHref)}"
298
- className="rounded-md bg-white px-6 py-3 text-sm font-semibold text-primary-600 shadow-sm hover:bg-primary-50"
299
- >
300
- ${escapeJsx(primaryCtaText)}
301
- </Link>
302
- </div>
303
- </div>
304
- </section>
305
- </main>
306
- <Footer />
307
- </>
308
- );
309
- }
310
- `;
311
- }
312
- /**
313
- * Generate pricing page with optional context-driven tiers and FAQ
314
- */
315
- export function generateWebsitePricingPage(projectName, context) {
316
- const title = projectName
317
- .split('-')
318
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
319
- .join(' ');
320
- const strategy = context?.strategy;
321
- const displayName = context?.productName || title;
322
- const tiers = context?.pricing && context.pricing.length > 0
323
- ? context.pricing
324
- : null;
325
- // Build tiers array
326
- const tiersBlock = tiers
327
- ? tiers.map((t) => {
328
- const featuresStr = t.features.map((f) => ` '${escapeJsx(f)}'`).join(',\n');
329
- return ` {
330
- name: '${escapeJsx(t.name)}',
331
- price: '${escapeJsx(t.price)}',
332
- description: '${escapeJsx(t.description)}',
333
- features: [
334
- ${featuresStr}
335
- ],
336
- cta: '${escapeJsx(t.cta)}',
337
- featured: ${t.featured ? 'true' : 'false'},
338
- }`;
339
- }).join(',\n')
340
- : ` {
341
- name: '/* TODO: tier name */',
342
- price: '/* TODO */',
343
- description: '/* TODO: populate from project specification */',
344
- features: ['/* TODO: populate from project specification */'],
345
- cta: 'Get started',
346
- featured: false,
347
- },
348
- {
349
- name: '/* TODO: tier name */',
350
- price: '/* TODO */',
351
- description: '/* TODO: populate from project specification */',
352
- features: ['/* TODO: populate from project specification */'],
353
- cta: 'Start free trial',
354
- featured: true,
355
- },
356
- {
357
- name: '/* TODO: tier name */',
358
- price: '/* TODO */',
359
- description: '/* TODO: populate from project specification */',
360
- features: ['/* TODO: populate from project specification */'],
361
- cta: 'Contact sales',
362
- featured: false,
363
- }`;
364
- // Pricing metadata from strategy or defaults
365
- const metaTitle = strategy?.seoStrategy.titleTemplates?.pricing || 'Pricing';
366
- const metaDesc = strategy?.seoStrategy.metaDescriptions?.pricing || `Choose the perfect plan for your needs - ${displayName}`;
367
- // Enterprise CTA from strategy
368
- const enterpriseCtaText = strategy?.conversionStrategy.primaryCta.text || 'Contact Sales';
369
- return `import type { Metadata } from 'next';
370
- import Link from 'next/link';
371
- import Header from '@/components/Header';
372
- import Footer from '@/components/Footer';
373
-
374
- export const metadata: Metadata = {
375
- title: '${escapeJsx(metaTitle)}',
376
- description: '${escapeJsx(metaDesc)}',
377
- };
378
-
379
- const tiers = [
380
- ${tiersBlock}
381
- ];
382
-
383
- export default function PricingPage() {
384
- return (
385
- <>
386
- <Header />
387
- <main className="py-20 sm:py-32">
388
- <div className="container">
389
- <div className="mx-auto max-w-2xl text-center">
390
- <h1 className="text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl">
391
- Simple, transparent pricing
392
- </h1>
393
- <p className="mt-6 text-lg text-gray-600">
394
- Choose the plan that works best for you.
395
- </p>
396
- </div>
397
-
398
- <div className="mx-auto mt-16 grid max-w-lg grid-cols-1 gap-8 lg:max-w-5xl lg:grid-cols-3">
399
- {tiers.map((tier) => (
400
- <div
401
- key={tier.name}
402
- className={\`rounded-2xl p-8 \${
403
- tier.featured
404
- ? 'bg-primary-600 text-white ring-2 ring-primary-600'
405
- : 'border border-gray-200 bg-white'
406
- }\`}
407
- >
408
- <h2
409
- className={\`text-lg font-semibold \${
410
- tier.featured ? 'text-white' : 'text-gray-900'
411
- }\`}
412
- >
413
- {tier.name}
414
- </h2>
415
- <p
416
- className={\`mt-2 text-sm \${
417
- tier.featured ? 'text-primary-100' : 'text-gray-600'
418
- }\`}
419
- >
420
- {tier.description}
421
- </p>
422
- <p className="mt-6">
423
- <span
424
- className={\`text-4xl font-bold \${
425
- tier.featured ? 'text-white' : 'text-gray-900'
426
- }\`}
427
- >
428
- {tier.price}
429
- </span>
430
- {tier.price !== 'Custom' && (
431
- <span
432
- className={\`text-sm \${
433
- tier.featured ? 'text-primary-100' : 'text-gray-600'
434
- }\`}
435
- >
436
- /month
437
- </span>
438
- )}
439
- </p>
440
- <ul className="mt-8 space-y-4">
441
- {tier.features.map((feature) => (
442
- <li
443
- key={feature}
444
- className={\`flex text-sm \${
445
- tier.featured ? 'text-primary-100' : 'text-gray-600'
446
- }\`}
447
- >
448
- <svg
449
- className={\`h-5 w-5 flex-shrink-0 \${
450
- tier.featured ? 'text-white' : 'text-primary-600'
451
- }\`}
452
- viewBox="0 0 20 20"
453
- fill="currentColor"
454
- >
455
- <path
456
- fillRule="evenodd"
457
- d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
458
- clipRule="evenodd"
459
- />
460
- </svg>
461
- <span className="ml-3">{feature}</span>
462
- </li>
463
- ))}
464
- </ul>
465
- <button
466
- className={\`mt-8 w-full rounded-md px-4 py-2 text-sm font-semibold \${
467
- tier.featured
468
- ? 'bg-white text-primary-600 hover:bg-primary-50'
469
- : 'bg-primary-600 text-white hover:bg-primary-500'
470
- }\`}
471
- >
472
- {tier.cta}
473
- </button>
474
- </div>
475
- ))}
476
- </div>
477
-
478
- {/* Enterprise CTA */}
479
- <div className="mx-auto mt-16 max-w-2xl text-center">
480
- <h2 className="text-2xl font-bold text-gray-900">
481
- Need a custom plan?
482
- </h2>
483
- <p className="mt-4 text-gray-600">
484
- Contact our sales team for enterprise pricing and custom solutions.
485
- </p>
486
- <Link
487
- href="/contact"
488
- className="mt-6 inline-block rounded-md border border-primary-600 px-6 py-3 text-sm font-semibold text-primary-600 hover:bg-primary-50"
489
- >
490
- ${escapeJsx(enterpriseCtaText)}
491
- </Link>
492
- </div>
493
- </div>
494
- </main>
495
- <Footer />
496
- </>
497
- );
498
- }
499
- `;
500
- }
501
- /**
502
- * Generate sitemap.ts
503
- */
504
- export function generateWebsiteSitemap(projectName) {
505
- return `import { MetadataRoute } from 'next';
506
-
507
- export default function sitemap(): MetadataRoute.Sitemap {
508
- const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
509
-
510
- return [
511
- {
512
- url: baseUrl,
513
- lastModified: new Date(),
514
- changeFrequency: 'weekly',
515
- priority: 1,
516
- },
517
- {
518
- url: \`\${baseUrl}/pricing\`,
519
- lastModified: new Date(),
520
- changeFrequency: 'monthly',
521
- priority: 0.8,
522
- },
523
- {
524
- url: \`\${baseUrl}/docs\`,
525
- lastModified: new Date(),
526
- changeFrequency: 'weekly',
527
- priority: 0.8,
528
- },
529
- {
530
- url: \`\${baseUrl}/blog\`,
531
- lastModified: new Date(),
532
- changeFrequency: 'daily',
533
- priority: 0.7,
534
- },
535
- ];
536
- }
537
- `;
538
- }
539
- /**
540
- * Generate robots.ts
541
- */
542
- export function generateWebsiteRobots(projectName) {
543
- return `import { MetadataRoute } from 'next';
544
-
545
- export default function robots(): MetadataRoute.Robots {
546
- const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
547
-
548
- return {
549
- rules: [
550
- {
551
- userAgent: '*',
552
- allow: '/',
553
- disallow: ['/api/', '/admin/'],
554
- },
555
- ],
556
- sitemap: \`\${baseUrl}/sitemap.xml\`,
557
- };
558
- }
559
- `;
560
- }
561
- /**
562
- * Generate website README
563
- */
564
- export function generateWebsiteReadme(projectName) {
565
- const title = projectName
566
- .split('-')
567
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
568
- .join(' ');
569
- return `# ${title} Website
570
-
571
- Next.js marketing website with SEO optimization.
572
-
573
- ## Getting Started
574
-
575
- \`\`\`bash
576
- # Install dependencies
577
- npm install
578
-
579
- # Run development server (port 3001)
580
- npm run dev
581
-
582
- # Build for production
583
- npm run build
584
-
585
- # Run production server
586
- npm start
587
- \`\`\`
588
-
589
- ## SEO Features
590
-
591
- - Server-side rendering (SSR)
592
- - Auto-generated sitemap
593
- - robots.txt configuration
594
- - OpenGraph and Twitter meta tags
595
- - Structured data support
596
-
597
- ## Project Structure
598
-
599
- \`\`\`
600
- src/
601
- app/
602
- layout.tsx # Root layout with metadata
603
- page.tsx # Landing page
604
- pricing/ # Pricing page
605
- docs/ # Documentation
606
- blog/ # Blog
607
- sitemap.ts # Auto-generated sitemap
608
- robots.ts # robots.txt config
609
- components/ # UI components
610
- lib/ # Utilities
611
- content/
612
- blog/ # MDX blog posts
613
- docs/ # MDX documentation
614
- \`\`\`
615
-
616
- ## Development
617
-
618
- - Port: 3001 (to avoid conflicts with frontend on 5173)
619
- - API URL: Configure via NEXT_PUBLIC_APP_URL
620
- `;
621
- }
622
- /**
623
- * Generate website spec JSON with optional context
624
- */
625
- export function generateWebsiteSpec(projectName, context) {
626
- const title = projectName
627
- .split('-')
628
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
629
- .join(' ');
630
- const displayName = context?.productName || title;
631
- const tagline = context?.tagline || context?.description || 'Build something amazing';
632
- const primaryColor = context?.brand?.primaryColor || '#0ea5e9';
633
- return JSON.stringify({
634
- version: '1.0',
635
- brand: {
636
- name: displayName,
637
- tagline,
638
- colors: {
639
- primary: primaryColor,
640
- secondary: '#64748b',
641
- accent: '#f59e0b',
642
- background: '#ffffff',
643
- foreground: '#0f172a',
644
- },
645
- typography: {
646
- headingFont: 'Inter',
647
- bodyFont: 'Inter',
648
- },
649
- },
650
- seo: {
651
- title: displayName,
652
- description: context?.description || `${displayName} - Your modern web application`,
653
- keywords: [projectName, 'web app', 'nextjs', 'saas'],
654
- locale: 'en_US',
655
- },
656
- pages: [
657
- { name: 'Home', path: '/', type: 'landing' },
658
- { name: 'Pricing', path: '/pricing', type: 'pricing' },
659
- { name: 'Documentation', path: '/docs', type: 'docs' },
660
- { name: 'Blog', path: '/blog', type: 'blog' },
661
- ],
662
- cta: {
663
- primary: { text: 'Get Started', href: '/pricing' },
664
- secondary: { text: 'Learn More', href: '/docs' },
665
- },
666
- features: {
667
- analytics: true,
668
- newsletter: false,
669
- mdxBlog: true,
670
- docsSearch: false,
671
- },
672
- }, null, 2);
673
- }
674
- /**
675
- * Generate sample test for website
676
- */
677
- export function generateWebsiteTest(projectName) {
678
- const title = projectName
679
- .split('-')
680
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
681
- .join(' ');
682
- return `import { describe, it, expect } from 'vitest';
683
- import { render, screen } from '@testing-library/react';
684
- import HomePage from '@/app/page';
685
-
686
- describe('HomePage', () => {
687
- it('renders the title', () => {
688
- render(<HomePage />);
689
- expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('${title}');
690
- });
691
-
692
- it('renders the call-to-action buttons', () => {
693
- render(<HomePage />);
694
- expect(screen.getByRole('link', { name: /get started/i })).toBeInTheDocument();
695
- expect(screen.getByRole('link', { name: /learn more/i })).toBeInTheDocument();
696
- });
697
- });
698
- `;
699
- }
700
- /**
701
- * Generate docs page placeholder
702
- */
703
- export function generateWebsiteDocsPage() {
704
- return `import type { Metadata } from 'next';
705
-
706
- export const metadata: Metadata = {
707
- title: 'Documentation',
708
- description: 'Learn how to use our platform with comprehensive documentation.',
709
- };
710
-
711
- export default function DocsPage() {
712
- return (
713
- <main className="py-20">
714
- <div className="container">
715
- <h1 className="text-4xl font-bold text-gray-900">Documentation</h1>
716
- <p className="mt-4 text-lg text-gray-600">
717
- Documentation coming soon...
718
- </p>
719
- </div>
720
- </main>
721
- );
722
- }
723
- `;
724
- }
725
- /**
726
- * Generate blog listing page placeholder
727
- */
728
- export function generateWebsiteBlogPage() {
729
- return `import type { Metadata } from 'next';
730
-
731
- export const metadata: Metadata = {
732
- title: 'Blog',
733
- description: 'Latest news, updates, and insights from our team.',
734
- };
735
-
736
- export default function BlogPage() {
737
- return (
738
- <main className="py-20">
739
- <div className="container">
740
- <h1 className="text-4xl font-bold text-gray-900">Blog</h1>
741
- <p className="mt-4 text-lg text-gray-600">
742
- Blog posts coming soon...
743
- </p>
744
- </div>
745
- </main>
746
- );
747
- }
748
- `;
749
- }
750
- /**
751
- * Escape a string for safe use inside JSX template literals
752
- */
753
- function escapeJsx(str) {
754
- return str
755
- .replace(/\\/g, '\\\\')
756
- .replace(/'/g, "\\'")
757
- .replace(/`/g, '\\`')
758
- .replace(/\$/g, '\\$');
759
- }
760
- /**
761
- * Convert hex color to HSL string for CSS custom properties
762
- * Returns format: "H S% L%"
763
- */
764
- function hexToHslString(hex) {
765
- // Remove # prefix
766
- const h = hex.replace('#', '');
767
- const r = parseInt(h.substring(0, 2), 16) / 255;
768
- const g = parseInt(h.substring(2, 4), 16) / 255;
769
- const b = parseInt(h.substring(4, 6), 16) / 255;
770
- const max = Math.max(r, g, b);
771
- const min = Math.min(r, g, b);
772
- const l = (max + min) / 2;
773
- if (max === min) {
774
- return `0 0% ${Math.round(l * 100)}%`;
775
- }
776
- const d = max - min;
777
- const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
778
- let hue = 0;
779
- if (max === r) {
780
- hue = ((g - b) / d + (g < b ? 6 : 0)) / 6;
781
- }
782
- else if (max === g) {
783
- hue = ((b - r) / d + 2) / 6;
784
- }
785
- else {
786
- hue = ((r - g) / d + 4) / 6;
787
- }
788
- return `${Math.round(hue * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
789
- }
2
+ * Website content templates - re-export module
3
+ * Backward compatibility: all functions are available from their new homes
4
+ * but can still be imported from this file
5
+ */
6
+ // Landing page (10-section redesign)
7
+ export { generateWebsiteLandingPage, generateWebsiteLandingPageWithInfo } from './website-landing.js';
8
+ // Pricing page (with toggle + comparison)
9
+ export { generateWebsitePricingPage } from './website-pricing.js';
10
+ // Layout, CSS, utility pages
11
+ export { generateWebsiteLayout, generateWebsiteGlobalsCss, generateWebsiteSitemap, generateWebsiteRobots, generateWebsiteReadme, generateWebsiteSpec, generateWebsiteTest, generateWebsiteDocsPage, generateWebsiteBlogPage, } from './website-layout.js';
12
+ // Reusable section generators
13
+ export { mapFeatureIcon, isNumericMetric, generatePainPointsSection, generateDifferentiatorsSection, generateHowItWorksSection, generateStatsSection, generateSocialProofSection, generatePricingTeaserSection, generateFaqSection, } from './website-sections.js';
790
14
  //# sourceMappingURL=website.js.map