popeye-cli 1.9.1 → 1.9.3

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 (53) hide show
  1. package/dist/generators/all.d.ts +4 -0
  2. package/dist/generators/all.d.ts.map +1 -1
  3. package/dist/generators/all.js +7 -22
  4. package/dist/generators/all.js.map +1 -1
  5. package/dist/generators/templates/database-docker.d.ts +2 -1
  6. package/dist/generators/templates/database-docker.d.ts.map +1 -1
  7. package/dist/generators/templates/database-docker.js +3 -17
  8. package/dist/generators/templates/database-docker.js.map +1 -1
  9. package/dist/generators/templates/website-components.d.ts.map +1 -1
  10. package/dist/generators/templates/website-components.js +3 -1
  11. package/dist/generators/templates/website-components.js.map +1 -1
  12. package/dist/generators/templates/website-landing.d.ts.map +1 -1
  13. package/dist/generators/templates/website-landing.js +4 -8
  14. package/dist/generators/templates/website-landing.js.map +1 -1
  15. package/dist/generators/templates/website-pricing.d.ts.map +1 -1
  16. package/dist/generators/templates/website-pricing.js +3 -27
  17. package/dist/generators/templates/website-pricing.js.map +1 -1
  18. package/dist/generators/templates/website-sections.d.ts +5 -0
  19. package/dist/generators/templates/website-sections.d.ts.map +1 -1
  20. package/dist/generators/templates/website-sections.js +43 -5
  21. package/dist/generators/templates/website-sections.js.map +1 -1
  22. package/dist/generators/templates/website-seo.d.ts.map +1 -1
  23. package/dist/generators/templates/website-seo.js +1 -0
  24. package/dist/generators/templates/website-seo.js.map +1 -1
  25. package/dist/generators/templates/website.d.ts +1 -1
  26. package/dist/generators/templates/website.d.ts.map +1 -1
  27. package/dist/generators/templates/website.js +1 -1
  28. package/dist/generators/templates/website.js.map +1 -1
  29. package/dist/generators/website.d.ts.map +1 -1
  30. package/dist/generators/website.js +5 -0
  31. package/dist/generators/website.js.map +1 -1
  32. package/dist/workflow/audit-analyzer.d.ts.map +1 -1
  33. package/dist/workflow/audit-analyzer.js +15 -3
  34. package/dist/workflow/audit-analyzer.js.map +1 -1
  35. package/dist/workflow/tester.d.ts.map +1 -1
  36. package/dist/workflow/tester.js +8 -0
  37. package/dist/workflow/tester.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/generators/all.ts +7 -22
  40. package/src/generators/templates/database-docker.ts +3 -17
  41. package/src/generators/templates/website-components.ts +3 -1
  42. package/src/generators/templates/website-landing.ts +3 -8
  43. package/src/generators/templates/website-pricing.ts +3 -27
  44. package/src/generators/templates/website-sections.ts +44 -5
  45. package/src/generators/templates/website-seo.ts +1 -0
  46. package/src/generators/templates/website.ts +1 -0
  47. package/src/generators/website.ts +5 -0
  48. package/src/workflow/audit-analyzer.ts +15 -3
  49. package/src/workflow/tester.ts +8 -0
  50. package/tests/generators/database.test.ts +3 -2
  51. package/tests/generators/fe-be-db-integration.test.ts +4 -3
  52. package/tests/generators/website-landing.test.ts +4 -1
  53. package/tests/generators/website-sections.test.ts +14 -3
@@ -185,13 +185,25 @@ ${scan.dockerComposeContent}
185
185
  // Framework-specific checks
186
186
  const frameworks = scan.components.map((c) => c.framework).filter(Boolean);
