@empline/preflight 1.1.11 ā 1.1.13
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/checks/consolidated/auth-storage-state.d.ts +3 -0
- package/dist/checks/consolidated/auth-storage-state.d.ts.map +1 -0
- package/dist/checks/consolidated/auth-storage-state.js +146 -0
- package/dist/checks/consolidated/auth-storage-state.js.map +1 -0
- package/dist/checks/consolidated/business.d.ts +50 -0
- package/dist/checks/consolidated/business.d.ts.map +1 -0
- package/dist/checks/consolidated/business.js +252 -0
- package/dist/checks/consolidated/business.js.map +1 -0
- package/dist/checks/consolidated/caching-strategy.d.ts +104 -0
- package/dist/checks/consolidated/caching-strategy.d.ts.map +1 -0
- package/dist/checks/consolidated/caching-strategy.js +725 -0
- package/dist/checks/consolidated/caching-strategy.js.map +1 -0
- package/dist/checks/consolidated/code-quality.d.ts +83 -0
- package/dist/checks/consolidated/code-quality.d.ts.map +1 -0
- package/dist/checks/consolidated/code-quality.js +445 -0
- package/dist/checks/consolidated/code-quality.js.map +1 -0
- package/dist/checks/consolidated/console-statements.d.ts +32 -0
- package/dist/checks/consolidated/console-statements.d.ts.map +1 -0
- package/dist/checks/consolidated/console-statements.js +304 -0
- package/dist/checks/consolidated/console-statements.js.map +1 -0
- package/dist/checks/consolidated/css-advanced-validation.d.ts +24 -0
- package/dist/checks/consolidated/css-advanced-validation.d.ts.map +1 -0
- package/dist/checks/consolidated/css-advanced-validation.js +415 -0
- package/dist/checks/consolidated/css-advanced-validation.js.map +1 -0
- package/dist/checks/consolidated/css-organization.d.ts +14 -0
- package/dist/checks/consolidated/css-organization.d.ts.map +1 -0
- package/dist/checks/consolidated/css-organization.js +432 -0
- package/dist/checks/consolidated/css-organization.js.map +1 -0
- package/dist/checks/consolidated/css-runtime-validation.d.ts +22 -0
- package/dist/checks/consolidated/css-runtime-validation.d.ts.map +1 -0
- package/dist/checks/consolidated/css-runtime-validation.js +330 -0
- package/dist/checks/consolidated/css-runtime-validation.js.map +1 -0
- package/dist/checks/consolidated/css-variable-validation.d.ts +17 -0
- package/dist/checks/consolidated/css-variable-validation.d.ts.map +1 -0
- package/dist/checks/consolidated/css-variable-validation.js +412 -0
- package/dist/checks/consolidated/css-variable-validation.js.map +1 -0
- package/dist/checks/consolidated/dark-mode-consistency.d.ts +23 -0
- package/dist/checks/consolidated/dark-mode-consistency.d.ts.map +1 -0
- package/dist/checks/consolidated/dark-mode-consistency.js +291 -0
- package/dist/checks/consolidated/dark-mode-consistency.js.map +1 -0
- package/dist/checks/consolidated/database.d.ts +95 -0
- package/dist/checks/consolidated/database.d.ts.map +1 -0
- package/dist/checks/consolidated/database.js +427 -0
- package/dist/checks/consolidated/database.js.map +1 -0
- package/dist/checks/consolidated/e2e-checks.d.ts +52 -0
- package/dist/checks/consolidated/e2e-checks.d.ts.map +1 -0
- package/dist/checks/consolidated/e2e-checks.js +157 -0
- package/dist/checks/consolidated/e2e-checks.js.map +1 -0
- package/dist/checks/consolidated/e2e-regression-coverage.d.ts +14 -0
- package/dist/checks/consolidated/e2e-regression-coverage.d.ts.map +1 -0
- package/dist/checks/consolidated/e2e-regression-coverage.js +151 -0
- package/dist/checks/consolidated/e2e-regression-coverage.js.map +1 -0
- package/dist/checks/consolidated/e2e-validation.d.ts +137 -0
- package/dist/checks/consolidated/e2e-validation.d.ts.map +1 -0
- package/dist/checks/consolidated/e2e-validation.js +1001 -0
- package/dist/checks/consolidated/e2e-validation.js.map +1 -0
- package/dist/checks/consolidated/enterprise-baseline.d.ts +9 -0
- package/dist/checks/consolidated/enterprise-baseline.d.ts.map +1 -0
- package/dist/checks/consolidated/enterprise-baseline.js +277 -0
- package/dist/checks/consolidated/enterprise-baseline.js.map +1 -0
- package/dist/checks/consolidated/generate-pageload-config.d.ts +6 -0
- package/dist/checks/consolidated/generate-pageload-config.d.ts.map +1 -0
- package/dist/checks/consolidated/generate-pageload-config.js +161 -0
- package/dist/checks/consolidated/generate-pageload-config.js.map +1 -0
- package/dist/checks/consolidated/hardened-checks.d.ts +276 -0
- package/dist/checks/consolidated/hardened-checks.d.ts.map +1 -0
- package/dist/checks/consolidated/hardened-checks.js +3056 -0
- package/dist/checks/consolidated/hardened-checks.js.map +1 -0
- package/dist/checks/consolidated/homepage-ux.d.ts +12 -0
- package/dist/checks/consolidated/homepage-ux.d.ts.map +1 -0
- package/dist/checks/consolidated/homepage-ux.js +242 -0
- package/dist/checks/consolidated/homepage-ux.js.map +1 -0
- package/dist/checks/consolidated/images.d.ts +76 -0
- package/dist/checks/consolidated/images.d.ts.map +1 -0
- package/dist/checks/consolidated/images.js +311 -0
- package/dist/checks/consolidated/images.js.map +1 -0
- package/dist/checks/consolidated/import-cycles.d.ts +63 -0
- package/dist/checks/consolidated/import-cycles.d.ts.map +1 -0
- package/dist/checks/consolidated/import-cycles.js +291 -0
- package/dist/checks/consolidated/import-cycles.js.map +1 -0
- package/dist/checks/consolidated/imports.d.ts +112 -0
- package/dist/checks/consolidated/imports.d.ts.map +1 -0
- package/dist/checks/consolidated/imports.js +977 -0
- package/dist/checks/consolidated/imports.js.map +1 -0
- package/dist/checks/consolidated/inline-style-conflicts.d.ts +21 -0
- package/dist/checks/consolidated/inline-style-conflicts.d.ts.map +1 -0
- package/dist/checks/consolidated/inline-style-conflicts.js +300 -0
- package/dist/checks/consolidated/inline-style-conflicts.js.map +1 -0
- package/dist/checks/consolidated/lib-organization.d.ts +12 -0
- package/dist/checks/consolidated/lib-organization.d.ts.map +1 -0
- package/dist/checks/consolidated/lib-organization.js +419 -0
- package/dist/checks/consolidated/lib-organization.js.map +1 -0
- package/dist/checks/consolidated/n-plus-one.d.ts +63 -0
- package/dist/checks/consolidated/n-plus-one.d.ts.map +1 -0
- package/dist/checks/consolidated/n-plus-one.js +331 -0
- package/dist/checks/consolidated/n-plus-one.js.map +1 -0
- package/dist/checks/consolidated/nextjs.d.ts +51 -0
- package/dist/checks/consolidated/nextjs.d.ts.map +1 -0
- package/dist/checks/consolidated/nextjs.js +205 -0
- package/dist/checks/consolidated/nextjs.js.map +1 -0
- package/dist/checks/consolidated/organization.d.ts +54 -0
- package/dist/checks/consolidated/organization.d.ts.map +1 -0
- package/dist/checks/consolidated/organization.js +158 -0
- package/dist/checks/consolidated/organization.js.map +1 -0
- package/dist/checks/consolidated/pageload.d.ts +12 -0
- package/dist/checks/consolidated/pageload.d.ts.map +1 -0
- package/dist/checks/consolidated/pageload.js +138 -0
- package/dist/checks/consolidated/pageload.js.map +1 -0
- package/dist/checks/consolidated/performance.d.ts +112 -0
- package/dist/checks/consolidated/performance.d.ts.map +1 -0
- package/dist/checks/consolidated/performance.js +1546 -0
- package/dist/checks/consolidated/performance.js.map +1 -0
- package/dist/checks/consolidated/quality.d.ts +52 -0
- package/dist/checks/consolidated/quality.d.ts.map +1 -0
- package/dist/checks/consolidated/quality.js +253 -0
- package/dist/checks/consolidated/quality.js.map +1 -0
- package/dist/checks/consolidated/react.d.ts +48 -0
- package/dist/checks/consolidated/react.d.ts.map +1 -0
- package/dist/checks/consolidated/react.js +203 -0
- package/dist/checks/consolidated/react.js.map +1 -0
- package/dist/checks/consolidated/regression-hygiene.d.ts +17 -0
- package/dist/checks/consolidated/regression-hygiene.d.ts.map +1 -0
- package/dist/checks/consolidated/regression-hygiene.js +242 -0
- package/dist/checks/consolidated/regression-hygiene.js.map +1 -0
- package/dist/checks/consolidated/regression.d.ts +20 -0
- package/dist/checks/consolidated/regression.d.ts.map +1 -0
- package/dist/checks/consolidated/regression.js +121 -0
- package/dist/checks/consolidated/regression.js.map +1 -0
- package/dist/checks/consolidated/runtime.d.ts +53 -0
- package/dist/checks/consolidated/runtime.d.ts.map +1 -0
- package/dist/checks/consolidated/runtime.js +160 -0
- package/dist/checks/consolidated/runtime.js.map +1 -0
- package/dist/checks/consolidated/script-performance.d.ts +17 -0
- package/dist/checks/consolidated/script-performance.d.ts.map +1 -0
- package/dist/checks/consolidated/script-performance.js +137 -0
- package/dist/checks/consolidated/script-performance.js.map +1 -0
- package/dist/checks/consolidated/security.d.ts +78 -0
- package/dist/checks/consolidated/security.d.ts.map +1 -0
- package/dist/checks/consolidated/security.js +404 -0
- package/dist/checks/consolidated/security.js.map +1 -0
- package/dist/checks/consolidated/seo.d.ts +31 -0
- package/dist/checks/consolidated/seo.d.ts.map +1 -0
- package/dist/checks/consolidated/seo.js +1438 -0
- package/dist/checks/consolidated/seo.js.map +1 -0
- package/dist/checks/consolidated/sx-prop-deprecation.d.ts +22 -0
- package/dist/checks/consolidated/sx-prop-deprecation.d.ts.map +1 -0
- package/dist/checks/consolidated/sx-prop-deprecation.js +280 -0
- package/dist/checks/consolidated/sx-prop-deprecation.js.map +1 -0
- package/dist/checks/consolidated/tailwind-class-validation.d.ts +25 -0
- package/dist/checks/consolidated/tailwind-class-validation.d.ts.map +1 -0
- package/dist/checks/consolidated/tailwind-class-validation.js +533 -0
- package/dist/checks/consolidated/tailwind-class-validation.js.map +1 -0
- package/dist/checks/consolidated/testing.d.ts +54 -0
- package/dist/checks/consolidated/testing.d.ts.map +1 -0
- package/dist/checks/consolidated/testing.js +163 -0
- package/dist/checks/consolidated/testing.js.map +1 -0
- package/dist/checks/consolidated/typescript.d.ts +3 -0
- package/dist/checks/consolidated/typescript.d.ts.map +1 -0
- package/dist/checks/consolidated/typescript.js +31 -0
- package/dist/checks/consolidated/typescript.js.map +1 -0
- package/dist/checks/consolidated/ui-accessibility-advanced.d.ts +104 -0
- package/dist/checks/consolidated/ui-accessibility-advanced.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-accessibility-advanced.js +689 -0
- package/dist/checks/consolidated/ui-accessibility-advanced.js.map +1 -0
- package/dist/checks/consolidated/ui-accessibility.d.ts +121 -0
- package/dist/checks/consolidated/ui-accessibility.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-accessibility.js +776 -0
- package/dist/checks/consolidated/ui-accessibility.js.map +1 -0
- package/dist/checks/consolidated/ui-advanced-spacing.d.ts +142 -0
- package/dist/checks/consolidated/ui-advanced-spacing.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-advanced-spacing.js +1220 -0
- package/dist/checks/consolidated/ui-advanced-spacing.js.map +1 -0
- package/dist/checks/consolidated/ui-animation-duration.d.ts +108 -0
- package/dist/checks/consolidated/ui-animation-duration.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-animation-duration.js +531 -0
- package/dist/checks/consolidated/ui-animation-duration.js.map +1 -0
- package/dist/checks/consolidated/ui-border-radius.d.ts +90 -0
- package/dist/checks/consolidated/ui-border-radius.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-border-radius.js +519 -0
- package/dist/checks/consolidated/ui-border-radius.js.map +1 -0
- package/dist/checks/consolidated/ui-buttons.d.ts +32 -0
- package/dist/checks/consolidated/ui-buttons.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-buttons.js +481 -0
- package/dist/checks/consolidated/ui-buttons.js.map +1 -0
- package/dist/checks/consolidated/ui-cards.d.ts +29 -0
- package/dist/checks/consolidated/ui-cards.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-cards.js +504 -0
- package/dist/checks/consolidated/ui-cards.js.map +1 -0
- package/dist/checks/consolidated/ui-checks.d.ts +48 -0
- package/dist/checks/consolidated/ui-checks.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-checks.js +264 -0
- package/dist/checks/consolidated/ui-checks.js.map +1 -0
- package/dist/checks/consolidated/ui-cleanup.d.ts +81 -0
- package/dist/checks/consolidated/ui-cleanup.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-cleanup.js +650 -0
- package/dist/checks/consolidated/ui-cleanup.js.map +1 -0
- package/dist/checks/consolidated/ui-components.d.ts +255 -0
- package/dist/checks/consolidated/ui-components.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-components.js +2008 -0
- package/dist/checks/consolidated/ui-components.js.map +1 -0
- package/dist/checks/consolidated/ui-consistency-advanced.d.ts +130 -0
- package/dist/checks/consolidated/ui-consistency-advanced.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-consistency-advanced.js +982 -0
- package/dist/checks/consolidated/ui-consistency-advanced.js.map +1 -0
- package/dist/checks/consolidated/ui-consistency-comprehensive.d.ts +30 -0
- package/dist/checks/consolidated/ui-consistency-comprehensive.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-consistency-comprehensive.js +1018 -0
- package/dist/checks/consolidated/ui-consistency-comprehensive.js.map +1 -0
- package/dist/checks/consolidated/ui-consistency-extended.d.ts +26 -0
- package/dist/checks/consolidated/ui-consistency-extended.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-consistency-extended.js +606 -0
- package/dist/checks/consolidated/ui-consistency-extended.js.map +1 -0
- package/dist/checks/consolidated/ui-data-display.d.ts +103 -0
- package/dist/checks/consolidated/ui-data-display.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-data-display.js +740 -0
- package/dist/checks/consolidated/ui-data-display.js.map +1 -0
- package/dist/checks/consolidated/ui-deprecated.d.ts +22 -0
- package/dist/checks/consolidated/ui-deprecated.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-deprecated.js +336 -0
- package/dist/checks/consolidated/ui-deprecated.js.map +1 -0
- package/dist/checks/consolidated/ui-empty-null-states.d.ts +90 -0
- package/dist/checks/consolidated/ui-empty-null-states.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-empty-null-states.js +511 -0
- package/dist/checks/consolidated/ui-empty-null-states.js.map +1 -0
- package/dist/checks/consolidated/ui-error-states.d.ts +99 -0
- package/dist/checks/consolidated/ui-error-states.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-error-states.js +694 -0
- package/dist/checks/consolidated/ui-error-states.js.map +1 -0
- package/dist/checks/consolidated/ui-feedback-confirmations.d.ts +90 -0
- package/dist/checks/consolidated/ui-feedback-confirmations.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-feedback-confirmations.js +596 -0
- package/dist/checks/consolidated/ui-feedback-confirmations.js.map +1 -0
- package/dist/checks/consolidated/ui-forms.d.ts +32 -0
- package/dist/checks/consolidated/ui-forms.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-forms.js +568 -0
- package/dist/checks/consolidated/ui-forms.js.map +1 -0
- package/dist/checks/consolidated/ui-gradient-shadow.d.ts +90 -0
- package/dist/checks/consolidated/ui-gradient-shadow.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-gradient-shadow.js +568 -0
- package/dist/checks/consolidated/ui-gradient-shadow.js.map +1 -0
- package/dist/checks/consolidated/ui-grid-responsive.d.ts +27 -0
- package/dist/checks/consolidated/ui-grid-responsive.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-grid-responsive.js +441 -0
- package/dist/checks/consolidated/ui-grid-responsive.js.map +1 -0
- package/dist/checks/consolidated/ui-icon-size-tokens.d.ts +104 -0
- package/dist/checks/consolidated/ui-icon-size-tokens.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-icon-size-tokens.js +514 -0
- package/dist/checks/consolidated/ui-icon-size-tokens.js.map +1 -0
- package/dist/checks/consolidated/ui-iconography.d.ts +90 -0
- package/dist/checks/consolidated/ui-iconography.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-iconography.js +565 -0
- package/dist/checks/consolidated/ui-iconography.js.map +1 -0
- package/dist/checks/consolidated/ui-interactive-states.d.ts +240 -0
- package/dist/checks/consolidated/ui-interactive-states.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-interactive-states.js +2474 -0
- package/dist/checks/consolidated/ui-interactive-states.js.map +1 -0
- package/dist/checks/consolidated/ui-layout.d.ts +256 -0
- package/dist/checks/consolidated/ui-layout.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-layout.js +1371 -0
- package/dist/checks/consolidated/ui-layout.js.map +1 -0
- package/dist/checks/consolidated/ui-loading-skeletons.d.ts +11 -0
- package/dist/checks/consolidated/ui-loading-skeletons.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-loading-skeletons.js +145 -0
- package/dist/checks/consolidated/ui-loading-skeletons.js.map +1 -0
- package/dist/checks/consolidated/ui-loading-state-skeletons.d.ts +9 -0
- package/dist/checks/consolidated/ui-loading-state-skeletons.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-loading-state-skeletons.js +125 -0
- package/dist/checks/consolidated/ui-loading-state-skeletons.js.map +1 -0
- package/dist/checks/consolidated/ui-media.d.ts +74 -0
- package/dist/checks/consolidated/ui-media.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-media.js +408 -0
- package/dist/checks/consolidated/ui-media.js.map +1 -0
- package/dist/checks/consolidated/ui-micro-interactions.d.ts +107 -0
- package/dist/checks/consolidated/ui-micro-interactions.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-micro-interactions.js +825 -0
- package/dist/checks/consolidated/ui-micro-interactions.js.map +1 -0
- package/dist/checks/consolidated/ui-microcopy-consistency.d.ts +114 -0
- package/dist/checks/consolidated/ui-microcopy-consistency.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-microcopy-consistency.js +566 -0
- package/dist/checks/consolidated/ui-microcopy-consistency.js.map +1 -0
- package/dist/checks/consolidated/ui-mobile-ux.d.ts +251 -0
- package/dist/checks/consolidated/ui-mobile-ux.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-mobile-ux.js +2212 -0
- package/dist/checks/consolidated/ui-mobile-ux.js.map +1 -0
- package/dist/checks/consolidated/ui-motion-accessibility.d.ts +93 -0
- package/dist/checks/consolidated/ui-motion-accessibility.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-motion-accessibility.js +450 -0
- package/dist/checks/consolidated/ui-motion-accessibility.js.map +1 -0
- package/dist/checks/consolidated/ui-navigation.d.ts +85 -0
- package/dist/checks/consolidated/ui-navigation.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-navigation.js +673 -0
- package/dist/checks/consolidated/ui-navigation.js.map +1 -0
- package/dist/checks/consolidated/ui-patterns.d.ts +174 -0
- package/dist/checks/consolidated/ui-patterns.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-patterns.js +1532 -0
- package/dist/checks/consolidated/ui-patterns.js.map +1 -0
- package/dist/checks/consolidated/ui-responsive.d.ts +89 -0
- package/dist/checks/consolidated/ui-responsive.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-responsive.js +588 -0
- package/dist/checks/consolidated/ui-responsive.js.map +1 -0
- package/dist/checks/consolidated/ui-spacing-standards.d.ts +43 -0
- package/dist/checks/consolidated/ui-spacing-standards.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-spacing-standards.js +874 -0
- package/dist/checks/consolidated/ui-spacing-standards.js.map +1 -0
- package/dist/checks/consolidated/ui-spacing.d.ts +751 -0
- package/dist/checks/consolidated/ui-spacing.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-spacing.js +4996 -0
- package/dist/checks/consolidated/ui-spacing.js.map +1 -0
- package/dist/checks/consolidated/ui-standards-auto-fixer.d.ts +70 -0
- package/dist/checks/consolidated/ui-standards-auto-fixer.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-standards-auto-fixer.js +429 -0
- package/dist/checks/consolidated/ui-standards-auto-fixer.js.map +1 -0
- package/dist/checks/consolidated/ui-standards-enforcement.d.ts +100 -0
- package/dist/checks/consolidated/ui-standards-enforcement.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-standards-enforcement.js +935 -0
- package/dist/checks/consolidated/ui-standards-enforcement.js.map +1 -0
- package/dist/checks/consolidated/ui-state-consistency.d.ts +90 -0
- package/dist/checks/consolidated/ui-state-consistency.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-state-consistency.js +659 -0
- package/dist/checks/consolidated/ui-state-consistency.js.map +1 -0
- package/dist/checks/consolidated/ui-style-validation.d.ts +74 -0
- package/dist/checks/consolidated/ui-style-validation.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-style-validation.js +403 -0
- package/dist/checks/consolidated/ui-style-validation.js.map +1 -0
- package/dist/checks/consolidated/ui-tokens.d.ts +110 -0
- package/dist/checks/consolidated/ui-tokens.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-tokens.js +990 -0
- package/dist/checks/consolidated/ui-tokens.js.map +1 -0
- package/dist/checks/consolidated/ui-typography.d.ts +77 -0
- package/dist/checks/consolidated/ui-typography.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-typography.js +416 -0
- package/dist/checks/consolidated/ui-typography.js.map +1 -0
- package/dist/checks/consolidated/ui-visual-hierarchy.d.ts +90 -0
- package/dist/checks/consolidated/ui-visual-hierarchy.d.ts.map +1 -0
- package/dist/checks/consolidated/ui-visual-hierarchy.js +562 -0
- package/dist/checks/consolidated/ui-visual-hierarchy.js.map +1 -0
- package/dist/checks/consolidated/woocommerce.d.ts +50 -0
- package/dist/checks/consolidated/woocommerce.d.ts.map +1 -0
- package/dist/checks/consolidated/woocommerce.js +198 -0
- package/dist/checks/consolidated/woocommerce.js.map +1 -0
- package/dist/checks/core/api-route-protection.d.ts +2 -0
- package/dist/checks/core/api-route-protection.d.ts.map +1 -0
- package/dist/checks/core/api-route-protection.js +101 -0
- package/dist/checks/core/api-route-protection.js.map +1 -0
- package/dist/checks/core/critical.d.ts +8 -0
- package/dist/checks/core/critical.d.ts.map +1 -0
- package/dist/checks/core/critical.js +200 -0
- package/dist/checks/core/critical.js.map +1 -0
- package/dist/checks/core/database.d.ts +8 -0
- package/dist/checks/core/database.d.ts.map +1 -0
- package/dist/checks/core/database.js +699 -0
- package/dist/checks/core/database.js.map +1 -0
- package/dist/checks/core/development.d.ts +8 -0
- package/dist/checks/core/development.d.ts.map +1 -0
- package/dist/checks/core/development.js +417 -0
- package/dist/checks/core/development.js.map +1 -0
- package/dist/checks/core/hydration-mismatch-check.d.ts +38 -0
- package/dist/checks/core/hydration-mismatch-check.d.ts.map +1 -0
- package/dist/checks/core/hydration-mismatch-check.js +411 -0
- package/dist/checks/core/hydration-mismatch-check.js.map +1 -0
- package/dist/checks/core/performance.d.ts +8 -0
- package/dist/checks/core/performance.d.ts.map +1 -0
- package/dist/checks/core/performance.js +474 -0
- package/dist/checks/core/performance.js.map +1 -0
- package/dist/checks/core/security.d.ts +8 -0
- package/dist/checks/core/security.d.ts.map +1 -0
- package/dist/checks/core/security.js +275 -0
- package/dist/checks/core/security.js.map +1 -0
- package/dist/checks/core/standardized-error-handling.d.ts +43 -0
- package/dist/checks/core/standardized-error-handling.d.ts.map +1 -0
- package/dist/checks/core/standardized-error-handling.js +384 -0
- package/dist/checks/core/standardized-error-handling.js.map +1 -0
- package/dist/checks/core/supercatch.d.ts +8 -0
- package/dist/checks/core/supercatch.d.ts.map +1 -0
- package/dist/checks/core/supercatch.js +750 -0
- package/dist/checks/core/supercatch.js.map +1 -0
- package/dist/checks/core/suppression-check.d.ts +2 -0
- package/dist/checks/core/suppression-check.d.ts.map +1 -0
- package/dist/checks/core/suppression-check.js +129 -0
- package/dist/checks/core/suppression-check.js.map +1 -0
- package/dist/checks/core/ui-quality.d.ts +8 -0
- package/dist/checks/core/ui-quality.d.ts.map +1 -0
- package/dist/checks/core/ui-quality.js +1736 -0
- package/dist/checks/core/ui-quality.js.map +1 -0
- package/dist/checks/core/unused-assets-check.d.ts +2 -0
- package/dist/checks/core/unused-assets-check.d.ts.map +1 -0
- package/dist/checks/core/unused-assets-check.js +112 -0
- package/dist/checks/core/unused-assets-check.js.map +1 -0
- package/dist/checks/core/use-status-ssr-safety.d.ts +34 -0
- package/dist/checks/core/use-status-ssr-safety.d.ts.map +1 -0
- package/dist/checks/core/use-status-ssr-safety.js +283 -0
- package/dist/checks/core/use-status-ssr-safety.js.map +1 -0
- package/dist/checks/email/email-flow-validation.d.ts +23 -0
- package/dist/checks/email/email-flow-validation.d.ts.map +1 -0
- package/dist/checks/email/email-flow-validation.js +468 -0
- package/dist/checks/email/email-flow-validation.js.map +1 -0
- package/dist/checks/email/email-template-db-verification.d.ts +20 -0
- package/dist/checks/email/email-template-db-verification.d.ts.map +1 -0
- package/dist/checks/email/email-template-db-verification.js +46 -0
- package/dist/checks/email/email-template-db-verification.js.map +1 -0
- package/dist/checks/email/email-template-validation.d.ts +24 -0
- package/dist/checks/email/email-template-validation.d.ts.map +1 -0
- package/dist/checks/email/email-template-validation.js +688 -0
- package/dist/checks/email/email-template-validation.js.map +1 -0
- package/dist/checks/jsx/comment-placement.d.ts +45 -0
- package/dist/checks/jsx/comment-placement.d.ts.map +1 -0
- package/dist/checks/jsx/comment-placement.js +316 -0
- package/dist/checks/jsx/comment-placement.js.map +1 -0
- package/dist/checks/specialized/admin-layout-check.d.ts +19 -0
- package/dist/checks/specialized/admin-layout-check.d.ts.map +1 -0
- package/dist/checks/specialized/admin-layout-check.js +166 -0
- package/dist/checks/specialized/admin-layout-check.js.map +1 -0
- package/dist/checks/specialized/client-server-separation.d.ts +14 -0
- package/dist/checks/specialized/client-server-separation.d.ts.map +1 -0
- package/dist/checks/specialized/client-server-separation.js +197 -0
- package/dist/checks/specialized/client-server-separation.js.map +1 -0
- package/dist/checks/specialized/cost-optimization.d.ts +18 -0
- package/dist/checks/specialized/cost-optimization.d.ts.map +1 -0
- package/dist/checks/specialized/cost-optimization.js +78 -0
- package/dist/checks/specialized/cost-optimization.js.map +1 -0
- package/dist/checks/specialized/database-migration-sync.d.ts +21 -0
- package/dist/checks/specialized/database-migration-sync.d.ts.map +1 -0
- package/dist/checks/specialized/database-migration-sync.js +150 -0
- package/dist/checks/specialized/database-migration-sync.js.map +1 -0
- package/dist/checks/specialized/database-model-validation.d.ts +15 -0
- package/dist/checks/specialized/database-model-validation.d.ts.map +1 -0
- package/dist/checks/specialized/database-model-validation.js +35 -0
- package/dist/checks/specialized/database-model-validation.js.map +1 -0
- package/dist/checks/specialized/database-schema-migrations-diff.d.ts +27 -0
- package/dist/checks/specialized/database-schema-migrations-diff.d.ts.map +1 -0
- package/dist/checks/specialized/database-schema-migrations-diff.js +177 -0
- package/dist/checks/specialized/database-schema-migrations-diff.js.map +1 -0
- package/dist/checks/specialized/database-schema-sync.d.ts +23 -0
- package/dist/checks/specialized/database-schema-sync.d.ts.map +1 -0
- package/dist/checks/specialized/database-schema-sync.js +77 -0
- package/dist/checks/specialized/database-schema-sync.js.map +1 -0
- package/dist/checks/specialized/decimal-serialization.d.ts +24 -0
- package/dist/checks/specialized/decimal-serialization.d.ts.map +1 -0
- package/dist/checks/specialized/decimal-serialization.js +400 -0
- package/dist/checks/specialized/decimal-serialization.js.map +1 -0
- package/dist/checks/specialized/detect-router-issues.d.ts +14 -0
- package/dist/checks/specialized/detect-router-issues.d.ts.map +1 -0
- package/dist/checks/specialized/detect-router-issues.js +96 -0
- package/dist/checks/specialized/detect-router-issues.js.map +1 -0
- package/dist/checks/specialized/enum-validation.d.ts +15 -0
- package/dist/checks/specialized/enum-validation.d.ts.map +1 -0
- package/dist/checks/specialized/enum-validation.js +35 -0
- package/dist/checks/specialized/enum-validation.js.map +1 -0
- package/dist/checks/specialized/hash-collision.d.ts +18 -0
- package/dist/checks/specialized/hash-collision.d.ts.map +1 -0
- package/dist/checks/specialized/hash-collision.js +78 -0
- package/dist/checks/specialized/hash-collision.js.map +1 -0
- package/dist/checks/specialized/id-generation-enforcement.d.ts +16 -0
- package/dist/checks/specialized/id-generation-enforcement.d.ts.map +1 -0
- package/dist/checks/specialized/id-generation-enforcement.js +307 -0
- package/dist/checks/specialized/id-generation-enforcement.js.map +1 -0
- package/dist/checks/specialized/image-data-integrity.d.ts +15 -0
- package/dist/checks/specialized/image-data-integrity.d.ts.map +1 -0
- package/dist/checks/specialized/image-data-integrity.js +79 -0
- package/dist/checks/specialized/image-data-integrity.js.map +1 -0
- package/dist/checks/specialized/image-health.d.ts +14 -0
- package/dist/checks/specialized/image-health.d.ts.map +1 -0
- package/dist/checks/specialized/image-health.js +122 -0
- package/dist/checks/specialized/image-health.js.map +1 -0
- package/dist/checks/specialized/image-metadata-validation.d.ts +14 -0
- package/dist/checks/specialized/image-metadata-validation.d.ts.map +1 -0
- package/dist/checks/specialized/image-metadata-validation.js +95 -0
- package/dist/checks/specialized/image-metadata-validation.js.map +1 -0
- package/dist/checks/specialized/image-optimization.d.ts +16 -0
- package/dist/checks/specialized/image-optimization.d.ts.map +1 -0
- package/dist/checks/specialized/image-optimization.js +86 -0
- package/dist/checks/specialized/image-optimization.js.map +1 -0
- package/dist/checks/specialized/invalid-module-imports.d.ts +24 -0
- package/dist/checks/specialized/invalid-module-imports.d.ts.map +1 -0
- package/dist/checks/specialized/invalid-module-imports.js +209 -0
- package/dist/checks/specialized/invalid-module-imports.js.map +1 -0
- package/dist/checks/specialized/lint-validation.d.ts +26 -0
- package/dist/checks/specialized/lint-validation.d.ts.map +1 -0
- package/dist/checks/specialized/lint-validation.js +193 -0
- package/dist/checks/specialized/lint-validation.js.map +1 -0
- package/dist/checks/specialized/listing-workflow.d.ts +19 -0
- package/dist/checks/specialized/listing-workflow.d.ts.map +1 -0
- package/dist/checks/specialized/listing-workflow.js +89 -0
- package/dist/checks/specialized/listing-workflow.js.map +1 -0
- package/dist/checks/specialized/mui-imports-validation.d.ts +18 -0
- package/dist/checks/specialized/mui-imports-validation.d.ts.map +1 -0
- package/dist/checks/specialized/mui-imports-validation.js +134 -0
- package/dist/checks/specialized/mui-imports-validation.js.map +1 -0
- package/dist/checks/specialized/nextauth-v5-compliance.d.ts +16 -0
- package/dist/checks/specialized/nextauth-v5-compliance.d.ts.map +1 -0
- package/dist/checks/specialized/nextauth-v5-compliance.js +164 -0
- package/dist/checks/specialized/nextauth-v5-compliance.js.map +1 -0
- package/dist/checks/specialized/nextjs-params-check.d.ts +14 -0
- package/dist/checks/specialized/nextjs-params-check.d.ts.map +1 -0
- package/dist/checks/specialized/nextjs-params-check.js +140 -0
- package/dist/checks/specialized/nextjs-params-check.js.map +1 -0
- package/dist/checks/specialized/no-legacy-catalog-aliases-validation.d.ts +16 -0
- package/dist/checks/specialized/no-legacy-catalog-aliases-validation.d.ts.map +1 -0
- package/dist/checks/specialized/no-legacy-catalog-aliases-validation.js +36 -0
- package/dist/checks/specialized/no-legacy-catalog-aliases-validation.js.map +1 -0
- package/dist/checks/specialized/no-wata-cardgraded-validation.d.ts +22 -0
- package/dist/checks/specialized/no-wata-cardgraded-validation.d.ts.map +1 -0
- package/dist/checks/specialized/no-wata-cardgraded-validation.js +97 -0
- package/dist/checks/specialized/no-wata-cardgraded-validation.js.map +1 -0
- package/dist/checks/specialized/parameter-consistency-check.d.ts +20 -0
- package/dist/checks/specialized/parameter-consistency-check.d.ts.map +1 -0
- package/dist/checks/specialized/parameter-consistency-check.js +115 -0
- package/dist/checks/specialized/parameter-consistency-check.js.map +1 -0
- package/dist/checks/specialized/prisma-field-names-validation.d.ts +15 -0
- package/dist/checks/specialized/prisma-field-names-validation.d.ts.map +1 -0
- package/dist/checks/specialized/prisma-field-names-validation.js +35 -0
- package/dist/checks/specialized/prisma-field-names-validation.js.map +1 -0
- package/dist/checks/specialized/prisma-null-syntax.d.ts +34 -0
- package/dist/checks/specialized/prisma-null-syntax.d.ts.map +1 -0
- package/dist/checks/specialized/prisma-null-syntax.js +330 -0
- package/dist/checks/specialized/prisma-null-syntax.js.map +1 -0
- package/dist/checks/specialized/prisma-query-validation.d.ts +15 -0
- package/dist/checks/specialized/prisma-query-validation.d.ts.map +1 -0
- package/dist/checks/specialized/prisma-query-validation.js +35 -0
- package/dist/checks/specialized/prisma-query-validation.js.map +1 -0
- package/dist/checks/specialized/product-type-validation.d.ts +17 -0
- package/dist/checks/specialized/product-type-validation.d.ts.map +1 -0
- package/dist/checks/specialized/product-type-validation.js +129 -0
- package/dist/checks/specialized/product-type-validation.js.map +1 -0
- package/dist/checks/specialized/responsive-image-validation.d.ts +14 -0
- package/dist/checks/specialized/responsive-image-validation.d.ts.map +1 -0
- package/dist/checks/specialized/responsive-image-validation.js +101 -0
- package/dist/checks/specialized/responsive-image-validation.js.map +1 -0
- package/dist/checks/specialized/root-cleanliness.d.ts +21 -0
- package/dist/checks/specialized/root-cleanliness.d.ts.map +1 -0
- package/dist/checks/specialized/root-cleanliness.js +251 -0
- package/dist/checks/specialized/root-cleanliness.js.map +1 -0
- package/dist/checks/specialized/rotation-detection-validation.d.ts +16 -0
- package/dist/checks/specialized/rotation-detection-validation.d.ts.map +1 -0
- package/dist/checks/specialized/rotation-detection-validation.js +113 -0
- package/dist/checks/specialized/rotation-detection-validation.js.map +1 -0
- package/dist/checks/specialized/script-organization.d.ts +17 -0
- package/dist/checks/specialized/script-organization.d.ts.map +1 -0
- package/dist/checks/specialized/script-organization.js +487 -0
- package/dist/checks/specialized/script-organization.js.map +1 -0
- package/dist/checks/specialized/shared-components-migration.d.ts +137 -0
- package/dist/checks/specialized/shared-components-migration.d.ts.map +1 -0
- package/dist/checks/specialized/shared-components-migration.js +1288 -0
- package/dist/checks/specialized/shared-components-migration.js.map +1 -0
- package/dist/checks/specialized/store-specialties-normalization.d.ts +10 -0
- package/dist/checks/specialized/store-specialties-normalization.d.ts.map +1 -0
- package/dist/checks/specialized/store-specialties-normalization.js +126 -0
- package/dist/checks/specialized/store-specialties-normalization.js.map +1 -0
- package/dist/checks/specialized/two-stage-trim-validation.d.ts +16 -0
- package/dist/checks/specialized/two-stage-trim-validation.d.ts.map +1 -0
- package/dist/checks/specialized/two-stage-trim-validation.js +115 -0
- package/dist/checks/specialized/two-stage-trim-validation.js.map +1 -0
- package/dist/checks/specialized/underscore-variable-audit.d.ts +26 -0
- package/dist/checks/specialized/underscore-variable-audit.d.ts.map +1 -0
- package/dist/checks/specialized/underscore-variable-audit.js +219 -0
- package/dist/checks/specialized/underscore-variable-audit.js.map +1 -0
- package/dist/checks/specialized/unified-badge-consistency.d.ts +16 -0
- package/dist/checks/specialized/unified-badge-consistency.d.ts.map +1 -0
- package/dist/checks/specialized/unified-badge-consistency.js +284 -0
- package/dist/checks/specialized/unified-badge-consistency.js.map +1 -0
- package/dist/checks/specialized/validate-integration-enums.d.ts +15 -0
- package/dist/checks/specialized/validate-integration-enums.d.ts.map +1 -0
- package/dist/checks/specialized/validate-integration-enums.js +131 -0
- package/dist/checks/specialized/validate-integration-enums.js.map +1 -0
- package/dist/checks/testing/action-regression.d.ts +23 -0
- package/dist/checks/testing/action-regression.d.ts.map +1 -0
- package/dist/checks/testing/action-regression.js +192 -0
- package/dist/checks/testing/action-regression.js.map +1 -0
- package/dist/checks/testing/critical-api-coverage.d.ts +21 -0
- package/dist/checks/testing/critical-api-coverage.d.ts.map +1 -0
- package/dist/checks/testing/critical-api-coverage.js +158 -0
- package/dist/checks/testing/critical-api-coverage.js.map +1 -0
- package/dist/checks/testing/data-entry-regression-required.d.ts +24 -0
- package/dist/checks/testing/data-entry-regression-required.d.ts.map +1 -0
- package/dist/checks/testing/data-entry-regression-required.js +378 -0
- package/dist/checks/testing/data-entry-regression-required.js.map +1 -0
- package/dist/checks/testing/e2e-best-practices.d.ts +24 -0
- package/dist/checks/testing/e2e-best-practices.d.ts.map +1 -0
- package/dist/checks/testing/e2e-best-practices.js +791 -0
- package/dist/checks/testing/e2e-best-practices.js.map +1 -0
- package/dist/checks/testing/e2e-flake-patterns.d.ts +26 -0
- package/dist/checks/testing/e2e-flake-patterns.d.ts.map +1 -0
- package/dist/checks/testing/e2e-flake-patterns.js +305 -0
- package/dist/checks/testing/e2e-flake-patterns.js.map +1 -0
- package/dist/checks/testing/e2e-redundant-visibility-checks.d.ts +25 -0
- package/dist/checks/testing/e2e-redundant-visibility-checks.d.ts.map +1 -0
- package/dist/checks/testing/e2e-redundant-visibility-checks.js +613 -0
- package/dist/checks/testing/e2e-redundant-visibility-checks.js.map +1 -0
- package/dist/checks/testing/e2e-slow-tests.d.ts +9 -0
- package/dist/checks/testing/e2e-slow-tests.d.ts.map +1 -0
- package/dist/checks/testing/e2e-slow-tests.js +142 -0
- package/dist/checks/testing/e2e-slow-tests.js.map +1 -0
- package/dist/checks/testing/e2e-timeouts.d.ts +9 -0
- package/dist/checks/testing/e2e-timeouts.d.ts.map +1 -0
- package/dist/checks/testing/e2e-timeouts.js +82 -0
- package/dist/checks/testing/e2e-timeouts.js.map +1 -0
- package/dist/checks/testing/integration-e2e-depth.d.ts +20 -0
- package/dist/checks/testing/integration-e2e-depth.d.ts.map +1 -0
- package/dist/checks/testing/integration-e2e-depth.js +575 -0
- package/dist/checks/testing/integration-e2e-depth.js.map +1 -0
- package/dist/checks/testing/playwright-feature-coverage-gaps.d.ts +31 -0
- package/dist/checks/testing/playwright-feature-coverage-gaps.d.ts.map +1 -0
- package/dist/checks/testing/playwright-feature-coverage-gaps.js +1582 -0
- package/dist/checks/testing/playwright-feature-coverage-gaps.js.map +1 -0
- package/dist/checks/testing/playwright-mock-inventory.d.ts +24 -0
- package/dist/checks/testing/playwright-mock-inventory.d.ts.map +1 -0
- package/dist/checks/testing/playwright-mock-inventory.js +380 -0
- package/dist/checks/testing/playwright-mock-inventory.js.map +1 -0
- package/dist/checks/testing/test-coverage-threshold.d.ts +25 -0
- package/dist/checks/testing/test-coverage-threshold.d.ts.map +1 -0
- package/dist/checks/testing/test-coverage-threshold.js +166 -0
- package/dist/checks/testing/test-coverage-threshold.js.map +1 -0
- package/dist/checks/testing/test-flakiness-score.d.ts +27 -0
- package/dist/checks/testing/test-flakiness-score.d.ts.map +1 -0
- package/dist/checks/testing/test-flakiness-score.js +358 -0
- package/dist/checks/testing/test-flakiness-score.js.map +1 -0
- package/dist/checks/testing/test-patterns.d.ts +16 -0
- package/dist/checks/testing/test-patterns.d.ts.map +1 -0
- package/dist/checks/testing/test-patterns.js +156 -0
- package/dist/checks/testing/test-patterns.js.map +1 -0
- package/dist/checks/workflows/a-plus-rating-validation.d.ts +42 -0
- package/dist/checks/workflows/a-plus-rating-validation.d.ts.map +1 -0
- package/dist/checks/workflows/a-plus-rating-validation.js +527 -0
- package/dist/checks/workflows/a-plus-rating-validation.js.map +1 -0
- package/dist/checks/workflows/affected.d.ts +14 -0
- package/dist/checks/workflows/affected.d.ts.map +1 -0
- package/dist/checks/workflows/affected.js +126 -0
- package/dist/checks/workflows/affected.js.map +1 -0
- package/dist/checks/workflows/ai.d.ts +6 -0
- package/dist/checks/workflows/ai.d.ts.map +1 -0
- package/dist/checks/workflows/ai.js +42 -0
- package/dist/checks/workflows/ai.js.map +1 -0
- package/dist/checks/workflows/all.d.ts +31 -0
- package/dist/checks/workflows/all.d.ts.map +1 -0
- package/dist/checks/workflows/all.js +2688 -0
- package/dist/checks/workflows/all.js.map +1 -0
- package/dist/checks/workflows/commit.d.ts +19 -0
- package/dist/checks/workflows/commit.d.ts.map +1 -0
- package/dist/checks/workflows/commit.js +207 -0
- package/dist/checks/workflows/commit.js.map +1 -0
- package/dist/checks/workflows/critical.d.ts +9 -0
- package/dist/checks/workflows/critical.d.ts.map +1 -0
- package/dist/checks/workflows/critical.js +213 -0
- package/dist/checks/workflows/critical.js.map +1 -0
- package/dist/checks/workflows/database-id-validation.d.ts +9 -0
- package/dist/checks/workflows/database-id-validation.d.ts.map +1 -0
- package/dist/checks/workflows/database-id-validation.js +13 -0
- package/dist/checks/workflows/database-id-validation.js.map +1 -0
- package/dist/checks/workflows/deploy.d.ts +20 -0
- package/dist/checks/workflows/deploy.d.ts.map +1 -0
- package/dist/checks/workflows/deploy.js +107 -0
- package/dist/checks/workflows/deploy.js.map +1 -0
- package/dist/checks/workflows/deployment-readiness.d.ts +12 -0
- package/dist/checks/workflows/deployment-readiness.d.ts.map +1 -0
- package/dist/checks/workflows/deployment-readiness.js +403 -0
- package/dist/checks/workflows/deployment-readiness.js.map +1 -0
- package/dist/checks/workflows/dev.d.ts +19 -0
- package/dist/checks/workflows/dev.d.ts.map +1 -0
- package/dist/checks/workflows/dev.js +88 -0
- package/dist/checks/workflows/dev.js.map +1 -0
- package/dist/checks/workflows/development.d.ts +9 -0
- package/dist/checks/workflows/development.d.ts.map +1 -0
- package/dist/checks/workflows/development.js +65 -0
- package/dist/checks/workflows/development.js.map +1 -0
- package/dist/checks/workflows/enterprise.d.ts +10 -0
- package/dist/checks/workflows/enterprise.d.ts.map +1 -0
- package/dist/checks/workflows/enterprise.js +359 -0
- package/dist/checks/workflows/enterprise.js.map +1 -0
- package/dist/checks/workflows/images.d.ts +6 -0
- package/dist/checks/workflows/images.d.ts.map +1 -0
- package/dist/checks/workflows/images.js +58 -0
- package/dist/checks/workflows/images.js.map +1 -0
- package/dist/checks/workflows/naming.d.ts +19 -0
- package/dist/checks/workflows/naming.d.ts.map +1 -0
- package/dist/checks/workflows/naming.js +42 -0
- package/dist/checks/workflows/naming.js.map +1 -0
- package/dist/checks/workflows/performance.d.ts +8 -0
- package/dist/checks/workflows/performance.d.ts.map +1 -0
- package/dist/checks/workflows/performance.js +77 -0
- package/dist/checks/workflows/performance.js.map +1 -0
- package/dist/checks/workflows/pre-deploy.d.ts +6 -0
- package/dist/checks/workflows/pre-deploy.d.ts.map +1 -0
- package/dist/checks/workflows/pre-deploy.js +41 -0
- package/dist/checks/workflows/pre-deploy.js.map +1 -0
- package/dist/checks/workflows/security.d.ts +8 -0
- package/dist/checks/workflows/security.d.ts.map +1 -0
- package/dist/checks/workflows/security.js +71 -0
- package/dist/checks/workflows/security.js.map +1 -0
- package/dist/checks/workflows/supercatch.d.ts +8 -0
- package/dist/checks/workflows/supercatch.d.ts.map +1 -0
- package/dist/checks/workflows/supercatch.js +127 -0
- package/dist/checks/workflows/supercatch.js.map +1 -0
- package/dist/checks/workflows/ui-quality.d.ts +9 -0
- package/dist/checks/workflows/ui-quality.d.ts.map +1 -0
- package/dist/checks/workflows/ui-quality.js +264 -0
- package/dist/checks/workflows/ui-quality.js.map +1 -0
- package/dist/checks/workflows/ui-uniformity.d.ts +18 -0
- package/dist/checks/workflows/ui-uniformity.d.ts.map +1 -0
- package/dist/checks/workflows/ui-uniformity.js +265 -0
- package/dist/checks/workflows/ui-uniformity.js.map +1 -0
- package/dist/checks/workflows/vercel.d.ts +16 -0
- package/dist/checks/workflows/vercel.d.ts.map +1 -0
- package/dist/checks/workflows/vercel.js +173 -0
- package/dist/checks/workflows/vercel.js.map +1 -0
- package/dist/utils/validation-helpers.d.ts +43 -0
- package/dist/utils/validation-helpers.d.ts.map +1 -0
- package/dist/utils/validation-helpers.js +370 -0
- package/dist/utils/validation-helpers.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1546 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Consolidated Performance Preflight
|
|
5
|
+
*
|
|
6
|
+
* Combines 10 performance checks into one module:
|
|
7
|
+
* - bundle (bundle size & large dependencies)
|
|
8
|
+
* - images (image optimization & lazy loading & LCP priority)
|
|
9
|
+
* - lazy (heavy component lazy loading)
|
|
10
|
+
* - skeleton (skeleton loader usage)
|
|
11
|
+
* - virtual (virtual scroll candidates)
|
|
12
|
+
* - zindex (z-index design token usage)
|
|
13
|
+
* - recharts (recharts lazy loading)
|
|
14
|
+
* - dialogs (dialog lazy loading)
|
|
15
|
+
* - queries (N+1 patterns & force-dynamic without caching)
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* pnpm preflight:perf # All checks
|
|
19
|
+
* pnpm preflight:perf bundle # Bundle size only
|
|
20
|
+
* pnpm preflight:perf images # Image optimization only
|
|
21
|
+
* pnpm preflight:perf lazy # Lazy loading only
|
|
22
|
+
* pnpm preflight:perf skeleton # Skeleton loaders only
|
|
23
|
+
* pnpm preflight:perf virtual # Virtual scroll only
|
|
24
|
+
* pnpm preflight:perf zindex # Z-index tokens only
|
|
25
|
+
* pnpm preflight:perf recharts # Recharts lazy loading only
|
|
26
|
+
* pnpm preflight:perf dialogs # Dialog lazy loading only
|
|
27
|
+
* pnpm preflight:perf queries # Database query efficiency only
|
|
28
|
+
*/
|
|
29
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
32
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
33
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
34
|
+
}
|
|
35
|
+
Object.defineProperty(o, k2, desc);
|
|
36
|
+
}) : (function(o, m, k, k2) {
|
|
37
|
+
if (k2 === undefined) k2 = k;
|
|
38
|
+
o[k2] = m[k];
|
|
39
|
+
}));
|
|
40
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
41
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
42
|
+
}) : function(o, v) {
|
|
43
|
+
o["default"] = v;
|
|
44
|
+
});
|
|
45
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
46
|
+
var ownKeys = function(o) {
|
|
47
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
48
|
+
var ar = [];
|
|
49
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
50
|
+
return ar;
|
|
51
|
+
};
|
|
52
|
+
return ownKeys(o);
|
|
53
|
+
};
|
|
54
|
+
return function (mod) {
|
|
55
|
+
if (mod && mod.__esModule) return mod;
|
|
56
|
+
var result = {};
|
|
57
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
58
|
+
__setModuleDefault(result, mod);
|
|
59
|
+
return result;
|
|
60
|
+
};
|
|
61
|
+
})();
|
|
62
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
63
|
+
exports.PerformancePreflightModule = void 0;
|
|
64
|
+
const child_process_1 = require("child_process");
|
|
65
|
+
const fs = __importStar(require("fs"));
|
|
66
|
+
const net = __importStar(require("net"));
|
|
67
|
+
const path = __importStar(require("path"));
|
|
68
|
+
const playwright_1 = require("playwright");
|
|
69
|
+
const promises_1 = require("timers/promises");
|
|
70
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
71
|
+
const file_cache_1 = require("../../shared/file-cache");
|
|
72
|
+
const glob_patterns_1 = require("../../shared/glob-patterns");
|
|
73
|
+
const universal_progress_reporter_1 = require("../system/universal-progress-reporter");
|
|
74
|
+
const concurrency_config_1 = require("../../shared/concurrency-config");
|
|
75
|
+
const EXCLUDED = [...glob_patterns_1.STANDARD_EXCLUDES];
|
|
76
|
+
const PREFLIGHT_PORT = Number(process.env.PREFLIGHT_PORT || process.env.PORT || 3000);
|
|
77
|
+
const HOMEPAGE_URL = process.env.PREFLIGHT_BASE_URL || `http://localhost:${PREFLIGHT_PORT}`;
|
|
78
|
+
const DEV_SERVER_START_TIMEOUT = 30_000;
|
|
79
|
+
const PROD_SERVER_START_TIMEOUT = 90_000;
|
|
80
|
+
const PAGELOAD_SERVER_MODE = (process.env.PREFLIGHT_PAGELOAD_SERVER_MODE || "dev");
|
|
81
|
+
const PAGELOAD_NAVIGATION_BUDGET_MS = Number(process.env.PREFLIGHT_PAGELOAD_NAVIGATION_BUDGET_MS || 4_000);
|
|
82
|
+
const PAGELOAD_REQUEST_BUDGET = Number(process.env.PREFLIGHT_PAGELOAD_REQUEST_BUDGET || 14);
|
|
83
|
+
const PAGELOAD_API_DUPLICATE_LIMIT = Number(process.env.PREFLIGHT_PAGELOAD_API_DUPLICATE_LIMIT || 1); // Allow one hit per endpoint by default
|
|
84
|
+
const PAGELOAD_SESSION_DUPLICATE_LIMIT = Number(process.env.PREFLIGHT_PAGELOAD_SESSION_DUPLICATE_LIMIT || 1);
|
|
85
|
+
const PAGELOAD_NOTIFICATION_DUPLICATE_LIMIT = Number(process.env.PREFLIGHT_PAGELOAD_NOTIFICATION_DUPLICATE_LIMIT || 1);
|
|
86
|
+
const PAGELOAD_PRISMA_QUERY_BUDGET = Number(process.env.PREFLIGHT_PAGELOAD_PRISMA_QUERY_BUDGET || 40);
|
|
87
|
+
const PAGELOAD_PRISMA_DUPLICATE_THRESHOLD = Number(process.env.PREFLIGHT_PAGELOAD_PRISMA_DUPLICATE_THRESHOLD || 3);
|
|
88
|
+
const WAIT_FOR_STABLE_NETWORK_MS = Number(process.env.PREFLIGHT_PAGELOAD_STABLE_NETWORK_MS || 1_000);
|
|
89
|
+
const PAGELOAD_TRANSFER_BUDGET_BYTES = Number(process.env.PREFLIGHT_PAGELOAD_TRANSFER_BUDGET_BYTES || 1_000_000); // ~1MB
|
|
90
|
+
const PAGELOAD_ROUTE_TIMEOUT_MS = Number(process.env.PREFLIGHT_PAGELOAD_ROUTE_TIMEOUT_MS || 8_000);
|
|
91
|
+
const PAGELOAD_LOAD_TIMEOUT_MS = Number(process.env.PREFLIGHT_PAGELOAD_LOAD_TIMEOUT_MS || 6_000);
|
|
92
|
+
const PAGELOAD_NETWORKIDLE_TIMEOUT_MS = Number(process.env.PREFLIGHT_PAGELOAD_NETWORKIDLE_TIMEOUT_MS || 2_000);
|
|
93
|
+
const PAGELOAD_PROGRESS_EVERY = Number(process.env.PREFLIGHT_PAGELOAD_PROGRESS_EVERY || 10);
|
|
94
|
+
const PAGELOAD_CONCURRENCY = Math.max(1, Number(process.env.PREFLIGHT_PAGELOAD_CONCURRENCY || 1));
|
|
95
|
+
const PAGELOAD_STRICT = process.env.PREFLIGHT_PAGELOAD_STRICT === "1";
|
|
96
|
+
const PAGELOAD_INCLUDE_SITEMAP = process.env.PREFLIGHT_PAGELOAD_INCLUDE_SITEMAP === "1";
|
|
97
|
+
const PAGELOAD_SITEMAP_MAX_URLS = Math.max(0, Number(process.env.PREFLIGHT_PAGELOAD_SITEMAP_MAX_URLS || 2000));
|
|
98
|
+
const PAGELOAD_ROUTES_CONFIG = process.env.PREFLIGHT_PAGELOAD_ROUTES_CONFIG ||
|
|
99
|
+
path.join(process.cwd(), "scripts/active/preflights/config/page-load-routes.json");
|
|
100
|
+
const DEFAULT_PAGE_ROUTES = ["/"];
|
|
101
|
+
const PNPM_CMD = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
|
|
102
|
+
function normalizeRoute(route) {
|
|
103
|
+
if (!route)
|
|
104
|
+
return "/";
|
|
105
|
+
let normalized = route.trim();
|
|
106
|
+
if (!normalized.startsWith("http")) {
|
|
107
|
+
normalized = normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
108
|
+
}
|
|
109
|
+
if (normalized.endsWith("/") && normalized !== "/") {
|
|
110
|
+
normalized = normalized.slice(0, -1);
|
|
111
|
+
}
|
|
112
|
+
return normalized;
|
|
113
|
+
}
|
|
114
|
+
function normalizeExampleRoutes(examples) {
|
|
115
|
+
const normalized = {};
|
|
116
|
+
if (!examples)
|
|
117
|
+
return normalized;
|
|
118
|
+
for (const [pattern, value] of Object.entries(examples)) {
|
|
119
|
+
const values = Array.isArray(value) ? value : [value];
|
|
120
|
+
const routes = values.map((v) => normalizeRoute(v)).filter(Boolean);
|
|
121
|
+
if (routes.length > 0)
|
|
122
|
+
normalized[normalizeRoute(pattern)] = routes;
|
|
123
|
+
}
|
|
124
|
+
return normalized;
|
|
125
|
+
}
|
|
126
|
+
function substituteDynamicRoute(template, example) {
|
|
127
|
+
// Replace each [param] segment with example segments in order.
|
|
128
|
+
const templateParts = template.split("/").filter(Boolean);
|
|
129
|
+
const exampleParts = example.split("/").filter(Boolean);
|
|
130
|
+
const out = [];
|
|
131
|
+
let exampleIndex = 0;
|
|
132
|
+
for (const part of templateParts) {
|
|
133
|
+
if (part.startsWith("[") && part.endsWith("]")) {
|
|
134
|
+
if (exampleIndex >= exampleParts.length)
|
|
135
|
+
return null;
|
|
136
|
+
out.push(exampleParts[exampleIndex++]);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
out.push(part);
|
|
140
|
+
if (exampleParts[exampleIndex] === part)
|
|
141
|
+
exampleIndex++;
|
|
142
|
+
}
|
|
143
|
+
return `/${out.join("/")}`.replace(/\/+$/, "") || "/";
|
|
144
|
+
}
|
|
145
|
+
function matchRoutePattern(pattern, route) {
|
|
146
|
+
const normalizedPattern = (pattern || "").trim();
|
|
147
|
+
if (!normalizedPattern)
|
|
148
|
+
return false;
|
|
149
|
+
if (normalizedPattern.startsWith("re:")) {
|
|
150
|
+
try {
|
|
151
|
+
return new RegExp(normalizedPattern.slice(3)).test(route);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Simple glob-ish support: '*' means any chars.
|
|
158
|
+
if (normalizedPattern.includes("*")) {
|
|
159
|
+
const escaped = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
160
|
+
const regex = new RegExp(`^${escaped.replace(/\*/g, ".*")}$`);
|
|
161
|
+
return regex.test(route);
|
|
162
|
+
}
|
|
163
|
+
// Prefix match support for directory-like patterns.
|
|
164
|
+
if (normalizedPattern.endsWith("/")) {
|
|
165
|
+
return route.startsWith(normalizedPattern);
|
|
166
|
+
}
|
|
167
|
+
return route === normalizedPattern;
|
|
168
|
+
}
|
|
169
|
+
function dynamicTemplateMatchesRoute(template, route) {
|
|
170
|
+
if (!template.includes("["))
|
|
171
|
+
return template === route;
|
|
172
|
+
const escaped = template.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
173
|
+
const regexStr = `^${escaped.replace(/\\\[[^\]]+\\\]/g, "[^/]+")}$`;
|
|
174
|
+
try {
|
|
175
|
+
return new RegExp(regexStr).test(route);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function extractSitemapLocs(xml) {
|
|
182
|
+
const locs = [];
|
|
183
|
+
const re = /<loc>([^<]+)<\/loc>/gim;
|
|
184
|
+
let match;
|
|
185
|
+
while ((match = re.exec(xml))) {
|
|
186
|
+
const loc = (match[1] || "").trim();
|
|
187
|
+
if (loc)
|
|
188
|
+
locs.push(loc);
|
|
189
|
+
}
|
|
190
|
+
return locs;
|
|
191
|
+
}
|
|
192
|
+
async function fetchText(url) {
|
|
193
|
+
try {
|
|
194
|
+
const res = await fetch(url, { redirect: "follow" });
|
|
195
|
+
if (!res.ok)
|
|
196
|
+
return null;
|
|
197
|
+
return await res.text();
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function discoverRoutesFromSitemaps() {
|
|
204
|
+
if (!PAGELOAD_INCLUDE_SITEMAP)
|
|
205
|
+
return [];
|
|
206
|
+
const base = HOMEPAGE_URL.replace(/\/$/, "");
|
|
207
|
+
// On some Windows setups `localhost` resolves to IPv6 first; use IPv4 loopback for fetch to avoid connection issues.
|
|
208
|
+
const fetchBase = base.replace("://localhost", "://127.0.0.1");
|
|
209
|
+
const primaryCandidates = [`${fetchBase}/sitemap.xml`, `${fetchBase}/sitemap/0.xml`];
|
|
210
|
+
let xml = null;
|
|
211
|
+
for (const candidate of primaryCandidates) {
|
|
212
|
+
xml = await fetchText(candidate);
|
|
213
|
+
if (xml)
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
if (!xml)
|
|
217
|
+
return [];
|
|
218
|
+
const locs = extractSitemapLocs(xml);
|
|
219
|
+
const sitemapXmlUrls = locs.filter((l) => l.toLowerCase().endsWith(".xml"));
|
|
220
|
+
const urlLocs = [];
|
|
221
|
+
if (sitemapXmlUrls.length > 0) {
|
|
222
|
+
for (const sitemapUrl of sitemapXmlUrls.slice(0, 25)) {
|
|
223
|
+
const childXml = await fetchText(sitemapUrl);
|
|
224
|
+
if (!childXml)
|
|
225
|
+
continue;
|
|
226
|
+
urlLocs.push(...extractSitemapLocs(childXml));
|
|
227
|
+
if (PAGELOAD_SITEMAP_MAX_URLS > 0 && urlLocs.length >= PAGELOAD_SITEMAP_MAX_URLS)
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
urlLocs.push(...locs);
|
|
233
|
+
}
|
|
234
|
+
const paths = [];
|
|
235
|
+
for (const loc of urlLocs) {
|
|
236
|
+
try {
|
|
237
|
+
const u = new URL(loc);
|
|
238
|
+
const pathOnly = normalizeRoute(u.pathname);
|
|
239
|
+
if (pathOnly.startsWith("/_next"))
|
|
240
|
+
continue;
|
|
241
|
+
paths.push(pathOnly);
|
|
242
|
+
if (PAGELOAD_SITEMAP_MAX_URLS > 0 && paths.length >= PAGELOAD_SITEMAP_MAX_URLS)
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// ignore invalid loc
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return Array.from(new Set(paths)).sort();
|
|
250
|
+
}
|
|
251
|
+
async function discoverRoutesFromApp() {
|
|
252
|
+
const files = await file_cache_1.fileCache.getFiles("app/**/page.{ts,tsx,js,jsx}", { ignore: EXCLUDED });
|
|
253
|
+
const routes = [];
|
|
254
|
+
const skippedDynamic = [];
|
|
255
|
+
for (const file of files) {
|
|
256
|
+
const rel = file.replace(/\\/g, "/");
|
|
257
|
+
const withoutApp = rel.replace(/^app\//, "");
|
|
258
|
+
const withoutPage = withoutApp.replace(/(^|[\\/])page\.(ts|tsx|js|jsx)$/i, "");
|
|
259
|
+
// Empty => root
|
|
260
|
+
const segments = withoutPage ? withoutPage.split("/") : [];
|
|
261
|
+
// Skip route groups, parallel routes, and intercepting routes in path computation.
|
|
262
|
+
const cleaned = [];
|
|
263
|
+
let isDynamic = false;
|
|
264
|
+
for (const seg of segments) {
|
|
265
|
+
if (!seg)
|
|
266
|
+
continue;
|
|
267
|
+
if (seg.startsWith("(") && seg.endsWith(")"))
|
|
268
|
+
continue; // route group
|
|
269
|
+
if (seg.startsWith("@"))
|
|
270
|
+
continue; // parallel route
|
|
271
|
+
if (seg.startsWith("(..") || seg.startsWith("(.)"))
|
|
272
|
+
continue; // intercepting
|
|
273
|
+
if (seg.startsWith("[") && seg.endsWith("]")) {
|
|
274
|
+
isDynamic = true;
|
|
275
|
+
cleaned.push(seg);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
cleaned.push(seg);
|
|
279
|
+
}
|
|
280
|
+
const route = `/${cleaned.filter(Boolean).join("/")}`.replace(/\/+$/, "") || "/";
|
|
281
|
+
const normalized = normalizeRoute(route);
|
|
282
|
+
if (isDynamic) {
|
|
283
|
+
skippedDynamic.push(normalized);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
routes.push(normalized);
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
routes: Array.from(new Set(routes)).sort(),
|
|
290
|
+
skippedDynamic: Array.from(new Set(skippedDynamic)).sort(),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
async function loadPageRoutes() {
|
|
294
|
+
const envRoutes = process.env.PREFLIGHT_PAGE_ROUTES;
|
|
295
|
+
const envMaxRoutes = Number(process.env.PREFLIGHT_PAGELOAD_MAX_ROUTES || 0);
|
|
296
|
+
const envAllowPartial = process.env.PREFLIGHT_PAGELOAD_ALLOW_PARTIAL === "1";
|
|
297
|
+
if (envRoutes) {
|
|
298
|
+
const parts = envRoutes.split(",").map((r) => normalizeRoute(r));
|
|
299
|
+
const dedupedParts = Array.from(new Set(parts)).filter(Boolean);
|
|
300
|
+
return {
|
|
301
|
+
routes: dedupedParts.length > 0 ? dedupedParts : DEFAULT_PAGE_ROUTES,
|
|
302
|
+
skippedDynamic: [],
|
|
303
|
+
maxRoutes: envMaxRoutes > 0 ? envMaxRoutes : 250,
|
|
304
|
+
allowPartial: envAllowPartial,
|
|
305
|
+
exclude: [],
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
let cfg;
|
|
309
|
+
if (fs.existsSync(PAGELOAD_ROUTES_CONFIG)) {
|
|
310
|
+
try {
|
|
311
|
+
cfg = JSON.parse(fs.readFileSync(PAGELOAD_ROUTES_CONFIG, "utf8"));
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
console.warn("Unable to parse page-load-routes config, falling back to discovery:", err);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Back-compat: array = explicit list.
|
|
318
|
+
if (Array.isArray(cfg)) {
|
|
319
|
+
const normalized = Array.from(new Set((cfg || []).map((r) => normalizeRoute(r)))).filter(Boolean);
|
|
320
|
+
return {
|
|
321
|
+
routes: normalized.length > 0 ? normalized : DEFAULT_PAGE_ROUTES,
|
|
322
|
+
skippedDynamic: [],
|
|
323
|
+
maxRoutes: envMaxRoutes > 0 ? envMaxRoutes : 250,
|
|
324
|
+
allowPartial: envAllowPartial,
|
|
325
|
+
exclude: [],
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
const mode = cfg?.mode || "discover";
|
|
329
|
+
const exclude = (cfg?.exclude || []).map((r) => normalizeRoute(r));
|
|
330
|
+
const allowPartial = process.env.PREFLIGHT_PAGELOAD_ALLOW_PARTIAL === "1"
|
|
331
|
+
? true
|
|
332
|
+
: (cfg?.allowPartial ?? envAllowPartial);
|
|
333
|
+
const maxRoutes = envMaxRoutes > 0 ? envMaxRoutes : cfg?.maxRoutes || 250;
|
|
334
|
+
const requireDynamicExamples = process.env.PREFLIGHT_PAGELOAD_REQUIRE_DYNAMIC_EXAMPLES === "1";
|
|
335
|
+
const dynamicExamples = normalizeExampleRoutes(cfg?.dynamicExamples);
|
|
336
|
+
const storageStatePath = process.env.PREFLIGHT_PLAYWRIGHT_STORAGE_STATE;
|
|
337
|
+
const includeProtected = process.env.PREFLIGHT_PAGELOAD_INCLUDE_PROTECTED === "1";
|
|
338
|
+
const defaultProtectedPrefixes = ["/account/", "/admin/", "/seller/", "/store/"];
|
|
339
|
+
const effectiveExclude = [...exclude];
|
|
340
|
+
if (!storageStatePath && !includeProtected) {
|
|
341
|
+
// Skip protected areas by default when we don't have auth state.
|
|
342
|
+
defaultProtectedPrefixes.forEach((p) => effectiveExclude.push(`${p}*`));
|
|
343
|
+
}
|
|
344
|
+
if (mode === "list") {
|
|
345
|
+
const configured = (cfg?.routes || []).map((r) => normalizeRoute(r));
|
|
346
|
+
const include = (cfg?.include || []).map((r) => normalizeRoute(r));
|
|
347
|
+
const routes = Array.from(new Set([...configured, ...include])).filter(Boolean);
|
|
348
|
+
const filtered = effectiveExclude.length > 0
|
|
349
|
+
? routes.filter((r) => !effectiveExclude.some((p) => matchRoutePattern(p, r)))
|
|
350
|
+
: routes;
|
|
351
|
+
return {
|
|
352
|
+
routes: filtered.length > 0 ? filtered : DEFAULT_PAGE_ROUTES,
|
|
353
|
+
skippedDynamic: [],
|
|
354
|
+
maxRoutes,
|
|
355
|
+
allowPartial,
|
|
356
|
+
exclude: effectiveExclude,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
const discovered = await discoverRoutesFromApp();
|
|
360
|
+
const include = (cfg?.include || []).map((r) => normalizeRoute(r));
|
|
361
|
+
let merged = Array.from(new Set([...discovered.routes, ...include])).filter(Boolean);
|
|
362
|
+
// Expand dynamic routes with user-provided examples.
|
|
363
|
+
const expandedDynamic = [];
|
|
364
|
+
const stillSkipped = [];
|
|
365
|
+
for (const dyn of discovered.skippedDynamic) {
|
|
366
|
+
const matches = Object.entries(dynamicExamples).find(([pattern]) => matchRoutePattern(pattern, dyn) || pattern === dyn);
|
|
367
|
+
const exampleList = matches ? matches[1] : undefined;
|
|
368
|
+
if (exampleList && exampleList.length > 0) {
|
|
369
|
+
for (const ex of exampleList) {
|
|
370
|
+
// If user provides a full example route, accept it. If they provide only segments, best-effort substitute.
|
|
371
|
+
const resolved = ex.includes("[")
|
|
372
|
+
? null
|
|
373
|
+
: ex.startsWith("/")
|
|
374
|
+
? ex
|
|
375
|
+
: substituteDynamicRoute(dyn, ex);
|
|
376
|
+
const route = resolved ? normalizeRoute(resolved) : normalizeRoute(ex);
|
|
377
|
+
if (route && !route.includes("["))
|
|
378
|
+
expandedDynamic.push(route);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
stillSkipped.push(dyn);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (expandedDynamic.length > 0) {
|
|
386
|
+
merged = Array.from(new Set([...merged, ...expandedDynamic]));
|
|
387
|
+
}
|
|
388
|
+
if (effectiveExclude.length > 0) {
|
|
389
|
+
merged = merged.filter((route) => !effectiveExclude.some((pattern) => matchRoutePattern(pattern, route)));
|
|
390
|
+
}
|
|
391
|
+
const finalSkippedDynamic = stillSkipped;
|
|
392
|
+
if (requireDynamicExamples && finalSkippedDynamic.length > 0) {
|
|
393
|
+
// Keep them in the skipped list; the checker will convert this into a blocking error.
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
routes: merged.length > 0 ? merged : DEFAULT_PAGE_ROUTES,
|
|
397
|
+
skippedDynamic: finalSkippedDynamic,
|
|
398
|
+
maxRoutes,
|
|
399
|
+
allowPartial,
|
|
400
|
+
exclude: effectiveExclude,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
function buildUrl(route) {
|
|
404
|
+
try {
|
|
405
|
+
return new URL(route, HOMEPAGE_URL).toString();
|
|
406
|
+
}
|
|
407
|
+
catch (_err) {
|
|
408
|
+
return `${HOMEPAGE_URL.replace(/\/$/, "")}${route.startsWith("/") ? route : `/${route}`}`;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async function isPortOpen(port) {
|
|
412
|
+
return new Promise((resolve) => {
|
|
413
|
+
const socket = net.createConnection({ port, host: "127.0.0.1" });
|
|
414
|
+
socket.once("connect", () => {
|
|
415
|
+
socket.destroy();
|
|
416
|
+
resolve(true);
|
|
417
|
+
});
|
|
418
|
+
socket.once("error", () => resolve(false));
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
async function waitForPort(port, timeoutMs) {
|
|
422
|
+
const start = Date.now();
|
|
423
|
+
while (Date.now() - start < timeoutMs) {
|
|
424
|
+
if (await isPortOpen(port))
|
|
425
|
+
return true;
|
|
426
|
+
await (0, promises_1.setTimeout)(300);
|
|
427
|
+
}
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
function startServer(logs) {
|
|
431
|
+
const args = PAGELOAD_SERVER_MODE === "prod" ? ["start"] : ["dev"];
|
|
432
|
+
const env = {
|
|
433
|
+
...process.env,
|
|
434
|
+
PORT: String(PREFLIGHT_PORT),
|
|
435
|
+
NEXT_TELEMETRY_DISABLED: "1",
|
|
436
|
+
TURBO_NO_PERSISTENCE: "1",
|
|
437
|
+
...(process.env.NODE_NO_WARNINGS ? {} : { NODE_NO_WARNINGS: "1" }),
|
|
438
|
+
...(PAGELOAD_SERVER_MODE === "prod" ? { NODE_ENV: "production" } : {}),
|
|
439
|
+
};
|
|
440
|
+
if (PAGELOAD_SERVER_MODE === "prod") {
|
|
441
|
+
// next start requires a build output
|
|
442
|
+
const nextDir = path.join(process.cwd(), ".next");
|
|
443
|
+
if (!fs.existsSync(nextDir)) {
|
|
444
|
+
throw new Error("Production page-load budgets require a built .next directory. Run `pnpm build` first or use PREFLIGHT_PAGELOAD_SERVER_MODE=dev.");
|
|
445
|
+
}
|
|
446
|
+
const buildIdPath = path.join(nextDir, "BUILD_ID");
|
|
447
|
+
if (!fs.existsSync(buildIdPath)) {
|
|
448
|
+
throw new Error("Production page-load budgets require a complete Next.js production build (.next/BUILD_ID missing). Run `pnpm build` (or at least `pnpm next build`) before running PREFLIGHT_PAGELOAD_SERVER_MODE=prod.");
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// Windows quirks:
|
|
452
|
+
// - npm_execpath may be pnpm.exe (native) or a JS entrypoint. Don't try to run pnpm.exe as JS.
|
|
453
|
+
// - Spawning .cmd can be flaky in some environments; prefer pnpm.exe when available.
|
|
454
|
+
const pnpmExecPath = process.platform === "win32" ? process.env.npm_execpath : undefined;
|
|
455
|
+
const pnpmExecLooksLikeExe = !!pnpmExecPath && /\.exe$/i.test(pnpmExecPath);
|
|
456
|
+
const pnpmExecLooksLikeNodeScript = !!pnpmExecPath && /\.(c?js|mjs)$/i.test(pnpmExecPath);
|
|
457
|
+
const spawnCommand = process.platform === "win32" && pnpmExecLooksLikeNodeScript
|
|
458
|
+
? process.execPath
|
|
459
|
+
: process.platform === "win32" && pnpmExecLooksLikeExe
|
|
460
|
+
? pnpmExecPath
|
|
461
|
+
: PNPM_CMD;
|
|
462
|
+
const spawnArgs = process.platform === "win32" && pnpmExecLooksLikeNodeScript ? [pnpmExecPath, ...args] : args;
|
|
463
|
+
const server = (0, child_process_1.spawn)(spawnCommand, spawnArgs, {
|
|
464
|
+
cwd: process.cwd(),
|
|
465
|
+
env,
|
|
466
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
467
|
+
});
|
|
468
|
+
server.stdout.on("data", (chunk) => logs.push(chunk.toString()));
|
|
469
|
+
server.stderr.on("data", (chunk) => logs.push(chunk.toString()));
|
|
470
|
+
return server;
|
|
471
|
+
}
|
|
472
|
+
function stopServer(server) {
|
|
473
|
+
if (!server)
|
|
474
|
+
return;
|
|
475
|
+
try {
|
|
476
|
+
if (process.platform === "win32" && typeof server.pid === "number") {
|
|
477
|
+
(0, child_process_1.spawnSync)("taskkill", ["/PID", String(server.pid), "/T", "/F"], {
|
|
478
|
+
stdio: "ignore",
|
|
479
|
+
});
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
server.kill();
|
|
483
|
+
}
|
|
484
|
+
catch (err) {
|
|
485
|
+
console.warn("Failed to stop server:", err);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
// CACHED FILE LISTS - Scan once, use everywhere
|
|
489
|
+
let _cachedAppTsxFiles = null;
|
|
490
|
+
async function getAppTsxFiles() {
|
|
491
|
+
if (!_cachedAppTsxFiles) {
|
|
492
|
+
_cachedAppTsxFiles = await file_cache_1.fileCache.getAppTSX();
|
|
493
|
+
}
|
|
494
|
+
return _cachedAppTsxFiles;
|
|
495
|
+
}
|
|
496
|
+
let _cachedAppComponentsTsxFiles = null;
|
|
497
|
+
async function getAppComponentsTsxFiles() {
|
|
498
|
+
if (!_cachedAppComponentsTsxFiles) {
|
|
499
|
+
_cachedAppComponentsTsxFiles = await file_cache_1.fileCache.getAppAndComponentsTSX();
|
|
500
|
+
}
|
|
501
|
+
return _cachedAppComponentsTsxFiles;
|
|
502
|
+
}
|
|
503
|
+
async function getAppLibFiles() {
|
|
504
|
+
return file_cache_1.fileCache.getAppLibFiles();
|
|
505
|
+
}
|
|
506
|
+
// Get concurrency from shared config (respects PREFLIGHT_CONCURRENCY env var)
|
|
507
|
+
const concurrencyConfig = (0, concurrency_config_1.getConcurrencyConfig)();
|
|
508
|
+
class PerformancePreflightModule {
|
|
509
|
+
verbose;
|
|
510
|
+
parallel = false;
|
|
511
|
+
constructor(options = {}) {
|
|
512
|
+
this.verbose = options.verbose || false;
|
|
513
|
+
this.parallel = options.parallel || concurrencyConfig.parallel;
|
|
514
|
+
}
|
|
515
|
+
async ensureServer(serverLogs) {
|
|
516
|
+
const alreadyRunning = await isPortOpen(PREFLIGHT_PORT);
|
|
517
|
+
if (alreadyRunning) {
|
|
518
|
+
return { usedExisting: true };
|
|
519
|
+
}
|
|
520
|
+
const server = startServer(serverLogs);
|
|
521
|
+
const timeoutMs = PAGELOAD_SERVER_MODE === "prod" ? PROD_SERVER_START_TIMEOUT : DEV_SERVER_START_TIMEOUT;
|
|
522
|
+
const readyResult = await Promise.race([
|
|
523
|
+
waitForPort(PREFLIGHT_PORT, timeoutMs).then((ok) => ({
|
|
524
|
+
ok,
|
|
525
|
+
exited: false,
|
|
526
|
+
})),
|
|
527
|
+
new Promise((resolve) => {
|
|
528
|
+
server.once("exit", (code, signal) => resolve({ ok: false, exited: true, code, signal }));
|
|
529
|
+
}),
|
|
530
|
+
]);
|
|
531
|
+
if (!readyResult.ok) {
|
|
532
|
+
stopServer(server);
|
|
533
|
+
const lastLogs = serverLogs.slice(-20).join("").trim();
|
|
534
|
+
const exitInfo = readyResult.exited
|
|
535
|
+
? `Process exited early (code: ${readyResult.code ?? "unknown"}, signal: ${readyResult.signal ?? "none"}). `
|
|
536
|
+
: "";
|
|
537
|
+
throw new Error(`${exitInfo}Server did not start on port ${PREFLIGHT_PORT} within ${timeoutMs}ms.` +
|
|
538
|
+
(lastLogs ? `\n\nLast server logs:\n${lastLogs}` : ""));
|
|
539
|
+
}
|
|
540
|
+
// Let the server settle before navigation to avoid capturing cold-start noise only.
|
|
541
|
+
await (0, promises_1.setTimeout)(500);
|
|
542
|
+
return { usedExisting: false, server };
|
|
543
|
+
}
|
|
544
|
+
async capturePageTrace(context, route) {
|
|
545
|
+
const page = await context.newPage();
|
|
546
|
+
const requests = [];
|
|
547
|
+
const startTimes = new Map();
|
|
548
|
+
page.on("request", (req) => {
|
|
549
|
+
startTimes.set(req.url(), Date.now());
|
|
550
|
+
});
|
|
551
|
+
page.on("requestfinished", async (req) => {
|
|
552
|
+
const started = startTimes.get(req.url());
|
|
553
|
+
const res = await req.response();
|
|
554
|
+
requests.push({
|
|
555
|
+
url: req.url(),
|
|
556
|
+
method: req.method(),
|
|
557
|
+
status: res?.status(),
|
|
558
|
+
durationMs: started ? Date.now() - started : undefined,
|
|
559
|
+
resourceType: req.resourceType(),
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
page.on("requestfailed", (req) => {
|
|
563
|
+
const started = startTimes.get(req.url());
|
|
564
|
+
requests.push({
|
|
565
|
+
url: req.url(),
|
|
566
|
+
method: req.method(),
|
|
567
|
+
status: undefined,
|
|
568
|
+
durationMs: started ? Date.now() - started : undefined,
|
|
569
|
+
resourceType: req.resourceType(),
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
try {
|
|
573
|
+
await page.goto(buildUrl(route), {
|
|
574
|
+
waitUntil: "domcontentloaded",
|
|
575
|
+
timeout: PAGELOAD_ROUTE_TIMEOUT_MS,
|
|
576
|
+
});
|
|
577
|
+
await page
|
|
578
|
+
.waitForLoadState("load", { timeout: PAGELOAD_LOAD_TIMEOUT_MS })
|
|
579
|
+
.catch(() => undefined);
|
|
580
|
+
await page
|
|
581
|
+
.waitForLoadState("networkidle", {
|
|
582
|
+
timeout: PAGELOAD_NETWORKIDLE_TIMEOUT_MS,
|
|
583
|
+
})
|
|
584
|
+
.catch(() => undefined);
|
|
585
|
+
await (0, promises_1.setTimeout)(WAIT_FOR_STABLE_NETWORK_MS);
|
|
586
|
+
const metrics = await page.evaluate(() => {
|
|
587
|
+
const nav = performance.getEntriesByType("navigation")[0];
|
|
588
|
+
const resources = performance.getEntriesByType("resource");
|
|
589
|
+
const totalTransferBytes = resources.reduce((sum, r) => sum + (r.transferSize || 0), 0);
|
|
590
|
+
const apiTransferBytes = resources
|
|
591
|
+
.filter((r) => typeof r.name === "string" && r.name.includes("/api/"))
|
|
592
|
+
.reduce((sum, r) => sum + (r.transferSize || 0), 0);
|
|
593
|
+
return {
|
|
594
|
+
navDuration: nav?.duration || 0,
|
|
595
|
+
totalTransferBytes,
|
|
596
|
+
apiTransferBytes,
|
|
597
|
+
};
|
|
598
|
+
});
|
|
599
|
+
return {
|
|
600
|
+
requests,
|
|
601
|
+
navDuration: metrics.navDuration,
|
|
602
|
+
totalTransferBytes: metrics.totalTransferBytes,
|
|
603
|
+
apiTransferBytes: metrics.apiTransferBytes,
|
|
604
|
+
finalUrl: page.url(),
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
finally {
|
|
608
|
+
await page.close().catch(() => undefined);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
async checkPageLoadBudgets() {
|
|
612
|
+
const startTime = Date.now();
|
|
613
|
+
const issues = [];
|
|
614
|
+
const serverLogs = [];
|
|
615
|
+
let server;
|
|
616
|
+
let routes = [];
|
|
617
|
+
let skippedDynamic = [];
|
|
618
|
+
let maxRoutes = 250;
|
|
619
|
+
let allowPartial = false;
|
|
620
|
+
let exclude = [];
|
|
621
|
+
const pathKey = (url) => {
|
|
622
|
+
try {
|
|
623
|
+
return new URL(url).pathname;
|
|
624
|
+
}
|
|
625
|
+
catch (_err) {
|
|
626
|
+
return url.split("?")[0];
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
const evaluateRoute = (route, requests, navDuration, totalTransferBytes, apiTransferBytes, prismaLogSlice) => {
|
|
630
|
+
const routeIssues = [];
|
|
631
|
+
const apiRequests = requests.filter((r) => {
|
|
632
|
+
const path = pathKey(r.url);
|
|
633
|
+
const isApi = path.startsWith("/api");
|
|
634
|
+
const isFetchLike = r.resourceType === "fetch" ||
|
|
635
|
+
r.resourceType === "xhr" ||
|
|
636
|
+
r.resourceType === "document" ||
|
|
637
|
+
!r.resourceType;
|
|
638
|
+
return isApi && isFetchLike;
|
|
639
|
+
});
|
|
640
|
+
const documentRequests = requests.filter((r) => r.resourceType === "document");
|
|
641
|
+
const meaningfulRequestCount = apiRequests.length + documentRequests.length;
|
|
642
|
+
const apiCountByPath = new Map();
|
|
643
|
+
apiRequests.forEach((req) => {
|
|
644
|
+
const key = pathKey(req.url);
|
|
645
|
+
apiCountByPath.set(key, (apiCountByPath.get(key) || 0) + 1);
|
|
646
|
+
});
|
|
647
|
+
const duplicateApis = Array.from(apiCountByPath.entries()).filter(([, count]) => count > PAGELOAD_API_DUPLICATE_LIMIT);
|
|
648
|
+
const sessionHits = apiCountByPath.get("/api/auth/session") || 0;
|
|
649
|
+
const notificationHits = apiCountByPath.get("/api/notifications") || 0;
|
|
650
|
+
if (navDuration > PAGELOAD_NAVIGATION_BUDGET_MS) {
|
|
651
|
+
routeIssues.push({
|
|
652
|
+
file: route,
|
|
653
|
+
type: "page-load-duration",
|
|
654
|
+
severity: "warning",
|
|
655
|
+
message: `Navigation duration ${navDuration.toFixed(0)}ms exceeds budget ${PAGELOAD_NAVIGATION_BUDGET_MS}ms`,
|
|
656
|
+
suggestion: "Reduce above-the-fold data fetching and defer non-critical API calls",
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
if (meaningfulRequestCount > PAGELOAD_REQUEST_BUDGET) {
|
|
660
|
+
routeIssues.push({
|
|
661
|
+
file: route,
|
|
662
|
+
type: "page-load-requests",
|
|
663
|
+
severity: "warning",
|
|
664
|
+
message: `Document + API requests ${meaningfulRequestCount} exceed budget ${PAGELOAD_REQUEST_BUDGET}`,
|
|
665
|
+
suggestion: "Consolidate API calls, enable caching, and defer non-critical widgets",
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
if (duplicateApis.length > 0) {
|
|
669
|
+
duplicateApis.forEach(([path, count]) => {
|
|
670
|
+
routeIssues.push({
|
|
671
|
+
file: route,
|
|
672
|
+
type: "duplicate-api-calls",
|
|
673
|
+
severity: "warning",
|
|
674
|
+
message: `${path} called ${count} times on first render`,
|
|
675
|
+
suggestion: "Use server-side injection or SWR dedupe for repeated endpoints",
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
if (sessionHits > PAGELOAD_SESSION_DUPLICATE_LIMIT) {
|
|
680
|
+
routeIssues.push({
|
|
681
|
+
file: route,
|
|
682
|
+
type: "session-duplication",
|
|
683
|
+
severity: "info",
|
|
684
|
+
message: `/api/auth/session called ${sessionHits} times`,
|
|
685
|
+
suggestion: "Inject session server-side or memoize client fetch",
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
if (notificationHits > PAGELOAD_NOTIFICATION_DUPLICATE_LIMIT) {
|
|
689
|
+
routeIssues.push({
|
|
690
|
+
file: route,
|
|
691
|
+
type: "notification-duplication",
|
|
692
|
+
severity: "info",
|
|
693
|
+
message: `/api/notifications called ${notificationHits} times`,
|
|
694
|
+
suggestion: "Combine counts + list into one query or cache the list server-side",
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
if (apiTransferBytes > PAGELOAD_TRANSFER_BUDGET_BYTES) {
|
|
698
|
+
routeIssues.push({
|
|
699
|
+
file: route,
|
|
700
|
+
type: "transfer-size",
|
|
701
|
+
severity: "info",
|
|
702
|
+
message: `API transfer ${(apiTransferBytes / 1024 / 1024).toFixed(2)}MB exceeds ${(PAGELOAD_TRANSFER_BUDGET_BYTES / 1024 / 1024).toFixed(2)}MB budget`,
|
|
703
|
+
suggestion: "Reduce initial API payloads and avoid overfetching",
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
if (totalTransferBytes > PAGELOAD_TRANSFER_BUDGET_BYTES * 4) {
|
|
707
|
+
routeIssues.push({
|
|
708
|
+
file: route,
|
|
709
|
+
type: "total-transfer-size",
|
|
710
|
+
severity: "info",
|
|
711
|
+
message: `Total transfer ${(totalTransferBytes / 1024 / 1024).toFixed(2)}MB (assets + API)`,
|
|
712
|
+
suggestion: "If this stays high in production, review bundle splitting and image optimization",
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
if (prismaLogSlice.length > 0) {
|
|
716
|
+
if (prismaLogSlice.length > PAGELOAD_PRISMA_QUERY_BUDGET) {
|
|
717
|
+
routeIssues.push({
|
|
718
|
+
file: route,
|
|
719
|
+
type: "prisma-query-volume",
|
|
720
|
+
severity: "warning",
|
|
721
|
+
message: `Prisma queries during first paint: ${prismaLogSlice.length} (budget ${PAGELOAD_PRISMA_QUERY_BUDGET})`,
|
|
722
|
+
suggestion: "Batch page queries and cache counts",
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
const prismaStatements = prismaLogSlice.map((line) => line.replace(/^.*prisma:query\s+/i, "").trim());
|
|
726
|
+
const prismaCounts = prismaStatements.reduce((acc, stmt) => {
|
|
727
|
+
acc[stmt] = (acc[stmt] || 0) + 1;
|
|
728
|
+
return acc;
|
|
729
|
+
}, {});
|
|
730
|
+
Object.entries(prismaCounts)
|
|
731
|
+
.filter(([, count]) => count >= PAGELOAD_PRISMA_DUPLICATE_THRESHOLD)
|
|
732
|
+
.slice(0, 5)
|
|
733
|
+
.forEach(([stmt, count]) => {
|
|
734
|
+
routeIssues.push({
|
|
735
|
+
file: route,
|
|
736
|
+
type: "prisma-duplicate-query",
|
|
737
|
+
severity: "warning",
|
|
738
|
+
message: `Repeated Prisma query (${count}x): ${stmt.slice(0, 160)}...`,
|
|
739
|
+
suggestion: "Cache notification counts and reuse results across components",
|
|
740
|
+
});
|
|
741
|
+
});
|
|
742
|
+
const nullInPatterns = prismaLogSlice.filter((line) => /IN\s*\(NULL\)/i.test(line));
|
|
743
|
+
if (nullInPatterns.length > 0) {
|
|
744
|
+
routeIssues.push({
|
|
745
|
+
file: route,
|
|
746
|
+
type: "prisma-null-in",
|
|
747
|
+
severity: "info",
|
|
748
|
+
message: `Prisma executed ${nullInPatterns.length} queries with IN (NULL)`,
|
|
749
|
+
suggestion: "Guard empty ID lists before querying to avoid wasted queries",
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return routeIssues;
|
|
754
|
+
};
|
|
755
|
+
let browser;
|
|
756
|
+
let context;
|
|
757
|
+
try {
|
|
758
|
+
const { usedExisting, server: started } = await this.ensureServer(serverLogs);
|
|
759
|
+
server = started;
|
|
760
|
+
const loaded = await loadPageRoutes();
|
|
761
|
+
routes = loaded.routes;
|
|
762
|
+
skippedDynamic = loaded.skippedDynamic;
|
|
763
|
+
maxRoutes = loaded.maxRoutes;
|
|
764
|
+
allowPartial = loaded.allowPartial;
|
|
765
|
+
exclude = loaded.exclude;
|
|
766
|
+
browser = await playwright_1.chromium.launch({ headless: true });
|
|
767
|
+
const storageStatePath = process.env.PREFLIGHT_PLAYWRIGHT_STORAGE_STATE;
|
|
768
|
+
context = storageStatePath
|
|
769
|
+
? await browser.newContext({ storageState: storageStatePath })
|
|
770
|
+
: await browser.newContext();
|
|
771
|
+
if (!context) {
|
|
772
|
+
throw new Error("Failed to create Playwright browser context");
|
|
773
|
+
}
|
|
774
|
+
// Augment with sitemap URLs (helps cover dynamic routes without manual examples).
|
|
775
|
+
if (PAGELOAD_INCLUDE_SITEMAP) {
|
|
776
|
+
const sitemapRoutes = await discoverRoutesFromSitemaps();
|
|
777
|
+
if (sitemapRoutes.length > 0) {
|
|
778
|
+
routes = Array.from(new Set([...routes, ...sitemapRoutes]));
|
|
779
|
+
issues.push({
|
|
780
|
+
file: "sitemap",
|
|
781
|
+
type: "sitemap-discovery",
|
|
782
|
+
severity: "info",
|
|
783
|
+
message: `Discovered ${sitemapRoutes.length} routes from sitemap`,
|
|
784
|
+
suggestion: "Disable PREFLIGHT_PAGELOAD_INCLUDE_SITEMAP if you only want filesystem-discovered routes",
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
issues.push({
|
|
789
|
+
file: "sitemap",
|
|
790
|
+
type: "sitemap-discovery",
|
|
791
|
+
severity: "info",
|
|
792
|
+
message: "Sitemap discovery enabled but no sitemap URLs were found",
|
|
793
|
+
suggestion: "Ensure your app serves /sitemap.xml or /sitemap/0.xml in production mode",
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
// Enforce true dynamic coverage when requested.
|
|
798
|
+
if (process.env.PREFLIGHT_PAGELOAD_REQUIRE_DYNAMIC_EXAMPLES === "1" &&
|
|
799
|
+
skippedDynamic.length > 0) {
|
|
800
|
+
const missing = skippedDynamic.filter((tpl) => !routes.some((r) => dynamicTemplateMatchesRoute(tpl, r)));
|
|
801
|
+
if (missing.length > 0) {
|
|
802
|
+
issues.push({
|
|
803
|
+
file: PAGELOAD_ROUTES_CONFIG,
|
|
804
|
+
type: "dynamic-routes-missing-examples",
|
|
805
|
+
severity: "error",
|
|
806
|
+
message: `Dynamic routes require examples but ${missing.length} are still missing coverage: ${missing.slice(0, 10).join(", ")}${missing.length > 10 ? "${chars.ellipsis}" : ""}`,
|
|
807
|
+
suggestion: "Add dynamicExamples entries to the configured routes JSON, or enable PREFLIGHT_PAGELOAD_INCLUDE_SITEMAP to pick up real URLs.",
|
|
808
|
+
});
|
|
809
|
+
return {
|
|
810
|
+
name: "Page Load Budgets",
|
|
811
|
+
passed: false,
|
|
812
|
+
blocking: true,
|
|
813
|
+
issues,
|
|
814
|
+
duration: Date.now() - startTime,
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
// Warm up a freshly started server to avoid penalizing cold-start compilation/caching.
|
|
819
|
+
if (!usedExisting) {
|
|
820
|
+
try {
|
|
821
|
+
await this.capturePageTrace(context, "/");
|
|
822
|
+
}
|
|
823
|
+
catch {
|
|
824
|
+
// ignore warmup failures
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
if (skippedDynamic.length > 0) {
|
|
828
|
+
issues.push({
|
|
829
|
+
file: "app",
|
|
830
|
+
type: "dynamic-routes-skipped",
|
|
831
|
+
severity: "info",
|
|
832
|
+
message: `Skipped ${skippedDynamic.length} dynamic routes (require params): ${skippedDynamic.slice(0, 10).join(", ")}${skippedDynamic.length > 10 ? "${chars.ellipsis}" : ""}`,
|
|
833
|
+
suggestion: "Add explicit examples via list-mode config if you want dynamic route coverage",
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
if (exclude.length > 0) {
|
|
837
|
+
issues.push({
|
|
838
|
+
file: PAGELOAD_ROUTES_CONFIG,
|
|
839
|
+
type: "route-excludes",
|
|
840
|
+
severity: "info",
|
|
841
|
+
message: `Excluded ${exclude.length} patterns from page-load budgets`,
|
|
842
|
+
suggestion: "Remove excludes if you want stricter coverage",
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
if (routes.length > maxRoutes) {
|
|
846
|
+
const msg = `Discovered ${routes.length} routes but maxRoutes is ${maxRoutes}`;
|
|
847
|
+
if (!allowPartial) {
|
|
848
|
+
issues.push({
|
|
849
|
+
file: PAGELOAD_ROUTES_CONFIG,
|
|
850
|
+
type: "too-many-routes",
|
|
851
|
+
severity: "error",
|
|
852
|
+
message: msg,
|
|
853
|
+
suggestion: "Increase maxRoutes or set PREFLIGHT_PAGELOAD_ALLOW_PARTIAL=1 to sample",
|
|
854
|
+
});
|
|
855
|
+
return {
|
|
856
|
+
name: "Page Load Budgets",
|
|
857
|
+
passed: false,
|
|
858
|
+
blocking: true,
|
|
859
|
+
issues,
|
|
860
|
+
duration: Date.now() - startTime,
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
issues.push({
|
|
864
|
+
file: PAGELOAD_ROUTES_CONFIG,
|
|
865
|
+
type: "route-sampling",
|
|
866
|
+
severity: "warning",
|
|
867
|
+
message: `${msg}; sampling first ${maxRoutes}`,
|
|
868
|
+
suggestion: "Increase maxRoutes to cover more pages",
|
|
869
|
+
});
|
|
870
|
+
routes = routes.slice(0, maxRoutes);
|
|
871
|
+
}
|
|
872
|
+
if (PAGELOAD_CONCURRENCY > 1) {
|
|
873
|
+
issues.push({
|
|
874
|
+
file: "page-load",
|
|
875
|
+
type: "concurrency-enabled",
|
|
876
|
+
severity: "info",
|
|
877
|
+
message: `Running page-load budgets with concurrency=${PAGELOAD_CONCURRENCY}`,
|
|
878
|
+
suggestion: "Set PREFLIGHT_PAGELOAD_CONCURRENCY=1 for deterministic per-route Prisma attribution",
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
let completed = 0;
|
|
882
|
+
let nextIndex = 0;
|
|
883
|
+
const runOne = async () => {
|
|
884
|
+
while (nextIndex < routes.length) {
|
|
885
|
+
const index = nextIndex++;
|
|
886
|
+
if (index >= routes.length) {
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
const route = routes[index];
|
|
890
|
+
if (this.verbose) {
|
|
891
|
+
console.log(` āŖļø pageload ${route}`);
|
|
892
|
+
}
|
|
893
|
+
const { requests, navDuration, totalTransferBytes, apiTransferBytes, finalUrl } = await this.capturePageTrace(context, route);
|
|
894
|
+
// When running concurrently we can't reliably slice logs per-route.
|
|
895
|
+
const prismaLogSlice = PAGELOAD_CONCURRENCY > 1
|
|
896
|
+
? []
|
|
897
|
+
: serverLogs.filter((line) => line.includes("prisma:query"));
|
|
898
|
+
let intendedPath;
|
|
899
|
+
let finalPath;
|
|
900
|
+
try {
|
|
901
|
+
intendedPath = new URL(buildUrl(route)).pathname;
|
|
902
|
+
finalPath = new URL(finalUrl).pathname;
|
|
903
|
+
}
|
|
904
|
+
catch {
|
|
905
|
+
// ignore url parse issues
|
|
906
|
+
}
|
|
907
|
+
if (intendedPath && finalPath && intendedPath !== finalPath) {
|
|
908
|
+
issues.push({
|
|
909
|
+
file: route,
|
|
910
|
+
type: "redirect-detected",
|
|
911
|
+
severity: "info",
|
|
912
|
+
message: `Route redirected: ${intendedPath} ${console_chars_1.chars.arrow} ${finalPath}`,
|
|
913
|
+
suggestion: "Provide PREFLIGHT_PLAYWRIGHT_STORAGE_STATE to test authenticated pages (or list-mode config to exclude protected routes)",
|
|
914
|
+
});
|
|
915
|
+
const authRedirect = finalPath === "/login" ||
|
|
916
|
+
finalPath.startsWith("/login/") ||
|
|
917
|
+
finalPath.startsWith("/auth/signin") ||
|
|
918
|
+
finalPath.startsWith("/auth/error") ||
|
|
919
|
+
finalPath === "/forbidden" ||
|
|
920
|
+
finalPath.startsWith("/forbidden/");
|
|
921
|
+
if (authRedirect) {
|
|
922
|
+
continue; // Don't apply budgets to protected routes when unauthenticated.
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
issues.push(...evaluateRoute(route, requests, navDuration, totalTransferBytes, apiTransferBytes, prismaLogSlice));
|
|
926
|
+
if (usedExisting && prismaLogSlice.length === 0) {
|
|
927
|
+
issues.push({
|
|
928
|
+
file: route,
|
|
929
|
+
type: "log-capture",
|
|
930
|
+
severity: "info",
|
|
931
|
+
message: "Prisma logs not captured (dev server already running)",
|
|
932
|
+
suggestion: "Run preflight on a fresh dev server to capture query counts",
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
completed++;
|
|
936
|
+
if (!this.verbose &&
|
|
937
|
+
PAGELOAD_PROGRESS_EVERY > 0 &&
|
|
938
|
+
completed % PAGELOAD_PROGRESS_EVERY === 0) {
|
|
939
|
+
console.log(` pageload progress ${completed}/${routes.length}`);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
const workerCount = Math.min(PAGELOAD_CONCURRENCY, routes.length);
|
|
944
|
+
await Promise.all(Array.from({ length: workerCount }, () => runOne()));
|
|
945
|
+
}
|
|
946
|
+
catch (err) {
|
|
947
|
+
issues.push({
|
|
948
|
+
file: "page-load",
|
|
949
|
+
type: "page-load-check",
|
|
950
|
+
severity: "error",
|
|
951
|
+
message: `Failed to run page load budget: ${err?.message || err}`,
|
|
952
|
+
suggestion: "Ensure pnpm dev can start and Playwright is installed",
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
finally {
|
|
956
|
+
await context?.close().catch(() => undefined);
|
|
957
|
+
await browser?.close().catch(() => undefined);
|
|
958
|
+
stopServer(server);
|
|
959
|
+
}
|
|
960
|
+
const failedIssues = issues.filter((i) => i.severity === "error" || (PAGELOAD_STRICT && i.severity === "warning"));
|
|
961
|
+
return {
|
|
962
|
+
name: "Page Load Budgets",
|
|
963
|
+
passed: failedIssues.length === 0,
|
|
964
|
+
blocking: true,
|
|
965
|
+
issues,
|
|
966
|
+
duration: Date.now() - startTime,
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Bundle Size Check
|
|
971
|
+
*/
|
|
972
|
+
async checkBundleSize() {
|
|
973
|
+
const startTime = Date.now();
|
|
974
|
+
const issues = [];
|
|
975
|
+
// Check for large dependencies
|
|
976
|
+
const largeDeps = [
|
|
977
|
+
{ name: "moment", suggestion: "Use date-fns (90% smaller)" },
|
|
978
|
+
{ name: "lodash", suggestion: "Use lodash-es or individual imports" },
|
|
979
|
+
{ name: "@mui/material", suggestion: "Use @supercatch/ui (98% smaller)" },
|
|
980
|
+
];
|
|
981
|
+
if (fs.existsSync("package.json")) {
|
|
982
|
+
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
|
983
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
984
|
+
for (const { name, suggestion } of largeDeps) {
|
|
985
|
+
if (deps[name]) {
|
|
986
|
+
issues.push({
|
|
987
|
+
file: "package.json",
|
|
988
|
+
type: "large-dep",
|
|
989
|
+
severity: "warning",
|
|
990
|
+
message: `Large dependency: ${name}`,
|
|
991
|
+
suggestion,
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return {
|
|
997
|
+
name: "Bundle Size",
|
|
998
|
+
passed: true,
|
|
999
|
+
blocking: false,
|
|
1000
|
+
issues,
|
|
1001
|
+
duration: Date.now() - startTime,
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Image Optimization Check
|
|
1006
|
+
* Detects:
|
|
1007
|
+
* - Images without width/height (causes CLS)
|
|
1008
|
+
* - Missing loading="lazy" on below-fold images
|
|
1009
|
+
* - Missing priority prop on above-fold/hero/featured images
|
|
1010
|
+
* - ListingShowcaseCard without priority prop in homepage/featured contexts
|
|
1011
|
+
*/
|
|
1012
|
+
async checkImageOptimization() {
|
|
1013
|
+
const startTime = Date.now();
|
|
1014
|
+
const issues = [];
|
|
1015
|
+
const files = await getAppComponentsTsxFiles();
|
|
1016
|
+
for (const file of files) {
|
|
1017
|
+
const content = fs.readFileSync(file, "utf8");
|
|
1018
|
+
const lines = content.split("\n");
|
|
1019
|
+
lines.forEach((line, index) => {
|
|
1020
|
+
// Check for img without width/height
|
|
1021
|
+
if (/<img[^>]*src=/i.test(line) && !/<img[^>]*(?:width|height)=/i.test(line)) {
|
|
1022
|
+
issues.push({
|
|
1023
|
+
file,
|
|
1024
|
+
line: index + 1,
|
|
1025
|
+
type: "img-no-dimensions",
|
|
1026
|
+
severity: "warning",
|
|
1027
|
+
message: "img without width/height (causes CLS)",
|
|
1028
|
+
suggestion: "Add width and height attributes",
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
// Check for img without loading="lazy"
|
|
1032
|
+
if (/<img[^>]*src=/i.test(line) && !line.includes("loading=")) {
|
|
1033
|
+
const context = lines.slice(Math.max(0, index - 5), index).join("\n");
|
|
1034
|
+
if (!context.includes("priority") && !context.includes("above-fold")) {
|
|
1035
|
+
issues.push({
|
|
1036
|
+
file,
|
|
1037
|
+
line: index + 1,
|
|
1038
|
+
type: "img-no-lazy",
|
|
1039
|
+
severity: "info",
|
|
1040
|
+
message: 'img without loading="lazy"',
|
|
1041
|
+
suggestion: 'Add loading="lazy" for below-fold images',
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
// Check for Next.js Image in hero/featured/banner contexts without priority
|
|
1046
|
+
if (/<Image[^>]*src=/i.test(line)) {
|
|
1047
|
+
const contextBefore = lines
|
|
1048
|
+
.slice(Math.max(0, index - 10), index + 1)
|
|
1049
|
+
.join("\n")
|
|
1050
|
+
.toLowerCase();
|
|
1051
|
+
const contextAfter = lines
|
|
1052
|
+
.slice(index, Math.min(lines.length, index + 5))
|
|
1053
|
+
.join("\n")
|
|
1054
|
+
.toLowerCase();
|
|
1055
|
+
const fullContext = contextBefore + contextAfter;
|
|
1056
|
+
const isAboveFold = /hero|banner|featured|showcase|primary|main.*image|first.*card/i.test(fullContext);
|
|
1057
|
+
const hasPriority = /priority/i.test(line) || /priority/i.test(contextAfter);
|
|
1058
|
+
if (isAboveFold && !hasPriority) {
|
|
1059
|
+
issues.push({
|
|
1060
|
+
file,
|
|
1061
|
+
line: index + 1,
|
|
1062
|
+
type: "lcp-no-priority",
|
|
1063
|
+
severity: "warning",
|
|
1064
|
+
message: "Above-fold Image missing priority prop (hurts LCP)",
|
|
1065
|
+
suggestion: "Add priority prop to above-fold images for better LCP",
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
// Check for ListingShowcaseCard in homepage/featured without priority
|
|
1070
|
+
if (/ListingShowcaseCard/i.test(line) && !line.includes("priority")) {
|
|
1071
|
+
const contextBefore = lines
|
|
1072
|
+
.slice(Math.max(0, index - 15), index)
|
|
1073
|
+
.join("\n")
|
|
1074
|
+
.toLowerCase();
|
|
1075
|
+
const isHomepageOrFeatured = /featured|homepage|hero|showcase|first/i.test(contextBefore) ||
|
|
1076
|
+
file.includes("page.tsx") ||
|
|
1077
|
+
file.includes("HomePageClient");
|
|
1078
|
+
if (isHomepageOrFeatured) {
|
|
1079
|
+
issues.push({
|
|
1080
|
+
file,
|
|
1081
|
+
line: index + 1,
|
|
1082
|
+
type: "showcase-no-priority",
|
|
1083
|
+
severity: "info",
|
|
1084
|
+
message: "ListingShowcaseCard in featured context without priority prop",
|
|
1085
|
+
suggestion: "Add priority={true} for first few visible cards to improve LCP",
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
return {
|
|
1092
|
+
name: "Image Optimization",
|
|
1093
|
+
passed: true,
|
|
1094
|
+
blocking: false,
|
|
1095
|
+
issues,
|
|
1096
|
+
duration: Date.now() - startTime,
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Lazy Loading Check
|
|
1101
|
+
*/
|
|
1102
|
+
async checkLazyLoading() {
|
|
1103
|
+
const startTime = Date.now();
|
|
1104
|
+
const issues = [];
|
|
1105
|
+
const files = await getAppComponentsTsxFiles();
|
|
1106
|
+
const heavyComponents = ["Chart", "Editor", "Map", "Calendar", "DataGrid", "RichText"];
|
|
1107
|
+
for (const file of files) {
|
|
1108
|
+
const content = fs.readFileSync(file, "utf8");
|
|
1109
|
+
for (const component of heavyComponents) {
|
|
1110
|
+
const staticImport = new RegExp(`import\\s+.*${component}.*from`);
|
|
1111
|
+
if (staticImport.test(content) && !content.includes("dynamic(")) {
|
|
1112
|
+
const lineNumber = content.split("\n").findIndex((l) => staticImport.test(l)) + 1;
|
|
1113
|
+
issues.push({
|
|
1114
|
+
file,
|
|
1115
|
+
line: lineNumber,
|
|
1116
|
+
type: "static-heavy-import",
|
|
1117
|
+
severity: "info",
|
|
1118
|
+
message: `Heavy component ${component} imported statically`,
|
|
1119
|
+
suggestion: `Use dynamic(() => import('...'))`,
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
return {
|
|
1125
|
+
name: "Lazy Loading",
|
|
1126
|
+
passed: true,
|
|
1127
|
+
blocking: false,
|
|
1128
|
+
issues,
|
|
1129
|
+
duration: Date.now() - startTime,
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Skeleton Loader Usage Check
|
|
1134
|
+
*/
|
|
1135
|
+
async checkSkeletonLoaders() {
|
|
1136
|
+
const startTime = Date.now();
|
|
1137
|
+
const issues = [];
|
|
1138
|
+
const files = await getAppComponentsTsxFiles();
|
|
1139
|
+
for (const file of files) {
|
|
1140
|
+
const content = fs.readFileSync(file, "utf8");
|
|
1141
|
+
// Check for loading states without skeleton
|
|
1142
|
+
if (content.includes("isLoading") || content.includes("loading")) {
|
|
1143
|
+
if (content.includes("Loading...") || content.includes('"Loading"')) {
|
|
1144
|
+
const lineNumber = content.split("\n").findIndex((l) => /Loading\.\.\./.test(l) || /"Loading"/.test(l)) +
|
|
1145
|
+
1;
|
|
1146
|
+
issues.push({
|
|
1147
|
+
file,
|
|
1148
|
+
line: lineNumber,
|
|
1149
|
+
type: "text-loading",
|
|
1150
|
+
severity: "info",
|
|
1151
|
+
message: "Text-based loading indicator",
|
|
1152
|
+
suggestion: "Use SkeletonLoader for better UX",
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
return {
|
|
1158
|
+
name: "Skeleton Loaders",
|
|
1159
|
+
passed: true,
|
|
1160
|
+
blocking: false,
|
|
1161
|
+
issues,
|
|
1162
|
+
duration: Date.now() - startTime,
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Virtual Scroll Candidates Check
|
|
1167
|
+
*/
|
|
1168
|
+
async checkVirtualScroll() {
|
|
1169
|
+
const startTime = Date.now();
|
|
1170
|
+
const issues = [];
|
|
1171
|
+
const files = await getAppComponentsTsxFiles();
|
|
1172
|
+
for (const file of files) {
|
|
1173
|
+
const content = fs.readFileSync(file, "utf8");
|
|
1174
|
+
const lines = content.split("\n");
|
|
1175
|
+
lines.forEach((line, index) => {
|
|
1176
|
+
// Check for .map() on potentially large arrays
|
|
1177
|
+
if (/\.(map|forEach)\s*\(\s*\([^)]*\)\s*=>/i.test(line)) {
|
|
1178
|
+
const context = lines.slice(Math.max(0, index - 10), index + 1).join("\n");
|
|
1179
|
+
if (/items|products|listings|results|data/i.test(context) &&
|
|
1180
|
+
!context.includes("slice(") &&
|
|
1181
|
+
!context.includes("VirtualList")) {
|
|
1182
|
+
// Only flag if it looks like a large list render
|
|
1183
|
+
if (context.includes("return") &&
|
|
1184
|
+
(context.includes("<li") || context.includes("<div") || context.includes("<Card"))) {
|
|
1185
|
+
issues.push({
|
|
1186
|
+
file,
|
|
1187
|
+
line: index + 1,
|
|
1188
|
+
type: "large-list",
|
|
1189
|
+
severity: "info",
|
|
1190
|
+
message: "Potentially large list without virtualization",
|
|
1191
|
+
suggestion: "Consider VirtualTable or VirtualList from @/components/shared",
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
return {
|
|
1199
|
+
name: "Virtual Scroll Candidates",
|
|
1200
|
+
passed: true,
|
|
1201
|
+
blocking: false,
|
|
1202
|
+
issues,
|
|
1203
|
+
duration: Date.now() - startTime,
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Z-Index Token Usage Check
|
|
1208
|
+
*/
|
|
1209
|
+
async checkZIndexTokens() {
|
|
1210
|
+
const startTime = Date.now();
|
|
1211
|
+
const issues = [];
|
|
1212
|
+
const files = await getAppComponentsTsxFiles();
|
|
1213
|
+
for (const file of files) {
|
|
1214
|
+
const content = fs.readFileSync(file, "utf8");
|
|
1215
|
+
const lines = content.split("\n");
|
|
1216
|
+
lines.forEach((line, index) => {
|
|
1217
|
+
// Skip comments
|
|
1218
|
+
if (line.trim().startsWith("//") || line.trim().startsWith("*"))
|
|
1219
|
+
return;
|
|
1220
|
+
// Check for hardcoded z-index values >= 100
|
|
1221
|
+
const zIndexMatch = line.match(/zIndex:\s*(\d+)/);
|
|
1222
|
+
if (zIndexMatch) {
|
|
1223
|
+
const value = parseInt(zIndexMatch[1], 10);
|
|
1224
|
+
if (value >= 100) {
|
|
1225
|
+
issues.push({
|
|
1226
|
+
file,
|
|
1227
|
+
line: index + 1,
|
|
1228
|
+
type: "hardcoded-zindex",
|
|
1229
|
+
severity: "warning",
|
|
1230
|
+
message: `Hardcoded z-index: ${value}`,
|
|
1231
|
+
suggestion: "Use design tokens: var(--z-modal), var(--z-dropdown), var(--z-sticky), etc.",
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
return {
|
|
1238
|
+
name: "Z-Index Tokens",
|
|
1239
|
+
passed: issues.length === 0,
|
|
1240
|
+
blocking: false,
|
|
1241
|
+
issues,
|
|
1242
|
+
duration: Date.now() - startTime,
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Recharts Lazy Loading Check
|
|
1247
|
+
*/
|
|
1248
|
+
async checkRechartsLazyLoading() {
|
|
1249
|
+
const startTime = Date.now();
|
|
1250
|
+
const issues = [];
|
|
1251
|
+
const files = await getAppComponentsTsxFiles();
|
|
1252
|
+
for (const file of files) {
|
|
1253
|
+
const content = fs.readFileSync(file, "utf8");
|
|
1254
|
+
// Check for static recharts imports
|
|
1255
|
+
const rechartsImport = /import\s+\{[^}]+\}\s+from\s+["']recharts["']/;
|
|
1256
|
+
if (rechartsImport.test(content)) {
|
|
1257
|
+
// Check if it's using dynamic import
|
|
1258
|
+
if (!content.includes("dynamic(") || !content.includes("recharts")) {
|
|
1259
|
+
const lineNumber = content.split("\n").findIndex((l) => rechartsImport.test(l)) + 1;
|
|
1260
|
+
issues.push({
|
|
1261
|
+
file,
|
|
1262
|
+
line: lineNumber,
|
|
1263
|
+
type: "static-recharts",
|
|
1264
|
+
severity: "warning",
|
|
1265
|
+
message: "Static recharts import (~200KB)",
|
|
1266
|
+
suggestion: 'Use dynamic(() => import("recharts").then(mod => mod.Component), { ssr: false })',
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
return {
|
|
1272
|
+
name: "Recharts Lazy Loading",
|
|
1273
|
+
passed: issues.length === 0,
|
|
1274
|
+
blocking: false,
|
|
1275
|
+
issues,
|
|
1276
|
+
duration: Date.now() - startTime,
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Dialog Lazy Loading Check
|
|
1281
|
+
*/
|
|
1282
|
+
async checkDialogLazyLoading() {
|
|
1283
|
+
const startTime = Date.now();
|
|
1284
|
+
const issues = [];
|
|
1285
|
+
const files = await getAppTsxFiles();
|
|
1286
|
+
for (const file of files) {
|
|
1287
|
+
const content = fs.readFileSync(file, "utf8");
|
|
1288
|
+
// Check for static dialog imports from local components
|
|
1289
|
+
const dialogImport = /import\s+\{[^}]*Dialog[^}]*\}\s+from\s+["']\.\/components/;
|
|
1290
|
+
if (dialogImport.test(content)) {
|
|
1291
|
+
const lineNumber = content.split("\n").findIndex((l) => dialogImport.test(l)) + 1;
|
|
1292
|
+
issues.push({
|
|
1293
|
+
file,
|
|
1294
|
+
line: lineNumber,
|
|
1295
|
+
type: "static-dialog",
|
|
1296
|
+
severity: "info",
|
|
1297
|
+
message: "Static dialog import - consider lazy loading",
|
|
1298
|
+
suggestion: "Use DynamicXxxDialog from @/lib/dynamic-imports for dialogs that are not immediately visible",
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
return {
|
|
1303
|
+
name: "Dialog Lazy Loading",
|
|
1304
|
+
passed: true,
|
|
1305
|
+
blocking: false,
|
|
1306
|
+
issues,
|
|
1307
|
+
duration: Date.now() - startTime,
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Database Query Efficiency Check
|
|
1312
|
+
* Detects N+1 query patterns and suggests Prisma include
|
|
1313
|
+
*/
|
|
1314
|
+
async checkDatabaseQueryEfficiency() {
|
|
1315
|
+
const startTime = Date.now();
|
|
1316
|
+
const issues = [];
|
|
1317
|
+
const files = await getAppLibFiles();
|
|
1318
|
+
for (const file of files) {
|
|
1319
|
+
const content = fs.readFileSync(file, "utf8");
|
|
1320
|
+
const lines = content.split("\n");
|
|
1321
|
+
// Check for N+1 pattern: findMany followed by separate queries for related data
|
|
1322
|
+
const hasFindMany = /prisma\.\w+\.findMany/.test(content);
|
|
1323
|
+
const hasManualStitching = /\.map\s*\(\s*\([^)]*\)\s*=>\s*[^)]*\.get\s*\(/.test(content);
|
|
1324
|
+
const hasParallelQueries = /Promise\.all\s*\(\s*\[[\s\S]*?prisma\.\w+\.findMany[\s\S]*?\]\s*\)/.test(content);
|
|
1325
|
+
if (hasFindMany && hasManualStitching && hasParallelQueries) {
|
|
1326
|
+
// Find the line with findMany
|
|
1327
|
+
const lineNumber = lines.findIndex((l) => /prisma\.\w+\.findMany/.test(l)) + 1;
|
|
1328
|
+
issues.push({
|
|
1329
|
+
file,
|
|
1330
|
+
line: lineNumber,
|
|
1331
|
+
type: "n-plus-1-pattern",
|
|
1332
|
+
severity: "warning",
|
|
1333
|
+
message: "Potential N+1 query pattern detected (findMany + separate relation queries)",
|
|
1334
|
+
suggestion: "Use Prisma include: { relatedModel: true } for single optimized query",
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
// Check for force-dynamic without caching
|
|
1338
|
+
if (content.includes("dynamic = 'force-dynamic'") ||
|
|
1339
|
+
content.includes('dynamic = "force-dynamic"')) {
|
|
1340
|
+
const hasUnstableCache = content.includes("unstable_cache");
|
|
1341
|
+
const lineNumber = lines.findIndex((l) => /dynamic\s*=\s*['"]force-dynamic['"]/.test(l)) + 1;
|
|
1342
|
+
// Skip layout.tsx which has a documented reason
|
|
1343
|
+
if (!file.includes("layout.tsx") && !hasUnstableCache) {
|
|
1344
|
+
issues.push({
|
|
1345
|
+
file,
|
|
1346
|
+
line: lineNumber,
|
|
1347
|
+
type: "force-dynamic-no-cache",
|
|
1348
|
+
severity: "warning",
|
|
1349
|
+
message: "force-dynamic without unstable_cache disables all caching",
|
|
1350
|
+
suggestion: "Remove force-dynamic and use unstable_cache for data fetching, or add caching if dynamic is required",
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
return {
|
|
1356
|
+
name: "Database Query Efficiency",
|
|
1357
|
+
passed: issues.length === 0,
|
|
1358
|
+
blocking: false,
|
|
1359
|
+
issues,
|
|
1360
|
+
duration: Date.now() - startTime,
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
async runAll() {
|
|
1364
|
+
const startTime = Date.now();
|
|
1365
|
+
console.log(`\n${console_chars_1.emoji.lightning} CONSOLIDATED PERFORMANCE PREFLIGHT`);
|
|
1366
|
+
console.log((0, console_chars_1.createDivider)(80, "heavy"));
|
|
1367
|
+
const checkPromises = [
|
|
1368
|
+
this.checkBundleSize(),
|
|
1369
|
+
this.checkImageOptimization(),
|
|
1370
|
+
this.checkLazyLoading(),
|
|
1371
|
+
this.checkSkeletonLoaders(),
|
|
1372
|
+
this.checkVirtualScroll(),
|
|
1373
|
+
this.checkZIndexTokens(),
|
|
1374
|
+
this.checkRechartsLazyLoading(),
|
|
1375
|
+
this.checkDialogLazyLoading(),
|
|
1376
|
+
this.checkDatabaseQueryEfficiency(),
|
|
1377
|
+
];
|
|
1378
|
+
if (process.env.PREFLIGHT_ENABLE_PAGELOAD_BUDGETS === "1") {
|
|
1379
|
+
checkPromises.push(this.checkPageLoadBudgets());
|
|
1380
|
+
}
|
|
1381
|
+
const checks = await Promise.all(checkPromises);
|
|
1382
|
+
const totalDuration = Date.now() - startTime;
|
|
1383
|
+
const allIssues = checks.flatMap((c) => c.issues);
|
|
1384
|
+
const hasBlockingFailure = checks.some((c) => !c.passed && c.blocking);
|
|
1385
|
+
const summary = {
|
|
1386
|
+
total: checks.length,
|
|
1387
|
+
passed: checks.filter((c) => c.passed).length,
|
|
1388
|
+
failed: checks.filter((c) => !c.passed).length,
|
|
1389
|
+
errors: allIssues.filter((i) => i.severity === "error").length,
|
|
1390
|
+
warnings: allIssues.filter((i) => i.severity === "warning").length,
|
|
1391
|
+
};
|
|
1392
|
+
this.printResults(checks, totalDuration, summary, hasBlockingFailure);
|
|
1393
|
+
return {
|
|
1394
|
+
module: "performance",
|
|
1395
|
+
passed: !hasBlockingFailure,
|
|
1396
|
+
totalDuration,
|
|
1397
|
+
checks,
|
|
1398
|
+
summary,
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
printResults(checks, totalDuration, summary, hasBlockingFailure) {
|
|
1402
|
+
console.log("\n" + (0, console_chars_1.createDivider)(80, "heavy"));
|
|
1403
|
+
console.log(`${console_chars_1.emoji.chart} PERFORMANCE RESULTS`);
|
|
1404
|
+
console.log((0, console_chars_1.createDivider)(80, "heavy"));
|
|
1405
|
+
const allIssues = checks.flatMap((c) => c.issues);
|
|
1406
|
+
checks.forEach((check) => {
|
|
1407
|
+
const icon = check.passed
|
|
1408
|
+
? `${console_chars_1.emoji.success}`
|
|
1409
|
+
: check.blocking
|
|
1410
|
+
? `${console_chars_1.emoji.error}`
|
|
1411
|
+
: `${console_chars_1.emoji.warning}`;
|
|
1412
|
+
const count = check.issues.length > 0 ? ` (${check.issues.length} issues)` : "";
|
|
1413
|
+
console.log(`${icon} ${check.name.padEnd(30)} ${(check.duration / 1000).toFixed(1)}s${count}`);
|
|
1414
|
+
});
|
|
1415
|
+
console.log((0, console_chars_1.createDivider)(80, "heavy"));
|
|
1416
|
+
console.log(`Warnings: ${summary.warnings} | Info: ${allIssues.filter((i) => i.severity === "info").length} | Time: ${(totalDuration / 1000).toFixed(1)}s`);
|
|
1417
|
+
const shouldPrintDetails = hasBlockingFailure || this.verbose || process.env.PREFLIGHT_VERBOSE === "1";
|
|
1418
|
+
if (shouldPrintDetails) {
|
|
1419
|
+
console.log("\nš Details (top findings)");
|
|
1420
|
+
console.log((0, console_chars_1.createDivider)(80, "heavy"));
|
|
1421
|
+
for (const check of checks) {
|
|
1422
|
+
if (check.issues.length === 0)
|
|
1423
|
+
continue;
|
|
1424
|
+
console.log(`\n${console_chars_1.chars.bullet} ${check.name} (${check.issues.length})`);
|
|
1425
|
+
// Special formatting for page routes
|
|
1426
|
+
if (check.name === "Page Load Budgets") {
|
|
1427
|
+
const byRoute = new Map();
|
|
1428
|
+
for (const issue of check.issues) {
|
|
1429
|
+
const key = issue.file || "unknown";
|
|
1430
|
+
if (!byRoute.has(key))
|
|
1431
|
+
byRoute.set(key, []);
|
|
1432
|
+
byRoute.get(key).push(issue);
|
|
1433
|
+
}
|
|
1434
|
+
const sorted = Array.from(byRoute.entries()).sort((a, b) => b[1].filter((i) => i.severity !== "info").length -
|
|
1435
|
+
a[1].filter((i) => i.severity !== "info").length);
|
|
1436
|
+
for (const [route, routeIssues] of sorted.slice(0, 15)) {
|
|
1437
|
+
const failing = routeIssues.filter((i) => i.severity !== "info");
|
|
1438
|
+
const info = routeIssues.filter((i) => i.severity === "info");
|
|
1439
|
+
const label = failing.length > 0 ? `${console_chars_1.emoji.error}` : `${console_chars_1.emoji.info}`;
|
|
1440
|
+
console.log(` ${label} ${route} (${failing.length} blocking/warn, ${info.length} info)`);
|
|
1441
|
+
routeIssues.slice(0, 6).forEach((i) => {
|
|
1442
|
+
const sev = i.severity === "error" ? "ERROR" : i.severity === "warning" ? "WARN" : "INFO";
|
|
1443
|
+
console.log(` - [${sev}] ${i.message}`);
|
|
1444
|
+
});
|
|
1445
|
+
if (routeIssues.length > 6)
|
|
1446
|
+
console.log(` - ... +${routeIssues.length - 6} more`);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
else {
|
|
1450
|
+
check.issues.slice(0, 15).forEach((i) => {
|
|
1451
|
+
const sev = i.severity === "error" ? "ERROR" : i.severity === "warning" ? "WARN" : "INFO";
|
|
1452
|
+
console.log(` - [${sev}] ${i.file}${i.line ? `:${i.line}` : ""} ${i.message}`);
|
|
1453
|
+
});
|
|
1454
|
+
if (check.issues.length > 15)
|
|
1455
|
+
console.log(` - ... +${check.issues.length - 15} more`);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
if (hasBlockingFailure) {
|
|
1460
|
+
console.log(`\n${console_chars_1.emoji.error} PERFORMANCE FAILED (blocking)`);
|
|
1461
|
+
}
|
|
1462
|
+
else if (summary.warnings > 0) {
|
|
1463
|
+
console.log(`\n${console_chars_1.emoji.warning} PERFORMANCE PASSED WITH WARNINGS`);
|
|
1464
|
+
}
|
|
1465
|
+
else {
|
|
1466
|
+
console.log(`\n${console_chars_1.emoji.success} PERFORMANCE PASSED`);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
exports.PerformancePreflightModule = PerformancePreflightModule;
|
|
1471
|
+
async function main() {
|
|
1472
|
+
const reporter = (0, universal_progress_reporter_1.createUniversalProgressReporter)(path.basename(__filename, ".ts"));
|
|
1473
|
+
const args = process.argv.slice(2);
|
|
1474
|
+
const mode = args[0] || "all";
|
|
1475
|
+
const verbose = args.includes("--verbose") || args.includes("-v");
|
|
1476
|
+
const parallel = args.includes("--parallel") || args.includes("-p");
|
|
1477
|
+
const module = new PerformancePreflightModule({ verbose, parallel });
|
|
1478
|
+
const runSingle = async (name, fn) => {
|
|
1479
|
+
const check = await fn();
|
|
1480
|
+
console.log(`\n${check.passed ? "${emoji.success}" : "${emoji.warning}"} ${check.name}: ${check.issues.length} issues`);
|
|
1481
|
+
if (check.issues.length > 0) {
|
|
1482
|
+
const shouldPrintMore = !check.passed || check.blocking || verbose || process.env.PREFLIGHT_VERBOSE === "1";
|
|
1483
|
+
const limit = shouldPrintMore ? 30 : 5;
|
|
1484
|
+
check.issues
|
|
1485
|
+
.slice(0, limit)
|
|
1486
|
+
.forEach((i) => console.log(` ${i.file}${i.line ? `:${i.line}` : ""} - ${i.message}`));
|
|
1487
|
+
if (check.issues.length > limit)
|
|
1488
|
+
console.log(` ... and ${check.issues.length - limit} more`);
|
|
1489
|
+
}
|
|
1490
|
+
const hasBlockingFailure = !check.passed && check.blocking;
|
|
1491
|
+
return {
|
|
1492
|
+
module: `performance:${name}`,
|
|
1493
|
+
passed: !hasBlockingFailure,
|
|
1494
|
+
totalDuration: check.duration,
|
|
1495
|
+
checks: [check],
|
|
1496
|
+
summary: {
|
|
1497
|
+
total: 1,
|
|
1498
|
+
passed: check.passed ? 1 : 0,
|
|
1499
|
+
failed: check.passed ? 0 : 1,
|
|
1500
|
+
errors: check.issues.filter((i) => i.severity === "error").length,
|
|
1501
|
+
warnings: check.issues.filter((i) => i.severity === "warning").length,
|
|
1502
|
+
},
|
|
1503
|
+
};
|
|
1504
|
+
};
|
|
1505
|
+
let result;
|
|
1506
|
+
switch (mode) {
|
|
1507
|
+
case "bundle":
|
|
1508
|
+
result = await runSingle("bundle", () => module.checkBundleSize());
|
|
1509
|
+
break;
|
|
1510
|
+
case "images":
|
|
1511
|
+
result = await runSingle("images", () => module.checkImageOptimization());
|
|
1512
|
+
break;
|
|
1513
|
+
case "lazy":
|
|
1514
|
+
result = await runSingle("lazy", () => module.checkLazyLoading());
|
|
1515
|
+
break;
|
|
1516
|
+
case "skeleton":
|
|
1517
|
+
result = await runSingle("skeleton", () => module.checkSkeletonLoaders());
|
|
1518
|
+
break;
|
|
1519
|
+
case "virtual":
|
|
1520
|
+
result = await runSingle("virtual", () => module.checkVirtualScroll());
|
|
1521
|
+
break;
|
|
1522
|
+
case "zindex":
|
|
1523
|
+
result = await runSingle("zindex", () => module.checkZIndexTokens());
|
|
1524
|
+
break;
|
|
1525
|
+
case "recharts":
|
|
1526
|
+
result = await runSingle("recharts", () => module.checkRechartsLazyLoading());
|
|
1527
|
+
break;
|
|
1528
|
+
case "dialogs":
|
|
1529
|
+
result = await runSingle("dialogs", () => module.checkDialogLazyLoading());
|
|
1530
|
+
break;
|
|
1531
|
+
case "queries":
|
|
1532
|
+
result = await runSingle("queries", () => module.checkDatabaseQueryEfficiency());
|
|
1533
|
+
break;
|
|
1534
|
+
case "pageload":
|
|
1535
|
+
result = await runSingle("pageload", () => module.checkPageLoadBudgets());
|
|
1536
|
+
break;
|
|
1537
|
+
default:
|
|
1538
|
+
result = await module.runAll();
|
|
1539
|
+
break;
|
|
1540
|
+
}
|
|
1541
|
+
process.exit(result.passed ? 0 : 1);
|
|
1542
|
+
}
|
|
1543
|
+
if (require.main === module) {
|
|
1544
|
+
main();
|
|
1545
|
+
}
|
|
1546
|
+
//# sourceMappingURL=performance.js.map
|