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.
- package/dist/generators/all.d.ts +4 -0
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +7 -22
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/templates/database-docker.d.ts +2 -1
- package/dist/generators/templates/database-docker.d.ts.map +1 -1
- package/dist/generators/templates/database-docker.js +3 -17
- package/dist/generators/templates/database-docker.js.map +1 -1
- package/dist/generators/templates/website-components.d.ts.map +1 -1
- package/dist/generators/templates/website-components.js +3 -1
- package/dist/generators/templates/website-components.js.map +1 -1
- package/dist/generators/templates/website-landing.d.ts.map +1 -1
- package/dist/generators/templates/website-landing.js +4 -8
- package/dist/generators/templates/website-landing.js.map +1 -1
- package/dist/generators/templates/website-pricing.d.ts.map +1 -1
- package/dist/generators/templates/website-pricing.js +3 -27
- package/dist/generators/templates/website-pricing.js.map +1 -1
- package/dist/generators/templates/website-sections.d.ts +5 -0
- package/dist/generators/templates/website-sections.d.ts.map +1 -1
- package/dist/generators/templates/website-sections.js +43 -5
- package/dist/generators/templates/website-sections.js.map +1 -1
- package/dist/generators/templates/website-seo.d.ts.map +1 -1
- package/dist/generators/templates/website-seo.js +1 -0
- package/dist/generators/templates/website-seo.js.map +1 -1
- package/dist/generators/templates/website.d.ts +1 -1
- package/dist/generators/templates/website.d.ts.map +1 -1
- package/dist/generators/templates/website.js +1 -1
- package/dist/generators/templates/website.js.map +1 -1
- package/dist/generators/website.d.ts.map +1 -1
- package/dist/generators/website.js +5 -0
- package/dist/generators/website.js.map +1 -1
- package/dist/workflow/audit-analyzer.d.ts.map +1 -1
- package/dist/workflow/audit-analyzer.js +15 -3
- package/dist/workflow/audit-analyzer.js.map +1 -1
- package/dist/workflow/tester.d.ts.map +1 -1
- package/dist/workflow/tester.js +8 -0
- package/dist/workflow/tester.js.map +1 -1
- package/package.json +1 -1
- package/src/generators/all.ts +7 -22
- package/src/generators/templates/database-docker.ts +3 -17
- package/src/generators/templates/website-components.ts +3 -1
- package/src/generators/templates/website-landing.ts +3 -8
- package/src/generators/templates/website-pricing.ts +3 -27
- package/src/generators/templates/website-sections.ts +44 -5
- package/src/generators/templates/website-seo.ts +1 -0
- package/src/generators/templates/website.ts +1 -0
- package/src/generators/website.ts +5 -0
- package/src/workflow/audit-analyzer.ts +15 -3
- package/src/workflow/tester.ts +8 -0
- package/tests/generators/database.test.ts +3 -2
- package/tests/generators/fe-be-db-integration.test.ts +4 -3
- package/tests/generators/website-landing.test.ts +4 -1
- 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
|
-
|
|
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
|
-
-
|
|
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
|
package/src/workflow/tester.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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
|
-
|
|
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
|
|
192
|
-
expect(jsx).toContain('
|
|
193
|
-
expect(jsx).toContain('faqItems
|
|
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: [] },
|