187
187
  if (frameworks.some((f) => f === 'next')) {
188
- sections.push(`## Next.js-Specific Checks (IMPORTANT)
189
- - Check for hydration mismatches: event handlers (onClick, onSubmit, onChange) in Server Components (files WITHOUT 'use client' directive) cause hydration errors
188
+ sections.push(`## Next.js-Specific Checks (IMPORTANT — Hydration Errors)
189
+
190
+ ### Server vs Client Component Architecture
191
+ - Pages (page.tsx) should be Server Components by default — do NOT add 'use client' to pages
192
+ - Only extract interactive parts (forms, accordions, modals, toggles) into separate Client Component files
193
+ - If a page has 'use client' ONLY because of one interactive section (e.g. FAQ accordion), flag as major issue: extract the interactive part into its own 'use client' component file and keep the page as a Server Component
194
+ - Server Components never hydrate, so they cannot have hydration mismatches
195
+
196
+ ### Hydration Mismatch Detection
197
+ - Check for event handlers (onClick, onSubmit, onChange) in Server Components (files WITHOUT 'use client' directive) — these cause hydration errors
190
198
  - Check for \`new Date()\`, \`Date.now()\`, \`Math.random()\` in Server Components — these produce different values on server vs client
191
199
  - Check for \`typeof window\`, \`localStorage\`, \`navigator\` usage in render path of Server Components
200
+ - Check for \`<script>\` tags with dangerouslySetInnerHTML in Client Components — add suppressHydrationWarning or move to Server Component
192
201
  - Check for invalid HTML nesting: \`<p>\` inside \`<p>\`, \`<div>\` inside \`<p>\`, block elements inside inline elements
202
+
203
+ ### Component Directive Verification
193
204
  - Verify that components with hooks (useState, useEffect, useRef) have 'use client' directive
194
- - Check for proper 'use client' boundary interactive components (forms, buttons with handlers) must be Client Components`);
205
+ - Components with form handlers (onSubmit) or interactive elements must be Client Components
206
+ - Components rendering \`new Date().getFullYear()\` in JSX should be Client Components OR use suppressHydrationWarning`);
195
207
  }
