@nextsparkjs/core 0.1.0-beta.189 → 0.1.0-beta.190
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/components/api/keys/CreateApiKeyDialog.d.ts.map +1 -1
- package/dist/components/api/keys/CreateApiKeyDialog.js +2 -1
- package/dist/components/app/layouts/PublicFooter.d.ts.map +1 -1
- package/dist/components/app/layouts/PublicFooter.js +2 -23
- package/dist/components/app/misc/ThemeToggle.d.ts.map +1 -1
- package/dist/components/app/misc/ThemeToggle.js +2 -1
- package/dist/components/auth/forms/LoginForm.js +1 -1
- package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
- package/dist/components/auth/forms/SignupForm.js +2 -1
- package/dist/components/auth/pages/AuthErrorPage.d.ts.map +1 -1
- package/dist/components/auth/pages/AuthErrorPage.js +2 -1
- package/dist/components/billing/ManageBillingButton.d.ts.map +1 -1
- package/dist/components/billing/ManageBillingButton.js +2 -1
- package/dist/components/dashboard/block-editor/array-field.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/array-field.js +2 -1
- package/dist/components/dashboard/block-editor/block-picker.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/block-picker.js +2 -1
- package/dist/components/dashboard/block-editor/builder-editor-view.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/builder-editor-view.js +3 -2
- package/dist/components/dashboard/block-editor/config-panel.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/config-panel.js +3 -2
- package/dist/components/dashboard/block-editor/dynamic-form.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/dynamic-form.js +2 -1
- package/dist/components/dashboard/block-editor/entity-fields-sidebar.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/entity-fields-sidebar.js +3 -2
- package/dist/components/dashboard/block-editor/pattern-reference-preview.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/pattern-reference-preview.js +2 -1
- package/dist/components/dashboard/layouts/TopNavbar.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/TopNavbar.js +4 -3
- package/dist/components/dashboard/misc/SearchDropdown.d.ts.map +1 -1
- package/dist/components/dashboard/misc/SearchDropdown.js +2 -1
- package/dist/components/dashboard/mobile/MobileTopBar.d.ts.map +1 -1
- package/dist/components/dashboard/mobile/MobileTopBar.js +2 -1
- package/dist/components/devtools/BlockDetailViewer.d.ts.map +1 -1
- package/dist/components/devtools/BlockDetailViewer.js +2 -1
- package/dist/components/devtools/BlocksViewer.d.ts.map +1 -1
- package/dist/components/devtools/BlocksViewer.js +2 -1
- package/dist/components/devtools/ConfigViewer.d.ts.map +1 -1
- package/dist/components/devtools/ConfigViewer.js +3 -2
- package/dist/components/devtools/TestCasesViewer.d.ts.map +1 -1
- package/dist/components/devtools/TestCasesViewer.js +3 -2
- package/dist/components/devtools/TestCoverageDashboard.d.ts.map +1 -1
- package/dist/components/devtools/TestCoverageDashboard.js +2 -1
- package/dist/components/devtools/api-explorer/ApiDocsModal.d.ts.map +1 -1
- package/dist/components/devtools/api-explorer/ApiDocsModal.js +0 -0
- package/dist/components/devtools/api-explorer/ApiExplorer.d.ts.map +1 -1
- package/dist/components/devtools/api-explorer/ApiExplorer.js +2 -1
- package/dist/components/devtools/scheduled-actions/actions-table.d.ts.map +1 -1
- package/dist/components/devtools/scheduled-actions/actions-table.js +3 -2
- package/dist/components/docs/docs-sidebar.d.ts.map +1 -1
- package/dist/components/docs/docs-sidebar.js +3 -2
- package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
- package/dist/components/entities/EntityFieldRenderer.js +2 -1
- package/dist/components/entities/EntityList.d.ts.map +1 -1
- package/dist/components/entities/EntityList.js +2 -1
- package/dist/components/entities/EntityTable.d.ts.map +1 -1
- package/dist/components/entities/EntityTable.js +3 -2
- package/dist/components/entities/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/entities/ErrorBoundary.js +2 -1
- package/dist/components/entities/wrappers/EntityDetailWrapper.d.ts.map +1 -1
- package/dist/components/entities/wrappers/EntityDetailWrapper.js +3 -2
- package/dist/components/media/MediaCard.d.ts.map +1 -1
- package/dist/components/media/MediaCard.js +2 -1
- package/dist/components/media/MediaDetailPanel.d.ts.map +1 -1
- package/dist/components/media/MediaDetailPanel.js +2 -1
- package/dist/components/media/MediaList.d.ts.map +1 -1
- package/dist/components/media/MediaList.js +2 -1
- package/dist/components/media/MediaSelector.d.ts.map +1 -1
- package/dist/components/media/MediaSelector.js +2 -1
- package/dist/components/media/MediaUploadZone.d.ts.map +1 -1
- package/dist/components/media/MediaUploadZone.js +2 -1
- package/dist/components/public/entities/PublicEntityDetail.d.ts.map +1 -1
- package/dist/components/public/entities/PublicEntityDetail.js +2 -1
- package/dist/components/teams/TeamPendingInvitations.d.ts.map +1 -1
- package/dist/components/teams/TeamPendingInvitations.js +3 -2
- package/dist/components/ui/accordion.js +1 -1
- package/dist/components/ui/audio-upload.d.ts.map +1 -1
- package/dist/components/ui/audio-upload.js +2 -1
- package/dist/components/ui/avatar.d.ts +17 -1
- package/dist/components/ui/avatar.d.ts.map +1 -1
- package/dist/components/ui/avatar.js +29 -2
- package/dist/components/ui/badge.js +1 -1
- package/dist/components/ui/breadcrumb.d.ts +13 -0
- package/dist/components/ui/breadcrumb.d.ts.map +1 -1
- package/dist/components/ui/breadcrumb.js +23 -10
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/card.js +1 -1
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/file-upload.d.ts.map +1 -1
- package/dist/components/ui/file-upload.js +2 -1
- package/dist/components/ui/image-upload.d.ts.map +1 -1
- package/dist/components/ui/image-upload.js +4 -3
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/label.js +1 -1
- package/dist/components/ui/optimized-image.d.ts.map +1 -1
- package/dist/components/ui/optimized-image.js +5 -0
- package/dist/components/ui/pagination.d.ts +1 -1
- package/dist/components/ui/pagination.d.ts.map +1 -1
- package/dist/components/ui/pagination.js +3 -0
- package/dist/components/ui/progress.js +1 -1
- package/dist/components/ui/relation-display.d.ts.map +1 -1
- package/dist/components/ui/relation-display.js +3 -2
- package/dist/components/ui/separator.js +1 -1
- package/dist/components/ui/simple-relation-select.d.ts.map +1 -1
- package/dist/components/ui/simple-relation-select.js +7 -6
- package/dist/components/ui/skeleton.js +1 -1
- package/dist/components/ui/slider.js +1 -1
- package/dist/components/ui/switch.js +1 -1
- package/dist/components/ui/tabs.js +1 -1
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/user-display.d.ts.map +1 -1
- package/dist/components/ui/user-display.js +2 -1
- package/dist/components/ui/user-select.d.ts.map +1 -1
- package/dist/components/ui/user-select.js +2 -1
- package/dist/components/ui/video-upload.d.ts.map +1 -1
- package/dist/components/ui/video-upload.js +3 -2
- package/dist/components/users/tables/UsersTable.d.ts.map +1 -1
- package/dist/components/users/tables/UsersTable.js +3 -2
- package/dist/contexts/SubscriptionContext.d.ts.map +1 -1
- package/dist/contexts/SubscriptionContext.js +4 -3
- package/dist/contexts/TeamContext.d.ts.map +1 -1
- package/dist/contexts/TeamContext.js +3 -2
- package/dist/hooks/useAccountPreferencesSync.d.ts.map +1 -1
- package/dist/hooks/useAccountPreferencesSync.js +2 -1
- package/dist/hooks/useApiExplorerNavigation.d.ts.map +1 -1
- package/dist/hooks/useApiExplorerNavigation.js +2 -1
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +3 -2
- package/dist/hooks/useEnsureUserMetadata.d.ts.map +1 -1
- package/dist/hooks/useEnsureUserMetadata.js +3 -2
- package/dist/hooks/useEntityWithMeta.d.ts.map +1 -1
- package/dist/hooks/useEntityWithMeta.js +3 -2
- package/dist/hooks/useInvoice.d.ts.map +1 -1
- package/dist/hooks/useInvoice.js +2 -1
- package/dist/hooks/useInvoices.d.ts.map +1 -1
- package/dist/hooks/useInvoices.js +2 -1
- package/dist/hooks/useMedia.d.ts.map +1 -1
- package/dist/hooks/useMedia.js +10 -9
- package/dist/hooks/useMediaUpload.d.ts.map +1 -1
- package/dist/hooks/useMediaUpload.js +2 -1
- package/dist/hooks/useMembership.d.ts.map +1 -1
- package/dist/hooks/useMembership.js +3 -2
- package/dist/hooks/usePatternUsages.d.ts.map +1 -1
- package/dist/hooks/usePatternUsages.js +3 -2
- package/dist/hooks/usePrefetch.d.ts.map +1 -1
- package/dist/hooks/usePrefetch.js +3 -2
- package/dist/hooks/useQuota.d.ts.map +1 -1
- package/dist/hooks/useQuota.js +2 -1
- package/dist/hooks/useTeamInvitations.d.ts.map +1 -1
- package/dist/hooks/useTeamInvitations.js +4 -3
- package/dist/hooks/useTeamMembers.d.ts.map +1 -1
- package/dist/hooks/useTeamMembers.js +5 -4
- package/dist/hooks/useTeamUpdate.d.ts.map +1 -1
- package/dist/hooks/useTeamUpdate.js +2 -1
- package/dist/hooks/useUserProfile.d.ts.map +1 -1
- package/dist/hooks/useUserProfile.js +3 -2
- package/dist/hooks/useUserSettings.d.ts.map +1 -1
- package/dist/hooks/useUserSettings.js +3 -2
- package/dist/lib/actions/entity.actions.d.ts.map +1 -1
- package/dist/lib/actions/entity.actions.js +2 -1
- package/dist/lib/api/entities.d.ts.map +1 -1
- package/dist/lib/api/entities.js +4 -3
- package/dist/lib/auth/verify-email-link.d.ts +10 -0
- package/dist/lib/auth/verify-email-link.d.ts.map +1 -0
- package/dist/lib/auth/verify-email-link.js +14 -0
- package/dist/lib/auth-client.d.ts +2 -0
- package/dist/lib/auth-client.d.ts.map +1 -1
- package/dist/lib/auth-client.js +4 -0
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +10 -2
- package/dist/lib/base-path.d.ts +85 -0
- package/dist/lib/base-path.d.ts.map +1 -0
- package/dist/lib/base-path.js +79 -0
- package/dist/lib/billing/schema.d.ts +1 -1
- package/dist/lib/billing/schema.d.ts.map +1 -1
- package/dist/lib/billing/schema.js +1 -1
- package/dist/lib/blocks/sanitize-html.d.ts.map +1 -1
- package/dist/lib/blocks/sanitize-html.js +18 -2
- package/dist/lib/cache/revalidate-tag.d.ts +14 -0
- package/dist/lib/cache/revalidate-tag.d.ts.map +1 -0
- package/dist/lib/cache/revalidate-tag.js +8 -0
- package/dist/lib/config/dashboard-schema.d.ts +1 -1
- package/dist/lib/config/dashboard-schema.d.ts.map +1 -1
- package/dist/lib/config/dashboard-schema.js +1 -1
- package/dist/lib/config/types.d.ts +36 -16
- package/dist/lib/config/types.d.ts.map +1 -1
- package/dist/lib/docs/access.d.ts +28 -0
- package/dist/lib/docs/access.d.ts.map +1 -0
- package/dist/lib/docs/access.js +16 -0
- package/dist/lib/docs/parser.d.ts.map +1 -1
- package/dist/lib/docs/parser.js +2 -1
- package/dist/lib/docs/remark-doc-links.d.ts +14 -0
- package/dist/lib/docs/remark-doc-links.d.ts.map +1 -0
- package/dist/lib/docs/remark-doc-links.js +38 -0
- package/dist/lib/entities/api-generator.d.ts +1 -1
- package/dist/lib/entities/api-generator.d.ts.map +1 -1
- package/dist/lib/entities/api-generator.js +1 -1
- package/dist/lib/entities/schema-generator.d.ts +1 -1
- package/dist/lib/entities/schema-generator.d.ts.map +1 -1
- package/dist/lib/entities/schema-generator.js +1 -1
- package/dist/lib/mcp/engine.js +1 -1
- package/dist/lib/mcp/schema-builder.d.ts +1 -1
- package/dist/lib/mcp/schema-builder.d.ts.map +1 -1
- package/dist/lib/mcp/schema-builder.js +1 -1
- package/dist/lib/mcp/tool-generator.js +1 -1
- package/dist/lib/mcp/types.d.ts +1 -1
- package/dist/lib/mcp/types.d.ts.map +1 -1
- package/dist/lib/media/schemas.d.ts +1 -1
- package/dist/lib/media/schemas.d.ts.map +1 -1
- package/dist/lib/media/schemas.js +1 -1
- package/dist/lib/middleware/index.d.ts +5 -1
- package/dist/lib/middleware/index.d.ts.map +1 -1
- package/dist/lib/middleware/index.js +3 -2
- package/dist/lib/services/meta.service.d.ts +60 -0
- package/dist/lib/services/meta.service.d.ts.map +1 -1
- package/dist/lib/services/meta.service.js +132 -12
- package/dist/lib/services/middleware.service.d.ts.map +1 -1
- package/dist/lib/services/middleware.service.js +3 -2
- package/dist/lib/teams/schema.d.ts +1 -1
- package/dist/lib/teams/schema.d.ts.map +1 -1
- package/dist/lib/teams/schema.js +1 -1
- package/dist/lib/teams/teams.api.d.ts.map +1 -1
- package/dist/lib/teams/teams.api.js +4 -3
- package/dist/lib/user-data-client.d.ts.map +1 -1
- package/dist/lib/user-data-client.js +3 -2
- package/dist/lib/validation/invoices.d.ts +1 -1
- package/dist/lib/validation/invoices.d.ts.map +1 -1
- package/dist/lib/validation/invoices.js +1 -1
- package/dist/lib/validation-schemas.d.ts +1 -1
- package/dist/lib/validation-schemas.d.ts.map +1 -1
- package/dist/lib/validation-schemas.js +1 -1
- package/dist/lib/validation.d.ts +1 -1
- package/dist/lib/validation.d.ts.map +1 -1
- package/dist/lib/validation.js +1 -1
- package/dist/messages/de/footer.json +0 -1
- package/dist/messages/de/index.d.ts +0 -1
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/en/footer.json +0 -1
- package/dist/messages/en/index.d.ts +0 -1
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/es/footer.json +0 -1
- package/dist/messages/es/index.d.ts +0 -1
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/fr/footer.json +0 -1
- package/dist/messages/fr/index.d.ts +0 -1
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/it/footer.json +0 -1
- package/dist/messages/it/index.d.ts +0 -1
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/pt/footer.json +0 -1
- package/dist/messages/pt/index.d.ts +0 -1
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/templates/app/(auth)/accept-invite/[token]/page.tsx +4 -3
- package/dist/templates/app/(auth)/verify-email/page.tsx +4 -3
- package/dist/templates/app/(public)/docs/[section]/[page]/page.tsx +11 -0
- package/dist/templates/app/api/auth/[...all]/route.ts +15 -14
- package/dist/templates/app/api/internal/user-metadata/route.ts +6 -8
- package/dist/templates/app/api/user/plan-flags/route.ts +1 -1
- package/dist/templates/app/api/user/profile/route.ts +12 -17
- package/dist/templates/app/api/v1/api-keys/route.ts +1 -1
- package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +2 -1
- package/dist/templates/app/api/v1/billing/cancel/route.ts +1 -1
- package/dist/templates/app/api/v1/billing/change-plan/route.ts +1 -1
- package/dist/templates/app/api/v1/billing/check-action/route.ts +1 -1
- package/dist/templates/app/api/v1/billing/checkout/route.ts +1 -1
- package/dist/templates/app/api/v1/blocks/validate/route.ts +1 -1
- package/dist/templates/app/api/v1/media/[id]/tags/route.ts +1 -1
- package/dist/templates/app/api/v1/post-categories/[id]/route.ts +1 -1
- package/dist/templates/app/api/v1/post-categories/route.ts +1 -1
- package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +2 -1
- package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +3 -5
- package/dist/templates/app/api/v1/teams/docs.md +4 -9
- package/dist/templates/app/api/v1/teams/switch/route.ts +1 -1
- package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +1 -1
- package/dist/templates/app/api/v1/users/[id]/route.ts +1 -1
- package/dist/templates/app/api/v1/users/route.ts +1 -1
- package/dist/templates/app/dashboard/(main)/media/page.tsx +3 -1
- package/dist/templates/app/dashboard/settings/api-keys/page.tsx +5 -4
- package/dist/templates/app/dashboard/settings/profile/page.tsx +3 -2
- package/dist/templates/app/devtools/scheduled-actions/page.tsx +2 -1
- package/dist/templates/app/superadmin/docs/[section]/[page]/page.tsx +16 -0
- package/dist/templates/app/superadmin/subscriptions/page.tsx +3 -2
- package/dist/templates/app/superadmin/teams/[teamId]/page.tsx +5 -4
- package/dist/templates/app/superadmin/users/[userId]/page.tsx +2 -1
- package/dist/templates/app/superadmin/users/page.tsx +2 -1
- package/dist/templates/blocks/cta-section/component.tsx +3 -2
- package/dist/templates/blocks/cta-section/schema.ts +1 -1
- package/dist/templates/blocks/features-grid/component.tsx +2 -1
- package/dist/templates/blocks/features-grid/schema.ts +1 -1
- package/dist/templates/blocks/hero/component.tsx +3 -2
- package/dist/templates/blocks/hero/schema.ts +1 -1
- package/dist/templates/blocks/testimonials/component.tsx +2 -1
- package/dist/templates/blocks/testimonials/schema.ts +1 -1
- package/dist/templates/blocks/text-content/component.tsx +2 -1
- package/dist/templates/blocks/text-content/schema.ts +1 -1
- package/dist/templates/ci-workflows/README.md +1 -1
- package/dist/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/dist/templates/ci-workflows/github/cypress-smoke.yml +1 -1
- package/dist/templates/contents/plugins/starter/plugin/api/example/route.ts.template +1 -1
- package/dist/templates/contents/themes/starter/blocks/cta-section/component.tsx +3 -2
- package/dist/templates/contents/themes/starter/blocks/cta-section/schema.ts +1 -1
- package/dist/templates/contents/themes/starter/blocks/features-grid/component.tsx +2 -1
- package/dist/templates/contents/themes/starter/blocks/features-grid/schema.ts +1 -1
- package/dist/templates/contents/themes/starter/blocks/hero/component.tsx +3 -2
- package/dist/templates/contents/themes/starter/blocks/hero/schema.ts +1 -1
- package/dist/templates/contents/themes/starter/blocks/testimonials/component.tsx +3 -2
- package/dist/templates/contents/themes/starter/blocks/testimonials/schema.ts +1 -1
- package/dist/templates/contents/themes/starter/blocks/text-content/component.tsx +2 -1
- package/dist/templates/contents/themes/starter/blocks/text-content/schema.ts +1 -1
- package/dist/templates/contents/themes/starter/entities/posts/components/post-header.tsx +2 -1
- package/dist/templates/contents/themes/starter/lib/hooks/useAnalyticsData.ts +2 -1
- package/dist/templates/contents/themes/starter/lib/hooks/useTaskStats.ts +2 -1
- package/dist/templates/eslint.config.mjs +133 -9
- package/dist/templates/features/blog/blocks/post-content/component.tsx +2 -1
- package/dist/templates/features/blog/blocks/post-content/schema.ts +1 -1
- package/dist/templates/features/blog/entities/posts/components/post-header.tsx +2 -1
- package/dist/templates/features/pages/blocks/hero/component.tsx +3 -2
- package/dist/templates/features/pages/blocks/hero/schema.ts +1 -1
- package/dist/templates/next.config.mjs +50 -29
- package/dist/templates/proxy.ts +100 -21
- package/dist/templates/public/sw.js +2 -2
- package/dist/templates/scripts/cy-run-prod.cjs +5 -5
- package/dist/types/blocks.d.ts +1 -1
- package/dist/types/blocks.d.ts.map +1 -1
- package/dist/types/blocks.js +1 -1
- package/package.json +9 -5
- package/scripts/build/docs-registry.mjs +7 -7
- package/scripts/build/registry/__tests__/billing-registry.test.mjs +43 -0
- package/scripts/build/registry/__tests__/build-output.test.mjs +209 -0
- package/scripts/build/registry/__tests__/copy-race-child.mjs +14 -0
- package/scripts/build/registry/__tests__/direct-fs-writes.mjs +203 -0
- package/scripts/build/registry/__tests__/globals-css-tag.test.mjs +66 -0
- package/scripts/build/registry/__tests__/icons-discovery.test.mjs +530 -29
- package/scripts/build/registry/__tests__/page-generator.test.mjs +211 -37
- package/scripts/build/registry/__tests__/published-signatures.test.mjs +239 -0
- package/scripts/build/registry/__tests__/route-file-rule.test.mjs +170 -0
- package/scripts/build/registry/__tests__/safe-fs.test.mjs +333 -0
- package/scripts/build/registry/__tests__/template-analysis.test.mjs +142 -0
- package/scripts/build/registry/__tests__/template-registry.test.mjs +279 -9
- package/scripts/build/registry/__tests__/templates-plan.test.mjs +128 -0
- package/scripts/build/registry/__tests__/templates-tree.test.mjs +367 -0
- package/scripts/build/registry/__tests__/write-places.test.mjs +425 -0
- package/scripts/build/registry/config.mjs +6 -65
- package/scripts/build/registry/discovery/icons.mjs +498 -43
- package/scripts/build/registry/discovery/templates.mjs +1 -1
- package/scripts/build/registry/generators/billing-registry.mjs +1 -1
- package/scripts/build/registry/generators/docs-registry.mjs +3 -4
- package/scripts/build/registry/generators/feature-registry.mjs +1 -1
- package/scripts/build/registry/generators/permissions-registry.mjs +2 -2
- package/scripts/build/registry/generators/template-registry.mjs +65 -49
- package/scripts/build/registry/index.mjs +1 -2
- package/scripts/build/registry/post-build/index.mjs +1 -1
- package/scripts/build/registry/post-build/own-gitignores.mjs +220 -0
- package/scripts/build/registry/post-build/page-generator.mjs +512 -268
- package/scripts/build/registry/post-build/route-cleanup.mjs +16 -13
- package/scripts/build/registry/post-build/test-fixtures.mjs +26 -8
- package/scripts/build/registry/project-mode.mjs +76 -0
- package/scripts/build/registry/shared/typescript-compiler.mjs +77 -0
- package/scripts/build/registry/validate-env.test.mjs +12 -8
- package/scripts/build/registry/watch.mjs +3 -53
- package/scripts/build/registry/write-places.mjs +228 -0
- package/scripts/build/registry.mjs +53 -30
- package/scripts/build/safe-fs.mjs +279 -0
- package/scripts/build/templates-plan.mjs +44 -0
- package/scripts/build/theme.mjs +46 -42
- package/scripts/build/update-tsconfig.mjs +16 -15
- package/scripts/db/cluster-changes.mjs +205 -0
- package/scripts/db/connection-time-limits.mjs +53 -0
- package/scripts/db/inspect-server.mjs +108 -0
- package/scripts/db/migration-time-limit.mjs +695 -0
- package/scripts/db/run-migrations.mjs +46 -41
- package/scripts/db/ssl-config.mjs +194 -0
- package/scripts/db/theme-location.mjs +69 -0
- package/scripts/db/verify-tables.mjs +3 -8
- package/scripts/db/verify-teams.mjs +2 -6
- package/scripts/db/verify-theme-migrations.mjs +169 -0
- package/scripts/maintenance/core-updater.mjs +1003 -0
- package/scripts/maintenance/step-guard.mjs +74 -0
- package/scripts/maintenance/update-core.mjs +14 -831
- package/scripts/utils/console-guard.mjs +13 -0
- package/scripts/utils/generated-tag.mjs +66 -0
- package/scripts/utils/index.mjs +9 -1
- package/scripts/utils/logging.mjs +140 -0
- package/templates/app/(auth)/accept-invite/[token]/page.tsx +4 -3
- package/templates/app/(auth)/verify-email/page.tsx +4 -3
- package/templates/app/(public)/docs/[section]/[page]/page.tsx +11 -0
- package/templates/app/api/auth/[...all]/route.ts +15 -14
- package/templates/app/api/internal/user-metadata/route.ts +6 -8
- package/templates/app/api/user/plan-flags/route.ts +1 -1
- package/templates/app/api/user/profile/route.ts +12 -17
- package/templates/app/api/v1/api-keys/route.ts +1 -1
- package/templates/app/api/v1/auth/signup-with-invite/route.ts +2 -1
- package/templates/app/api/v1/billing/cancel/route.ts +1 -1
- package/templates/app/api/v1/billing/change-plan/route.ts +1 -1
- package/templates/app/api/v1/billing/check-action/route.ts +1 -1
- package/templates/app/api/v1/billing/checkout/route.ts +1 -1
- package/templates/app/api/v1/blocks/validate/route.ts +1 -1
- package/templates/app/api/v1/media/[id]/tags/route.ts +1 -1
- package/templates/app/api/v1/post-categories/[id]/route.ts +1 -1
- package/templates/app/api/v1/post-categories/route.ts +1 -1
- package/templates/app/api/v1/teams/[teamId]/members/route.ts +2 -1
- package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +3 -5
- package/templates/app/api/v1/teams/docs.md +4 -9
- package/templates/app/api/v1/teams/switch/route.ts +1 -1
- package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +1 -1
- package/templates/app/api/v1/users/[id]/route.ts +1 -1
- package/templates/app/api/v1/users/route.ts +1 -1
- package/templates/app/dashboard/(main)/media/page.tsx +3 -1
- package/templates/app/dashboard/settings/api-keys/page.tsx +5 -4
- package/templates/app/dashboard/settings/profile/page.tsx +3 -2
- package/templates/app/devtools/scheduled-actions/page.tsx +2 -1
- package/templates/app/superadmin/docs/[section]/[page]/page.tsx +16 -0
- package/templates/app/superadmin/subscriptions/page.tsx +3 -2
- package/templates/app/superadmin/teams/[teamId]/page.tsx +5 -4
- package/templates/app/superadmin/users/[userId]/page.tsx +2 -1
- package/templates/app/superadmin/users/page.tsx +2 -1
- package/templates/blocks/cta-section/component.tsx +3 -2
- package/templates/blocks/cta-section/schema.ts +1 -1
- package/templates/blocks/features-grid/component.tsx +2 -1
- package/templates/blocks/features-grid/schema.ts +1 -1
- package/templates/blocks/hero/component.tsx +3 -2
- package/templates/blocks/hero/schema.ts +1 -1
- package/templates/blocks/testimonials/component.tsx +2 -1
- package/templates/blocks/testimonials/schema.ts +1 -1
- package/templates/blocks/text-content/component.tsx +2 -1
- package/templates/blocks/text-content/schema.ts +1 -1
- package/templates/ci-workflows/README.md +1 -1
- package/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/templates/ci-workflows/github/cypress-smoke.yml +1 -1
- package/templates/contents/plugins/starter/plugin/api/example/route.ts.template +1 -1
- package/templates/contents/themes/starter/blocks/cta-section/component.tsx +3 -2
- package/templates/contents/themes/starter/blocks/cta-section/schema.ts +1 -1
- package/templates/contents/themes/starter/blocks/features-grid/component.tsx +2 -1
- package/templates/contents/themes/starter/blocks/features-grid/schema.ts +1 -1
- package/templates/contents/themes/starter/blocks/hero/component.tsx +3 -2
- package/templates/contents/themes/starter/blocks/hero/schema.ts +1 -1
- package/templates/contents/themes/starter/blocks/testimonials/component.tsx +3 -2
- package/templates/contents/themes/starter/blocks/testimonials/schema.ts +1 -1
- package/templates/contents/themes/starter/blocks/text-content/component.tsx +2 -1
- package/templates/contents/themes/starter/blocks/text-content/schema.ts +1 -1
- package/templates/contents/themes/starter/entities/posts/components/post-header.tsx +2 -1
- package/templates/contents/themes/starter/lib/hooks/useAnalyticsData.ts +2 -1
- package/templates/contents/themes/starter/lib/hooks/useTaskStats.ts +2 -1
- package/templates/eslint.config.mjs +133 -9
- package/templates/features/blog/blocks/post-content/component.tsx +2 -1
- package/templates/features/blog/blocks/post-content/schema.ts +1 -1
- package/templates/features/blog/entities/posts/components/post-header.tsx +2 -1
- package/templates/features/pages/blocks/hero/component.tsx +3 -2
- package/templates/features/pages/blocks/hero/schema.ts +1 -1
- package/templates/next.config.mjs +50 -29
- package/templates/proxy.ts +100 -21
- package/templates/public/sw.js +2 -2
- package/templates/scripts/cy-run-prod.cjs +5 -5
- package/tests/jest/__mocks__/@nextsparkjs/registries/template-registry.client.ts +11 -0
- package/tests/jest/__mocks__/next-server.js +10 -0
- package/scripts/build/docs.mjs +0 -392
|
@@ -20,9 +20,11 @@ import { join, dirname } from 'node:path'
|
|
|
20
20
|
import {
|
|
21
21
|
SEGMENT_CONFIG_EXPORTS,
|
|
22
22
|
MODULE_LEVEL_EXPORTS,
|
|
23
|
+
analyzeTemplates,
|
|
23
24
|
extractRouteExports,
|
|
24
25
|
generateTemplatePage,
|
|
25
|
-
|
|
26
|
+
selectTypeScriptModule,
|
|
27
|
+
willGenerateRoute,
|
|
26
28
|
} from '../post-build/page-generator.mjs'
|
|
27
29
|
|
|
28
30
|
const FIXTURE_FILE = '/virtual/theme/templates/(public)/page.tsx'
|
|
@@ -34,13 +36,11 @@ async function createProjectRoot() {
|
|
|
34
36
|
return mkdtemp(join(tmpdir(), 'nextspark-page-generator-test-'))
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
//
|
|
38
|
-
// generator
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
async function useProjectRoot(root) {
|
|
43
|
-
await generateMissingPages([], { projectRoot: root })
|
|
39
|
+
// The registry build parses templates once, with analyzeTemplates, and the page
|
|
40
|
+
// generator renders from that analysis. Generating a single page does the same.
|
|
41
|
+
async function generatePage(template, outputPath, root) {
|
|
42
|
+
const analysis = await analyzeTemplates([template], { projectRoot: root })
|
|
43
|
+
await generateTemplatePage(template, outputPath, analysis)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
async function writeThemeTemplate(root, relativePath, content) {
|
|
@@ -198,7 +198,6 @@ test('the error names the line the bad export is declared on', async () => {
|
|
|
198
198
|
test('the generated page re-declares segment config as a literal and forwards module exports separately', async () => {
|
|
199
199
|
const root = await createProjectRoot()
|
|
200
200
|
try {
|
|
201
|
-
await useProjectRoot(root)
|
|
202
201
|
const templatePath = await writeThemeTemplate(
|
|
203
202
|
root,
|
|
204
203
|
'(public)/blog/[slug]/page.tsx',
|
|
@@ -206,9 +205,10 @@ test('the generated page re-declares segment config as a literal and forwards mo
|
|
|
206
205
|
)
|
|
207
206
|
|
|
208
207
|
const outputPath = join(root, 'app/(templates)/(public)/blog/[slug]/page.tsx')
|
|
209
|
-
await
|
|
208
|
+
await generatePage(
|
|
210
209
|
{ appPath: 'app/(public)/blog/[slug]/page.tsx', templateType: 'page', name: '(public)/blog/[slug]/page', templatePath },
|
|
211
|
-
outputPath
|
|
210
|
+
outputPath,
|
|
211
|
+
root
|
|
212
212
|
)
|
|
213
213
|
|
|
214
214
|
const generated = await readFile(outputPath, 'utf8')
|
|
@@ -228,13 +228,13 @@ test('the generated page re-declares segment config as a literal and forwards mo
|
|
|
228
228
|
test('a page with no route-level exports is generated unchanged (no forwarded-exports section)', async () => {
|
|
229
229
|
const root = await createProjectRoot()
|
|
230
230
|
try {
|
|
231
|
-
await useProjectRoot(root)
|
|
232
231
|
const templatePath = await writeThemeTemplate(root, 'plain/page.tsx', 'export default function Page() { return null }\n')
|
|
233
232
|
|
|
234
233
|
const outputPath = join(root, 'app/(templates)/plain/page.tsx')
|
|
235
|
-
await
|
|
234
|
+
await generatePage(
|
|
236
235
|
{ appPath: 'app/plain/page.tsx', templateType: 'page', name: 'plain/page', templatePath },
|
|
237
|
-
outputPath
|
|
236
|
+
outputPath,
|
|
237
|
+
root
|
|
238
238
|
)
|
|
239
239
|
|
|
240
240
|
const generated = await readFile(outputPath, 'utf8')
|
|
@@ -249,7 +249,6 @@ test('a page with no route-level exports is generated unchanged (no forwarded-ex
|
|
|
249
249
|
test('an invalid segment config value fails page generation instead of being silently dropped', async () => {
|
|
250
250
|
const root = await createProjectRoot()
|
|
251
251
|
try {
|
|
252
|
-
await useProjectRoot(root)
|
|
253
252
|
const templatePath = await writeThemeTemplate(
|
|
254
253
|
root,
|
|
255
254
|
'broken/page.tsx',
|
|
@@ -259,9 +258,10 @@ test('an invalid segment config value fails page generation instead of being sil
|
|
|
259
258
|
const outputPath = join(root, 'app/(templates)/broken/page.tsx')
|
|
260
259
|
await assert.rejects(
|
|
261
260
|
() =>
|
|
262
|
-
|
|
261
|
+
generatePage(
|
|
263
262
|
{ appPath: 'app/broken/page.tsx', templateType: 'page', name: 'broken/page', templatePath },
|
|
264
|
-
outputPath
|
|
263
|
+
outputPath,
|
|
264
|
+
root
|
|
265
265
|
),
|
|
266
266
|
/"revalidate"/
|
|
267
267
|
)
|
|
@@ -273,7 +273,6 @@ test('an invalid segment config value fails page generation instead of being sil
|
|
|
273
273
|
test('a layout with a default export and metadata forwards both, unlike the previous component-only behavior', async () => {
|
|
274
274
|
const root = await createProjectRoot()
|
|
275
275
|
try {
|
|
276
|
-
await useProjectRoot(root)
|
|
277
276
|
const templatePath = await writeThemeTemplate(
|
|
278
277
|
root,
|
|
279
278
|
'dashboard/layout.tsx',
|
|
@@ -281,9 +280,10 @@ test('a layout with a default export and metadata forwards both, unlike the prev
|
|
|
281
280
|
)
|
|
282
281
|
|
|
283
282
|
const outputPath = join(root, 'app/(templates)/dashboard/layout.tsx')
|
|
284
|
-
await
|
|
283
|
+
await generatePage(
|
|
285
284
|
{ appPath: 'app/dashboard/layout.tsx', templateType: 'layout', name: 'dashboard/layout', templatePath },
|
|
286
|
-
outputPath
|
|
285
|
+
outputPath,
|
|
286
|
+
root
|
|
287
287
|
)
|
|
288
288
|
|
|
289
289
|
const generated = await readFile(outputPath, 'utf8')
|
|
@@ -297,13 +297,13 @@ test('a layout with a default export and metadata forwards both, unlike the prev
|
|
|
297
297
|
test('a layout template with no exports at all still gets the plain pass-through wrapper', async () => {
|
|
298
298
|
const root = await createProjectRoot()
|
|
299
299
|
try {
|
|
300
|
-
await useProjectRoot(root)
|
|
301
300
|
const templatePath = await writeThemeTemplate(root, 'empty/layout.tsx', '')
|
|
302
301
|
|
|
303
302
|
const outputPath = join(root, 'app/(templates)/empty/layout.tsx')
|
|
304
|
-
await
|
|
303
|
+
await generatePage(
|
|
305
304
|
{ appPath: 'app/empty/layout.tsx', templateType: 'layout', name: 'empty/layout', templatePath },
|
|
306
|
-
outputPath
|
|
305
|
+
outputPath,
|
|
306
|
+
root
|
|
307
307
|
)
|
|
308
308
|
|
|
309
309
|
const generated = await readFile(outputPath, 'utf8')
|
|
@@ -335,7 +335,6 @@ for (const [label, source, expected] of [
|
|
|
335
335
|
test('a metadata-only layout whose comment mentions export default gets the pass-through, not an import of a missing default', async () => {
|
|
336
336
|
const root = await createProjectRoot()
|
|
337
337
|
try {
|
|
338
|
-
await useProjectRoot(root)
|
|
339
338
|
const templatePath = await writeThemeTemplate(
|
|
340
339
|
root,
|
|
341
340
|
'docs/layout.tsx',
|
|
@@ -343,13 +342,14 @@ test('a metadata-only layout whose comment mentions export default gets the pass
|
|
|
343
342
|
)
|
|
344
343
|
|
|
345
344
|
const outputPath = join(root, 'app/(templates)/docs/layout.tsx')
|
|
346
|
-
await
|
|
345
|
+
await generatePage(
|
|
347
346
|
{ appPath: 'app/docs/layout.tsx', templateType: 'layout', name: 'docs/layout', templatePath },
|
|
348
|
-
outputPath
|
|
347
|
+
outputPath,
|
|
348
|
+
root
|
|
349
349
|
)
|
|
350
350
|
|
|
351
351
|
const generated = await readFile(outputPath, 'utf8')
|
|
352
|
-
assert.match(generated, /
|
|
352
|
+
assert.match(generated, /Pass-through component/)
|
|
353
353
|
assert.doesNotMatch(generated, /import TemplateComponent from/)
|
|
354
354
|
assert.match(generated, /export \{ metadata \} from/)
|
|
355
355
|
} finally {
|
|
@@ -357,10 +357,45 @@ test('a metadata-only layout whose comment mentions export default gets the pass
|
|
|
357
357
|
}
|
|
358
358
|
})
|
|
359
359
|
|
|
360
|
+
test('a pass-through layout on an unprotected path does not claim to be PROTECTED_RENDER', async () => {
|
|
361
|
+
const root = await createProjectRoot()
|
|
362
|
+
try {
|
|
363
|
+
const templatePath = await writeThemeTemplate(root, '(public)/docs/layout.tsx', "export const viewport = { themeColor: 'black' }\n")
|
|
364
|
+
|
|
365
|
+
const outputPath = join(root, 'app/(templates)/(public)/docs/layout.tsx')
|
|
366
|
+
await generatePage(
|
|
367
|
+
{ appPath: 'app/(public)/docs/layout.tsx', templateType: 'layout', name: '(public)/docs/layout', templatePath },
|
|
368
|
+
outputPath,
|
|
369
|
+
root
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
const generated = await readFile(outputPath, 'utf8')
|
|
373
|
+
assert.match(generated, /Pass-through component \(the theme template exports no component/)
|
|
374
|
+
assert.doesNotMatch(generated, /PROTECTED_RENDER/)
|
|
375
|
+
} finally {
|
|
376
|
+
await rm(root, { recursive: true, force: true })
|
|
377
|
+
}
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
test('a pass-through layout on a PROTECTED_RENDER path is labeled as one', async () => {
|
|
381
|
+
const root = await createProjectRoot()
|
|
382
|
+
try {
|
|
383
|
+
const templatePath = await writeThemeTemplate(root, 'layout.tsx', "export const metadata = { title: 'App' }\n")
|
|
384
|
+
|
|
385
|
+
const outputPath = join(root, 'app/(templates)/layout.tsx')
|
|
386
|
+
await generatePage({ appPath: 'app/layout.tsx', templateType: 'layout', name: 'layout', templatePath }, outputPath, root)
|
|
387
|
+
|
|
388
|
+
const generated = await readFile(outputPath, 'utf8')
|
|
389
|
+
assert.match(generated, /Layout template - metadata-only \(PROTECTED_RENDER\)/)
|
|
390
|
+
assert.match(generated, /Pass-through component \(actual rendering blocked by PROTECTED_RENDER\)/)
|
|
391
|
+
} finally {
|
|
392
|
+
await rm(root, { recursive: true, force: true })
|
|
393
|
+
}
|
|
394
|
+
})
|
|
395
|
+
|
|
360
396
|
test('a layout exported through `export { X as default }` imports the theme component instead of a pass-through', async () => {
|
|
361
397
|
const root = await createProjectRoot()
|
|
362
398
|
try {
|
|
363
|
-
await useProjectRoot(root)
|
|
364
399
|
const templatePath = await writeThemeTemplate(
|
|
365
400
|
root,
|
|
366
401
|
'dashboard/layout.tsx',
|
|
@@ -368,9 +403,10 @@ test('a layout exported through `export { X as default }` imports the theme comp
|
|
|
368
403
|
)
|
|
369
404
|
|
|
370
405
|
const outputPath = join(root, 'app/(templates)/dashboard/layout.tsx')
|
|
371
|
-
await
|
|
406
|
+
await generatePage(
|
|
372
407
|
{ appPath: 'app/dashboard/layout.tsx', templateType: 'layout', name: 'dashboard/layout', templatePath },
|
|
373
|
-
outputPath
|
|
408
|
+
outputPath,
|
|
409
|
+
root
|
|
374
410
|
)
|
|
375
411
|
|
|
376
412
|
const generated = await readFile(outputPath, 'utf8')
|
|
@@ -392,7 +428,6 @@ test('a .ts template is parsed as TypeScript, so syntax a TSX parse reads as JSX
|
|
|
392
428
|
test('a .ts layout with TypeScript-only syntax imports the theme component instead of a pass-through', async () => {
|
|
393
429
|
const root = await createProjectRoot()
|
|
394
430
|
try {
|
|
395
|
-
await useProjectRoot(root)
|
|
396
431
|
const templatePath = await writeThemeTemplate(
|
|
397
432
|
root,
|
|
398
433
|
'reports/layout.ts',
|
|
@@ -400,9 +435,10 @@ test('a .ts layout with TypeScript-only syntax imports the theme component inste
|
|
|
400
435
|
)
|
|
401
436
|
|
|
402
437
|
const outputPath = join(root, 'app/(templates)/reports/layout.ts')
|
|
403
|
-
await
|
|
438
|
+
await generatePage(
|
|
404
439
|
{ appPath: 'app/reports/layout.ts', templateType: 'layout', name: 'reports/layout', templatePath },
|
|
405
|
-
outputPath
|
|
440
|
+
outputPath,
|
|
441
|
+
root
|
|
406
442
|
)
|
|
407
443
|
|
|
408
444
|
const generated = await readFile(outputPath, 'utf8')
|
|
@@ -420,11 +456,149 @@ test('a template that does not parse fails with its file and line instead of bei
|
|
|
420
456
|
)
|
|
421
457
|
})
|
|
422
458
|
|
|
423
|
-
test('
|
|
459
|
+
test('a template using syntax TypeScript rejects but Next.js accepts retains its route exports', async () => {
|
|
424
460
|
const source = "import source wasm from './module.wasm'\nexport const runtime = 'edge'\nexport default wasm\n"
|
|
425
461
|
for (const file of ['/virtual/theme/templates/(public)/page.ts', FIXTURE_FILE]) {
|
|
426
|
-
const
|
|
427
|
-
assert.deepEqual(segmentConfig, { runtime: 'edge' }
|
|
428
|
-
assert.equal(hasDefaultExport, true
|
|
462
|
+
const routeExports = await extractRouteExports(source, file)
|
|
463
|
+
assert.deepEqual(routeExports.segmentConfig, { runtime: 'edge' })
|
|
464
|
+
assert.equal(routeExports.hasDefaultExport, true)
|
|
465
|
+
}
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
test('a syntax error rejected by both TypeScript and Next.js is rejected even when TypeScript recovers later route exports', async () => {
|
|
469
|
+
const source = "import = './module.wasm'\nexport const runtime = 'edge'\nexport default wasm\n"
|
|
470
|
+
for (const file of ['/virtual/theme/templates/(public)/page.ts', FIXTURE_FILE]) {
|
|
471
|
+
await assert.rejects(() => extractRouteExports(source, file), /the template does not parse/)
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
// --- selectTypeScriptModule: picking a candidate with the TS 5/6 compiler API (#195) --
|
|
476
|
+
|
|
477
|
+
// A stand-in for TypeScript 7's native port: it has a version but none of the
|
|
478
|
+
// compiler API (`createSourceFile`, `getScriptKindFromFileName`, `ScriptKind`)
|
|
479
|
+
// this generator parses templates with.
|
|
480
|
+
const fakeTypeScript7 = { version: '7.0.2' }
|
|
481
|
+
|
|
482
|
+
const fakeTypeScript5 = {
|
|
483
|
+
version: '5.9.3',
|
|
484
|
+
createSourceFile: () => ({}),
|
|
485
|
+
getScriptKindFromFileName: () => 1,
|
|
486
|
+
ScriptKind: { TSX: 4 },
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
test('a candidate with the compiler API is used', async () => {
|
|
490
|
+
const ts = await selectTypeScriptModule([{ label: 'only candidate', load: async () => fakeTypeScript5 }])
|
|
491
|
+
assert.equal(ts, fakeTypeScript5)
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
test('a candidate module shaped like an ESM default export is unwrapped', async () => {
|
|
495
|
+
const ts = await selectTypeScriptModule([{ label: 'only candidate', load: async () => ({ default: fakeTypeScript5 }) }])
|
|
496
|
+
assert.equal(ts, fakeTypeScript5)
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
test('a TypeScript 7 candidate without the compiler API is skipped in favor of the next candidate that has it', async () => {
|
|
500
|
+
const ts = await selectTypeScriptModule([
|
|
501
|
+
{ label: '@nextsparkjs/core', load: async () => fakeTypeScript7 },
|
|
502
|
+
{ label: 'the project', load: async () => fakeTypeScript5 },
|
|
503
|
+
])
|
|
504
|
+
assert.equal(ts, fakeTypeScript5)
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
test('a candidate whose load() rejects is skipped in favor of the next candidate', async () => {
|
|
508
|
+
const ts = await selectTypeScriptModule([
|
|
509
|
+
{ label: '@nextsparkjs/core', load: async () => { throw new Error('Cannot find module \'typescript\'') } },
|
|
510
|
+
{ label: 'the project', load: async () => fakeTypeScript5 },
|
|
511
|
+
])
|
|
512
|
+
assert.equal(ts, fakeTypeScript5)
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
test('no candidate providing the compiler API fails with a message naming each candidate and what was found', async () => {
|
|
516
|
+
await assert.rejects(
|
|
517
|
+
() =>
|
|
518
|
+
selectTypeScriptModule([
|
|
519
|
+
{ label: '@nextsparkjs/core', load: async () => fakeTypeScript7 },
|
|
520
|
+
{ label: 'the project at /virtual/project', load: async () => { throw new Error('Cannot find module \'typescript\'') } },
|
|
521
|
+
]),
|
|
522
|
+
error => {
|
|
523
|
+
assert.match(error.message, /TypeScript 5 or 6 compiler API/)
|
|
524
|
+
assert.match(error.message, /@nextsparkjs\/core.*TypeScript 7\.0\.2/)
|
|
525
|
+
assert.match(error.message, /the project at \/virtual\/project.*could not be loaded/)
|
|
526
|
+
assert.doesNotMatch(error.message, /createSourceFile is not a function/)
|
|
527
|
+
return true
|
|
528
|
+
}
|
|
529
|
+
)
|
|
530
|
+
})
|
|
531
|
+
|
|
532
|
+
// --- willGenerateRoute: whether the page generator writes a route file importing the template (#197) --
|
|
533
|
+
|
|
534
|
+
test('willGenerateRoute is always true for a layout, whether or not the app already has one', async () => {
|
|
535
|
+
const root = await createProjectRoot()
|
|
536
|
+
try {
|
|
537
|
+
assert.equal(willGenerateRoute('app/dashboard/layout.tsx', 'layout', root), true)
|
|
538
|
+
|
|
539
|
+
await mkdir(join(root, 'app/dashboard'), { recursive: true })
|
|
540
|
+
await writeFile(join(root, 'app/dashboard/layout.tsx'), 'export default function Layout({ children }) { return children }\n', 'utf8')
|
|
541
|
+
assert.equal(willGenerateRoute('app/dashboard/layout.tsx', 'layout', root), true)
|
|
542
|
+
} finally {
|
|
543
|
+
await rm(root, { recursive: true, force: true })
|
|
544
|
+
}
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
// --- analyzeTemplates: what the build reads from each template, and generates from (#197) --
|
|
548
|
+
|
|
549
|
+
test('analyzeTemplates records, per template path, its route-level exports, whether it has a default export and whether a route file is generated', async () => {
|
|
550
|
+
const root = await createProjectRoot()
|
|
551
|
+
try {
|
|
552
|
+
const layoutPath = await writeThemeTemplate(root, 'docs/layout.tsx', "export const revalidate = 60\nexport const viewport = { themeColor: 'black' }\n")
|
|
553
|
+
const pagePath = await writeThemeTemplate(root, 'pricing/page.tsx', 'export default function Page() { return null }\n')
|
|
554
|
+
await mkdir(join(root, 'app/pricing'), { recursive: true })
|
|
555
|
+
await writeFile(join(root, 'app/pricing/page.tsx'), 'export default function Page() { return null }\n', 'utf8')
|
|
556
|
+
const layout = { appPath: 'app/docs/layout.tsx', templateType: 'layout', name: 'docs/layout', templatePath: layoutPath }
|
|
557
|
+
const page = { appPath: 'app/pricing/page.tsx', templateType: 'page', name: 'pricing/page', templatePath: pagePath }
|
|
558
|
+
|
|
559
|
+
const analysis = await analyzeTemplates([layout, page, { ...page }], { projectRoot: root })
|
|
560
|
+
|
|
561
|
+
assert.deepEqual([...analysis.keys()], [layoutPath, pagePath])
|
|
562
|
+
assert.deepEqual(analysis.get(layoutPath), {
|
|
563
|
+
segmentConfig: { revalidate: 60 },
|
|
564
|
+
moduleExports: ['viewport'],
|
|
565
|
+
hasDefaultExport: false,
|
|
566
|
+
generatesRoute: true,
|
|
567
|
+
})
|
|
568
|
+
assert.deepEqual(analysis.get(pagePath), { segmentConfig: {}, moduleExports: [], hasDefaultExport: true, generatesRoute: false })
|
|
569
|
+
} finally {
|
|
570
|
+
await rm(root, { recursive: true, force: true })
|
|
571
|
+
}
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
test('generating a page from an analysis that never read its template fails instead of reading the template', async () => {
|
|
575
|
+
const root = await createProjectRoot()
|
|
576
|
+
try {
|
|
577
|
+
const templatePath = await writeThemeTemplate(root, 'plain/page.tsx', 'export default function Page() { return null }\n')
|
|
578
|
+
|
|
579
|
+
await assert.rejects(
|
|
580
|
+
() =>
|
|
581
|
+
generateTemplatePage(
|
|
582
|
+
{ appPath: 'app/plain/page.tsx', templateType: 'page', name: 'plain/page', templatePath },
|
|
583
|
+
join(root, 'app/(templates)/plain/page.tsx'),
|
|
584
|
+
new Map()
|
|
585
|
+
),
|
|
586
|
+
/was not read by analyzeTemplates/
|
|
587
|
+
)
|
|
588
|
+
} finally {
|
|
589
|
+
await rm(root, { recursive: true, force: true })
|
|
590
|
+
}
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
test('willGenerateRoute is true for a page only when the app has no file there yet', async () => {
|
|
594
|
+
const root = await createProjectRoot()
|
|
595
|
+
try {
|
|
596
|
+
assert.equal(willGenerateRoute('app/docs/page.tsx', 'page', root), true)
|
|
597
|
+
|
|
598
|
+
await mkdir(join(root, 'app/docs'), { recursive: true })
|
|
599
|
+
await writeFile(join(root, 'app/docs/page.tsx'), 'export default function Page() { return null }\n', 'utf8')
|
|
600
|
+
assert.equal(willGenerateRoute('app/docs/page.tsx', 'page', root), false)
|
|
601
|
+
} finally {
|
|
602
|
+
await rm(root, { recursive: true, force: true })
|
|
429
603
|
}
|
|
430
604
|
})
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The template generators @nextsparkjs/core exposes through `./scripts/*`
|
|
3
|
+
* keep the signatures they were published with. A caller that doesn't pass
|
|
4
|
+
* the analysis `analyzeTemplates` produces gets one taken for that call, with
|
|
5
|
+
* the same output the registry build produces, and no generator deletes
|
|
6
|
+
* anything before its input has been checked.
|
|
7
|
+
*
|
|
8
|
+
* Run: node --test packages/core/scripts/build/registry/__tests__/published-signatures.test.mjs
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { test } from 'node:test'
|
|
12
|
+
import assert from 'node:assert/strict'
|
|
13
|
+
import { mkdtemp, mkdir, writeFile, readFile, rm } from 'node:fs/promises'
|
|
14
|
+
import { existsSync } from 'node:fs'
|
|
15
|
+
import { tmpdir } from 'node:os'
|
|
16
|
+
import { join, dirname } from 'node:path'
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
generateTemplateRegistry,
|
|
20
|
+
generateTemplateRegistryClient
|
|
21
|
+
} from '../generators/template-registry.mjs'
|
|
22
|
+
import { generateMissingPages, generateTemplatePage } from '../post-build/page-generator.mjs'
|
|
23
|
+
|
|
24
|
+
async function createProject() {
|
|
25
|
+
return mkdtemp(join(tmpdir(), 'nextspark-published-signatures-test-'))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function writeProjectFile(root, relativePath, content) {
|
|
29
|
+
const absolutePath = join(root, relativePath)
|
|
30
|
+
await mkdir(dirname(absolutePath), { recursive: true })
|
|
31
|
+
await writeFile(absolutePath, content, 'utf8')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** A theme template at `contents/themes/testtheme/templates/<relativePath>`, overriding `app/<relativePath>`. */
|
|
35
|
+
async function writeTemplate(root, relativePath, templateType, content) {
|
|
36
|
+
await writeProjectFile(root, join('contents/themes/testtheme/templates', relativePath), content)
|
|
37
|
+
return {
|
|
38
|
+
name: relativePath.replace(/\.(tsx|ts)$/, ''),
|
|
39
|
+
themeName: 'testtheme',
|
|
40
|
+
templateType,
|
|
41
|
+
fileName: relativePath.split('/').pop(),
|
|
42
|
+
relativePath,
|
|
43
|
+
appPath: `app/${relativePath}`,
|
|
44
|
+
templatePath: `@/contents/themes/testtheme/templates/${relativePath}`,
|
|
45
|
+
priority: 10
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const LAYOUT_WITHOUT_COMPONENT = "export const viewport = { themeColor: 'black' }\n"
|
|
50
|
+
const PAGE_WITH_COMPONENT = 'export default function Page() { return null }\n'
|
|
51
|
+
|
|
52
|
+
test('generateTemplateRegistry(templates, config) decides component vs metadata-only without being handed an analysis', async () => {
|
|
53
|
+
const root = await createProject()
|
|
54
|
+
try {
|
|
55
|
+
const layout = await writeTemplate(root, 'docs/layout.tsx', 'layout', LAYOUT_WITHOUT_COMPONENT)
|
|
56
|
+
const page = await writeTemplate(root, 'pricing/page.tsx', 'page', PAGE_WITH_COMPONENT)
|
|
57
|
+
|
|
58
|
+
const out = await generateTemplateRegistry([layout, page], { outputDir: join(root, '.nextspark/registries'), projectRoot: root })
|
|
59
|
+
|
|
60
|
+
assert.match(out, /'app\/docs\/layout\.tsx': \{\n\s*appPath: 'app\/docs\/layout\.tsx',\n\s*component: null,/)
|
|
61
|
+
assert.match(out, /component: lazyTemplate\('app\/pricing\/page\.tsx', \(\) => import\('@\/contents\/themes\/testtheme\/templates\/pricing\/page'\)\)/)
|
|
62
|
+
} finally {
|
|
63
|
+
await rm(root, { recursive: true, force: true })
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('generateTemplateRegistryClient(templates, config) leaves out a template with no component without being handed an analysis', async () => {
|
|
68
|
+
const root = await createProject()
|
|
69
|
+
try {
|
|
70
|
+
const layout = await writeTemplate(root, 'docs/layout.tsx', 'layout', LAYOUT_WITHOUT_COMPONENT)
|
|
71
|
+
const page = await writeTemplate(root, 'pricing/page.tsx', 'page', PAGE_WITH_COMPONENT)
|
|
72
|
+
|
|
73
|
+
const out = await generateTemplateRegistryClient([layout, page], { outputDir: join(root, '.nextspark/registries'), projectRoot: root })
|
|
74
|
+
|
|
75
|
+
assert.doesNotMatch(out, /app\/docs\/layout\.tsx/)
|
|
76
|
+
assert.match(out, /'app\/pricing\/page\.tsx': dynamic\(\(\) => import\('@\/contents\/themes\/testtheme\/templates\/pricing\/page'\)\)/)
|
|
77
|
+
} finally {
|
|
78
|
+
await rm(root, { recursive: true, force: true })
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('generateMissingPages(templates, config) writes the same routes the registry build does without being handed an analysis', async () => {
|
|
83
|
+
const root = await createProject()
|
|
84
|
+
try {
|
|
85
|
+
const layout = await writeTemplate(root, 'docs/layout.tsx', 'layout', LAYOUT_WITHOUT_COMPONENT)
|
|
86
|
+
const page = await writeTemplate(root, 'pricing/page.tsx', 'page', PAGE_WITH_COMPONENT)
|
|
87
|
+
|
|
88
|
+
await generateMissingPages([layout, page], { projectRoot: root })
|
|
89
|
+
|
|
90
|
+
const generatedLayout = await readFile(join(root, 'app/(templates)/docs/layout.tsx'), 'utf8')
|
|
91
|
+
assert.match(generatedLayout, /Pass-through component/)
|
|
92
|
+
assert.match(generatedLayout, /export \{ viewport \} from '@\/contents\/themes\/testtheme\/templates\/docs\/layout'/)
|
|
93
|
+
|
|
94
|
+
const generatedPage = await readFile(join(root, 'app/(templates)/pricing/page.tsx'), 'utf8')
|
|
95
|
+
assert.match(generatedPage, /import TemplateComponent from '@\/contents\/themes\/testtheme\/templates\/pricing\/page'/)
|
|
96
|
+
} finally {
|
|
97
|
+
await rm(root, { recursive: true, force: true })
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('generateTemplatePage(template, outputPath) writes a route file without being handed an analysis', async () => {
|
|
102
|
+
const root = await createProject()
|
|
103
|
+
try {
|
|
104
|
+
// generateTemplatePage resolves templates against the project root the last
|
|
105
|
+
// generateMissingPages call was given.
|
|
106
|
+
await generateMissingPages([], { projectRoot: root })
|
|
107
|
+
const page = await writeTemplate(root, 'blog/page.tsx', 'page', `export const revalidate = 3600\n${PAGE_WITH_COMPONENT}`)
|
|
108
|
+
const outputPath = join(root, 'app/(templates)/blog/page.tsx')
|
|
109
|
+
|
|
110
|
+
await generateTemplatePage(page, outputPath)
|
|
111
|
+
|
|
112
|
+
const generated = await readFile(outputPath, 'utf8')
|
|
113
|
+
assert.match(generated, /import TemplateComponent from '@\/contents\/themes\/testtheme\/templates\/blog\/page'/)
|
|
114
|
+
assert.match(generated, /export const revalidate = 3600/)
|
|
115
|
+
} finally {
|
|
116
|
+
await rm(root, { recursive: true, force: true })
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('generateTemplatePage(template, outputPath) still rejects segment config Next.js would not read', async () => {
|
|
121
|
+
const root = await createProject()
|
|
122
|
+
try {
|
|
123
|
+
await generateMissingPages([], { projectRoot: root })
|
|
124
|
+
const page = await writeTemplate(root, 'broken/page.tsx', 'page', `export const revalidate = 60 * 60\n${PAGE_WITH_COMPONENT}`)
|
|
125
|
+
const outputPath = join(root, 'app/(templates)/broken/page.tsx')
|
|
126
|
+
|
|
127
|
+
await assert.rejects(() => generateTemplatePage(page, outputPath), /segment config export "revalidate" is not a literal/)
|
|
128
|
+
assert.equal(existsSync(outputPath), false)
|
|
129
|
+
} finally {
|
|
130
|
+
await rm(root, { recursive: true, force: true })
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('generateMissingPages(templates, config) rejects a template it cannot generate before deleting anything', async () => {
|
|
135
|
+
const root = await createProject()
|
|
136
|
+
try {
|
|
137
|
+
await writeProjectFile(root, 'app/(templates)/old/page.tsx', PAGE_WITH_COMPONENT)
|
|
138
|
+
const page = await writeTemplate(root, 'docs/page.tsx', 'page', "export const metadata = { title: 'Docs' }\n")
|
|
139
|
+
|
|
140
|
+
await assert.rejects(() => generateMissingPages([page], { projectRoot: root }), /has no default export/)
|
|
141
|
+
|
|
142
|
+
assert.equal(existsSync(join(root, 'app/(templates)/old/page.tsx')), true)
|
|
143
|
+
} finally {
|
|
144
|
+
await rm(root, { recursive: true, force: true })
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('generateMissingPages rejects an analysis that is missing one of its templates before deleting anything', async () => {
|
|
149
|
+
const root = await createProject()
|
|
150
|
+
try {
|
|
151
|
+
await writeProjectFile(root, 'app/(templates)/old/page.tsx', PAGE_WITH_COMPONENT)
|
|
152
|
+
const page = await writeTemplate(root, 'pricing/page.tsx', 'page', PAGE_WITH_COMPONENT)
|
|
153
|
+
|
|
154
|
+
await assert.rejects(() => generateMissingPages([page], { projectRoot: root }, new Map()), /was not read by analyzeTemplates/)
|
|
155
|
+
|
|
156
|
+
assert.equal(existsSync(join(root, 'app/(templates)/old/page.tsx')), true)
|
|
157
|
+
} finally {
|
|
158
|
+
await rm(root, { recursive: true, force: true })
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
/** A standalone `page.meta.ts` - which never has a default export - overriding `app/<route>/page.tsx`. */
|
|
163
|
+
async function writeMetaTemplate(root, route) {
|
|
164
|
+
const relativePath = `${route}/page.meta.ts`
|
|
165
|
+
await writeProjectFile(root, join('contents/themes/testtheme/templates', relativePath), "export const metadata = { title: 'Docs' }\n")
|
|
166
|
+
return {
|
|
167
|
+
name: `${route}/page`,
|
|
168
|
+
themeName: 'testtheme',
|
|
169
|
+
templateType: 'page',
|
|
170
|
+
fileName: 'page.meta.ts',
|
|
171
|
+
relativePath,
|
|
172
|
+
appPath: `app/${route}/page.tsx`,
|
|
173
|
+
templatePath: `@/contents/themes/testtheme/templates/${relativePath}`,
|
|
174
|
+
priority: 10
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
test('generateTemplatePage(template, outputPath) rejects a page.meta.ts for a route the app has no page at, without writing it', async () => {
|
|
179
|
+
const root = await createProject()
|
|
180
|
+
try {
|
|
181
|
+
await generateMissingPages([], { projectRoot: root })
|
|
182
|
+
const meta = await writeMetaTemplate(root, 'docs')
|
|
183
|
+
const outputPath = join(root, 'app/(templates)/docs/page.tsx')
|
|
184
|
+
|
|
185
|
+
await assert.rejects(
|
|
186
|
+
() => generateTemplatePage(meta, outputPath),
|
|
187
|
+
/page\.meta\.ts has no default export, and the app has no existing route at "app\/docs\/page\.tsx"/
|
|
188
|
+
)
|
|
189
|
+
assert.equal(existsSync(outputPath), false)
|
|
190
|
+
} finally {
|
|
191
|
+
await rm(root, { recursive: true, force: true })
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test('generateTemplatePage(template, outputPath) rejects a page.tsx with no default export for a route the app has no page at, without writing it', async () => {
|
|
196
|
+
const root = await createProject()
|
|
197
|
+
try {
|
|
198
|
+
await generateMissingPages([], { projectRoot: root })
|
|
199
|
+
const page = await writeTemplate(root, 'docs/page.tsx', 'page', "export const metadata = { title: 'Docs' }\n")
|
|
200
|
+
const outputPath = join(root, 'app/(templates)/docs/page.tsx')
|
|
201
|
+
|
|
202
|
+
await assert.rejects(
|
|
203
|
+
() => generateTemplatePage(page, outputPath),
|
|
204
|
+
/docs\/page\.tsx has no default export, and the app has no existing route at "app\/docs\/page\.tsx"/
|
|
205
|
+
)
|
|
206
|
+
assert.equal(existsSync(outputPath), false)
|
|
207
|
+
} finally {
|
|
208
|
+
await rm(root, { recursive: true, force: true })
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
test('generateMissingPages(templates, config) rejects a page.meta.ts for a route the app has no page at, without writing anything', async () => {
|
|
213
|
+
const root = await createProject()
|
|
214
|
+
try {
|
|
215
|
+
const meta = await writeMetaTemplate(root, 'docs')
|
|
216
|
+
|
|
217
|
+
await assert.rejects(() => generateMissingPages([meta], { projectRoot: root }), /page\.meta\.ts has no default export/)
|
|
218
|
+
|
|
219
|
+
assert.equal(existsSync(join(root, 'app/(templates)')), false)
|
|
220
|
+
} finally {
|
|
221
|
+
await rm(root, { recursive: true, force: true })
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
test('a page.meta.ts over a page the app already has is accepted, and no route file is written for it', async () => {
|
|
226
|
+
const root = await createProject()
|
|
227
|
+
try {
|
|
228
|
+
await writeProjectFile(root, 'app/docs/page.tsx', PAGE_WITH_COMPONENT)
|
|
229
|
+
const meta = await writeMetaTemplate(root, 'docs')
|
|
230
|
+
|
|
231
|
+
await generateMissingPages([meta], { projectRoot: root })
|
|
232
|
+
assert.equal(existsSync(join(root, 'app/(templates)/docs/page.tsx')), false)
|
|
233
|
+
|
|
234
|
+
const out = await generateTemplateRegistry([meta], { outputDir: join(root, '.nextspark/registries'), projectRoot: root })
|
|
235
|
+
assert.match(out, /'app\/docs\/page\.tsx': \{\n\s*appPath: 'app\/docs\/page\.tsx',\n\s*component: null,/)
|
|
236
|
+
} finally {
|
|
237
|
+
await rm(root, { recursive: true, force: true })
|
|
238
|
+
}
|
|
239
|
+
})
|