196
208
  if (frameworks.some((f) => f === 'react' || f === 'vue' || f === 'svelte')) {
197
209
  sections.push(`## Frontend Framework Checks
@@ -134,6 +134,14 @@ export function getComponentPlaybook(language: OutputLanguage): string {
134
134
  - Verify SEO meta tags with custom assertions (title, description, OG tags)
135
135
  - Test responsive layouts with viewport size assertions
136
136
  - Lighthouse CI for performance regression testing
137
+
138
+ ### Hydration Error Prevention (CRITICAL for Next.js)
139
+ - Verify page.tsx files are Server Components (NO 'use client') — pages should not hydrate
140
+ - Interactive sections (FAQ accordions, forms, modals) must be in separate 'use client' component files
141
+ - Check that components with event handlers (onClick, onSubmit) or hooks (useState) have 'use client'
142
+ - Check that <script> tags with dangerouslySetInnerHTML use suppressHydrationWarning
143
+ - Verify no new Date(), Math.random(), or window/localStorage access in Server Component render paths
144
+ - Run \`npm run build\` to catch hydration warnings at build time
137
145
  `.trim();
138
146
 
139
147
  if (language === 'python') return pythonPlaybook;
@@ -299,12 +299,13 @@ describe('generateDockerComposeWithDb', () => {
299
299
  });
300
300
 
301
301
  describe('generateAllDockerComposeWithDb', () => {
302
- it('should include postgres, frontend, backend, and website services', () => {
302
+ it('should include postgres, frontend, and backend services (no website)', () => {
303
303
  const result = generateAllDockerComposeWithDb(TEST_PROJECT);
304
304
  expect(result).toContain('postgres:');
305
305
  expect(result).toContain('frontend:');
306
306
  expect(result).toContain('backend:');
307
- expect(result).toContain('website:');
307
+ // Website runs outside Docker (npm run dev / npm start)
308
+ expect(result).not.toContain('website:');
308
309
  });
309
310
 
310
311
  it('should have backend depends_on postgres with healthy condition', () => {
@@ -328,11 +328,12 @@ describe('Docker Compose: Fullstack service wiring', () => {
328
328
  describe('Docker Compose: All project service wiring', () => {
329
329
  const compose = generateAllDockerComposeWithDb(TEST_PROJECT);
330
330
 
331
- it('should include frontend, backend, website, and postgres services', () => {
331
+ it('should include frontend, backend, and postgres services (no website)', () => {
332
332
  expect(compose).toContain('frontend:');
333
333
  expect(compose).toContain('backend:');
334
- expect(compose).toContain('website:');
335
334
  expect(compose).toContain('postgres:');
335
+ // Website runs outside Docker (npm run dev / npm start)
336
+ expect(compose).not.toContain('website:');
336
337
  });
337
338
 
338
339
  it('backend should depend on postgres with health condition', () => {
@@ -344,7 +345,7 @@ describe('Docker Compose: All project service wiring', () => {
344
345
  expect(compose).toContain(networkName);
345
346
  // Count network references - should appear in services + network definition
346
347
  const networkCount = (compose.match(new RegExp(networkName, 'g')) || []).length;
347
- expect(networkCount).toBeGreaterThanOrEqual(5); // 4 services + 1 definition
348
+ expect(networkCount).toBeGreaterThanOrEqual(4); // 3 services + 1 definition
348
349
  });
349
350
 
350
351
  it('frontend API URL should point to backend service', () => {
@@ -121,7 +121,10 @@ describe('generateWebsiteLandingPageWithInfo', () => {
121
121
  const result = generateWebsiteLandingPageWithInfo('gateco', makeContext());
122
122
  expect(result.code).toContain('Is it secure?');
123
123
  expect(result.code).toContain('Does it scale?');
124
- expect(result.code).toContain('FaqItem');
124
+ // Uses FaqSection component (separate client component) instead of inline FaqItem
125
+ expect(result.code).toContain('FaqSection');
126
+ // Page should NOT have 'use client' — FAQ is isolated in FaqSection component
127
+ expect(result.code).not.toContain("'use client'");
125
128
  });
126
129
 
127
130
  it('renders pricing teaser from context pricing', () => {
@@ -15,6 +15,7 @@ import {
15
15
  generateFaqSection,
16
16
  buildFaqItemsDeclaration,
17
17
  generateFaqItemComponent,
18
+ generateFaqSectionComponent,
18
19
  generatePricingTeaserSection,
19
20
  } from '../../src/generators/templates/website-sections.js';
20
21
  import type { WebsiteStrategyDocument } from '../../src/types/website-strategy.js';
@@ -188,9 +189,9 @@ describe('generateSocialProofSection', () => {
188
189
  describe('generateFaqSection', () => {
189
190
  it('renders FAQ from objections', () => {
190
191
  const { jsx, info, needsClientDirective } = generateFaqSection(makeStrategy());
191
- // FAQ section JSX references faqItems via runtime mapping
192
- expect(jsx).toContain('FaqItem');
193
- expect(jsx).toContain('faqItems.map');
192
+ // FAQ section uses FaqSection component (separate client component)
193
+ expect(jsx).toContain('FaqSection');
194
+ expect(jsx).toContain('faqItems');
194
195
  expect(info.dataSource).toBe('strategy');
195
196
  expect(needsClientDirective).toBe(true);
196
197
  });
@@ -207,6 +208,16 @@ describe('generateFaqSection', () => {
207
208
  expect(component).toContain('onClick');
208
209
  });
209
210
 
211
+ it('generates standalone FaqSection client component', () => {
212
+ const component = generateFaqSectionComponent();
213
+ expect(component).toContain("'use client'");
214
+ expect(component).toContain('useState');
215
+ expect(component).toContain('FaqSection');
216
+ expect(component).toContain('FaqItem');
217
+ expect(component).toContain('aria-expanded');
218
+ expect(component).toContain('ChevronDown');
219
+ });
220
+
210
221
  it('skips when no objections', () => {
211
222
  const strategy = makeStrategy({
212
223
  icp: { primaryPersona: 'devs', painPoints: [], goals: [], objections: [] },