@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
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one way the generator writes, creates directories, renames and removes:
|
|
3
|
+
* the registry build and the rest of scripts/build, and `nextspark sync:app`,
|
|
4
|
+
* which loads this module from the core installed in the project. No other file
|
|
5
|
+
* of the generator calls fs to change anything; a test fails when one does.
|
|
6
|
+
*
|
|
7
|
+
* Each call is bound to a root, the directory it may change things under: the
|
|
8
|
+
* project, or, for the themes of the monorepo, the monorepo. Before it touches
|
|
9
|
+
* anything the call looks at the path it was given, from the root down, and
|
|
10
|
+
* refuses - throwing an error with the code UNSAFE_WRITE and writing nothing -
|
|
11
|
+
* when the path is outside the root, when something along it inside the root is
|
|
12
|
+
* a symlink, the path itself included, when a part of it can't be looked at,
|
|
13
|
+
* when what exists of it resolves outside the root, or when the file it would
|
|
14
|
+
* write into has other hard links, which writing it would change too. What goes
|
|
15
|
+
* through a symlink lands wherever the symlink points; what a hard link shares
|
|
16
|
+
* is written wherever the other names are.
|
|
17
|
+
*
|
|
18
|
+
* What the check looks at is what is on disk when the call runs. A file is
|
|
19
|
+
* opened without following a symlink put in its place after the check; a
|
|
20
|
+
* directory along the path replaced by one in between is not caught.
|
|
21
|
+
*
|
|
22
|
+
* @module core/scripts/build/safe-fs
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
closeSync,
|
|
27
|
+
constants,
|
|
28
|
+
fchmodSync,
|
|
29
|
+
fstatSync,
|
|
30
|
+
lstatSync,
|
|
31
|
+
mkdirSync,
|
|
32
|
+
mkdtempSync,
|
|
33
|
+
openSync,
|
|
34
|
+
readSync,
|
|
35
|
+
realpathSync,
|
|
36
|
+
renameSync,
|
|
37
|
+
rmdirSync,
|
|
38
|
+
rmSync,
|
|
39
|
+
unlinkSync,
|
|
40
|
+
writeFileSync,
|
|
41
|
+
writeSync,
|
|
42
|
+
} from 'fs'
|
|
43
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from 'path'
|
|
44
|
+
|
|
45
|
+
/** The code of the error a refused call throws. */
|
|
46
|
+
export const UNSAFE_WRITE = 'UNSAFE_WRITE'
|
|
47
|
+
|
|
48
|
+
const NO_FOLLOW = constants.O_NOFOLLOW ?? 0
|
|
49
|
+
|
|
50
|
+
/** The open flags of the writeFile flags the generator uses, none of them following a symlink. */
|
|
51
|
+
const WRITE_FLAGS = {
|
|
52
|
+
w: constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | NO_FOLLOW,
|
|
53
|
+
wx: constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | NO_FOLLOW,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The size of the buffer copyFile reads and writes through: memory use stays at this regardless of file size. */
|
|
57
|
+
export const COPY_BLOCK_SIZE = 1024 * 1024
|
|
58
|
+
|
|
59
|
+
/** The copyFile mode bits safe-fs can honor without following the destination: no flag, or COPYFILE_EXCL. */
|
|
60
|
+
const KNOWN_COPY_MODE = constants.COPYFILE_EXCL
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether `error` is a refusal of this module: nothing was written, and the
|
|
64
|
+
* caller must not carry on as if it had been, or read it as a missing file.
|
|
65
|
+
*
|
|
66
|
+
* @param {unknown} error
|
|
67
|
+
* @returns {boolean}
|
|
68
|
+
*/
|
|
69
|
+
export function isUnsafeWrite(error) {
|
|
70
|
+
return error instanceof Error && error.code === UNSAFE_WRITE
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** A path from `base` with forward slashes, as a refusal names it. */
|
|
74
|
+
function fromBase(base, absolute) {
|
|
75
|
+
return relative(base, absolute).split(sep).join('/') || '.'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** `path` with a separator at its end, for telling whether another path is under it. */
|
|
79
|
+
function withSeparator(path) {
|
|
80
|
+
return path.endsWith(sep) ? path : `${path}${sep}`
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function realpathOrNull(path) {
|
|
84
|
+
try {
|
|
85
|
+
return realpathSync(path)
|
|
86
|
+
} catch {
|
|
87
|
+
return null
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Where `absolute` is looked at from: the root as given when it is under it, or
|
|
93
|
+
* else the shortest of its own leading directories that is the root under
|
|
94
|
+
* another spelling - through a symlink above the root, like /tmp on macOS -
|
|
95
|
+
* whatever comes after that one being looked at part by part; null when none is.
|
|
96
|
+
*/
|
|
97
|
+
function spelledRoot(absolute, rootPath, rootReal) {
|
|
98
|
+
if (absolute.startsWith(withSeparator(rootPath))) return rootPath
|
|
99
|
+
const leading = []
|
|
100
|
+
for (let path = dirname(absolute); ; path = dirname(path)) {
|
|
101
|
+
leading.unshift(path)
|
|
102
|
+
if (dirname(path) === path) break
|
|
103
|
+
}
|
|
104
|
+
for (const path of leading) {
|
|
105
|
+
try {
|
|
106
|
+
if (realpathSync(path) === rootReal) return path
|
|
107
|
+
} catch {
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* What is wrong with changing `target` under `root`, or null when nothing is.
|
|
116
|
+
* A target spelled with another path to the root - through a symlink above it,
|
|
117
|
+
* like /tmp on macOS - is looked at from where that path reaches the root.
|
|
118
|
+
*
|
|
119
|
+
* @param {string} root - The directory the change may be made under
|
|
120
|
+
* @param {string} target - An absolute path, or one from the root
|
|
121
|
+
* @param {{ writesInto?: boolean, root?: boolean }} [options] - `writesInto` when an existing file at `target` is written
|
|
122
|
+
* into; `root` when the root itself may be the target, as for creating a directory that is there already
|
|
123
|
+
* @returns {{ target: string, path: string, problem: string } | null} The target and the path the problem is at, both
|
|
124
|
+
* from the root, and the problem
|
|
125
|
+
*/
|
|
126
|
+
export function unsafeWriteProblem(root, target, { writesInto = false, root: rootAllowed = false } = {}) {
|
|
127
|
+
const rootPath = resolve(root)
|
|
128
|
+
const absolute = resolve(rootPath, target)
|
|
129
|
+
let rootReal
|
|
130
|
+
try {
|
|
131
|
+
rootReal = realpathSync(rootPath)
|
|
132
|
+
} catch {
|
|
133
|
+
return { target: absolute, path: rootPath, problem: "can't be resolved" }
|
|
134
|
+
}
|
|
135
|
+
if (absolute === rootPath) {
|
|
136
|
+
return rootAllowed ? null : { target: '.', path: '.', problem: 'is the root itself, which is not changed' }
|
|
137
|
+
}
|
|
138
|
+
const base = spelledRoot(absolute, rootPath, rootReal)
|
|
139
|
+
if (base === null && rootAllowed && realpathOrNull(absolute) === rootReal) return null
|
|
140
|
+
const along = base === null ? null : relative(base, absolute)
|
|
141
|
+
if (along === null || along === '' || isAbsolute(along) || along === '..' || along.startsWith(`..${sep}`)) {
|
|
142
|
+
return { target: absolute, path: absolute, problem: `is outside ${rootPath}` }
|
|
143
|
+
}
|
|
144
|
+
const shownTarget = fromBase(base, absolute)
|
|
145
|
+
|
|
146
|
+
let deepest = base
|
|
147
|
+
let current = base
|
|
148
|
+
const parts = along.split(sep)
|
|
149
|
+
for (let depth = 0; depth < parts.length; depth++) {
|
|
150
|
+
current = join(current, parts[depth])
|
|
151
|
+
let stat
|
|
152
|
+
try {
|
|
153
|
+
stat = lstatSync(current)
|
|
154
|
+
} catch (error) {
|
|
155
|
+
// Nothing is there yet, or a file stands where a directory goes and the call itself fails
|
|
156
|
+
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') break
|
|
157
|
+
return { target: shownTarget, path: fromBase(base, current), problem: "can't be looked at" }
|
|
158
|
+
}
|
|
159
|
+
if (stat.isSymbolicLink()) {
|
|
160
|
+
return { target: shownTarget, path: fromBase(base, current), problem: 'is a symlink' }
|
|
161
|
+
}
|
|
162
|
+
deepest = current
|
|
163
|
+
if (depth === parts.length - 1) {
|
|
164
|
+
if (writesInto && stat.isFile() && stat.nlink > 1) {
|
|
165
|
+
return { target: shownTarget, path: shownTarget, problem: 'has other hard links' }
|
|
166
|
+
}
|
|
167
|
+
} else if (!stat.isDirectory()) {
|
|
168
|
+
break
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let deepestReal
|
|
173
|
+
try {
|
|
174
|
+
deepestReal = realpathSync(deepest)
|
|
175
|
+
} catch {
|
|
176
|
+
return { target: shownTarget, path: fromBase(base, deepest), problem: "can't be resolved" }
|
|
177
|
+
}
|
|
178
|
+
if (deepestReal !== rootReal && !deepestReal.startsWith(withSeparator(rootReal))) {
|
|
179
|
+
return { target: shownTarget, path: fromBase(base, deepest), problem: `resolves outside ${rootReal}` }
|
|
180
|
+
}
|
|
181
|
+
return null
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** The error a refused call throws. */
|
|
185
|
+
function refusal(operation, root, { target, path, problem }) {
|
|
186
|
+
const error = new Error(
|
|
187
|
+
`Refused to ${operation} ${target}: ${path} ${problem}, and nothing was written. ` +
|
|
188
|
+
'What is written or removed through a symlink, a hard link or a path out of the project lands outside it: ' +
|
|
189
|
+
'make each part of the path a file or directory of its own inside the project, and run the command again.'
|
|
190
|
+
)
|
|
191
|
+
return Object.assign(error, { code: UNSAFE_WRITE, root: resolve(root), target, path, problem })
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** `target` as an absolute path under `root`, once changing it is safe; throws the refusal otherwise. */
|
|
195
|
+
function checked(operation, root, target, options) {
|
|
196
|
+
const problem = unsafeWriteProblem(root, target, options)
|
|
197
|
+
if (problem) throw refusal(operation, root, problem)
|
|
198
|
+
return resolve(resolve(root), target)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** The flags a writeFile call opens with: the ones the generator uses, never following a symlink. */
|
|
202
|
+
function writeOptions(options) {
|
|
203
|
+
const normalized = typeof options === 'string' ? { encoding: options } : { ...options }
|
|
204
|
+
const flag = normalized.flag ?? 'w'
|
|
205
|
+
if (!(flag in WRITE_FLAGS)) {
|
|
206
|
+
throw new Error(`safe-fs writes with the flags ${Object.keys(WRITE_FLAGS).join(' and ')}, not ${flag}`)
|
|
207
|
+
}
|
|
208
|
+
return { ...normalized, flag: WRITE_FLAGS[flag] }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* The calls that change things under `root`, each refusing - with an error whose
|
|
213
|
+
* code is UNSAFE_WRITE, before touching anything - what `unsafeWriteProblem`
|
|
214
|
+
* finds wrong with its path. Paths are absolute, or from the root.
|
|
215
|
+
*
|
|
216
|
+
* @param {string} root - The directory these calls may change things under
|
|
217
|
+
*/
|
|
218
|
+
export function projectFiles(root) {
|
|
219
|
+
const writeFile = (path, data, options) => {
|
|
220
|
+
writeFileSync(checked('write', root, path, { writesInto: true }), data, writeOptions(options))
|
|
221
|
+
}
|
|
222
|
+
const mkdir = (path, options) => mkdirSync(checked('create', root, path, { root: true }), options)
|
|
223
|
+
const mkdtemp = prefix => {
|
|
224
|
+
checked('create a directory beside', root, prefix)
|
|
225
|
+
return mkdtempSync(resolve(resolve(root), prefix))
|
|
226
|
+
}
|
|
227
|
+
const copyFile = (source, destination, mode = 0) => {
|
|
228
|
+
if (mode & ~KNOWN_COPY_MODE) {
|
|
229
|
+
throw new Error(`safe-fs copies with COPYFILE_EXCL or no mode, not ${mode}`)
|
|
230
|
+
}
|
|
231
|
+
const target = checked('copy to', root, destination, { writesInto: true })
|
|
232
|
+
const sourceFd = openSync(source, constants.O_RDONLY)
|
|
233
|
+
try {
|
|
234
|
+
const sourceMode = fstatSync(sourceFd).mode
|
|
235
|
+
const flag = mode & constants.COPYFILE_EXCL ? WRITE_FLAGS.wx : WRITE_FLAGS.w
|
|
236
|
+
const destFd = openSync(target, flag, sourceMode & 0o777)
|
|
237
|
+
try {
|
|
238
|
+
const buffer = Buffer.allocUnsafe(COPY_BLOCK_SIZE)
|
|
239
|
+
for (;;) {
|
|
240
|
+
const bytesRead = readSync(sourceFd, buffer, 0, COPY_BLOCK_SIZE, null)
|
|
241
|
+
if (bytesRead === 0) break
|
|
242
|
+
writeSync(destFd, buffer, 0, bytesRead, null)
|
|
243
|
+
}
|
|
244
|
+
fchmodSync(destFd, sourceMode & 0o777)
|
|
245
|
+
} finally {
|
|
246
|
+
closeSync(destFd)
|
|
247
|
+
}
|
|
248
|
+
} finally {
|
|
249
|
+
closeSync(sourceFd)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const rm = (path, options) => rmSync(checked('remove', root, path), options)
|
|
253
|
+
const rmdir = path => rmdirSync(checked('remove', root, path))
|
|
254
|
+
const unlink = path => unlinkSync(checked('remove', root, path))
|
|
255
|
+
const rename = (from, to) => {
|
|
256
|
+
const source = checked('move', root, from)
|
|
257
|
+
renameSync(source, checked('move to', root, to))
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
root: resolve(root),
|
|
262
|
+
writeFileSync: writeFile,
|
|
263
|
+
mkdirSync: mkdir,
|
|
264
|
+
mkdtempSync: mkdtemp,
|
|
265
|
+
copyFileSync: copyFile,
|
|
266
|
+
rmSync: rm,
|
|
267
|
+
rmdirSync: rmdir,
|
|
268
|
+
unlinkSync: unlink,
|
|
269
|
+
renameSync: rename,
|
|
270
|
+
writeFile: async (...args) => writeFile(...args),
|
|
271
|
+
mkdir: async (...args) => mkdir(...args),
|
|
272
|
+
mkdtemp: async (...args) => mkdtemp(...args),
|
|
273
|
+
copyFile: async (...args) => copyFile(...args),
|
|
274
|
+
rm: async (...args) => rm(...args),
|
|
275
|
+
rmdir: async (...args) => rmdir(...args),
|
|
276
|
+
unlink: async (...args) => unlink(...args),
|
|
277
|
+
rename: async (...args) => rename(...args),
|
|
278
|
+
}
|
|
279
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What the registry build would change in a project's app/(templates), worked
|
|
5
|
+
* out without writing anything. `nextspark sync:app` runs it to name those
|
|
6
|
+
* changes in --dry-run, and to count them before asking for confirmation.
|
|
7
|
+
*
|
|
8
|
+
* The project comes from NEXTSPARK_PROJECT_ROOT, as for registry.mjs. Stdin
|
|
9
|
+
* carries a JSON object of files under app/, by path from the project root, to
|
|
10
|
+
* read as having that content (null: removed) instead of what is on disk: what
|
|
11
|
+
* the sync about to run leaves there, since the build copies app/ layouts into
|
|
12
|
+
* the tree. The result is printed on a line of its own, after
|
|
13
|
+
* `nextspark-templates-plan:`, as { create, replace, remove } with paths from
|
|
14
|
+
* the project root.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// First, so what any module prints as it loads is escaped too
|
|
18
|
+
import '../utils/console-guard.mjs'
|
|
19
|
+
|
|
20
|
+
import { getConfig } from './registry/config.mjs'
|
|
21
|
+
import { discoverTemplates } from './registry/discovery/templates.mjs'
|
|
22
|
+
import { planMissingPages } from './registry/post-build/page-generator.mjs'
|
|
23
|
+
import { jsonLine, messageLines } from '../utils/index.mjs'
|
|
24
|
+
|
|
25
|
+
const RESULT_MARKER = 'nextspark-templates-plan:'
|
|
26
|
+
|
|
27
|
+
async function readAppFiles() {
|
|
28
|
+
if (process.stdin.isTTY) return new Map()
|
|
29
|
+
|
|
30
|
+
let input = ''
|
|
31
|
+
for await (const chunk of process.stdin) input += chunk
|
|
32
|
+
return new Map(Object.entries(input.trim() ? JSON.parse(input) : {}))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const appFiles = await readAppFiles()
|
|
37
|
+
const config = getConfig()
|
|
38
|
+
const templates = await discoverTemplates(config)
|
|
39
|
+
const changes = await planMissingPages(templates, config, appFiles)
|
|
40
|
+
console.log(`${RESULT_MARKER}${jsonLine(changes)}`)
|
|
41
|
+
} catch (error) {
|
|
42
|
+
for (const line of messageLines(error)) console.error(line)
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
package/scripts/build/theme.mjs
CHANGED
|
@@ -11,11 +11,13 @@
|
|
|
11
11
|
* node scripts/build-theme.mjs --watch # Build and watch for changes
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import
|
|
14
|
+
import { existsSync, readdirSync, readFileSync, statSync, watch } from 'fs'
|
|
15
15
|
import path from 'path'
|
|
16
16
|
import { fileURLToPath } from 'url'
|
|
17
17
|
import dotenv from 'dotenv'
|
|
18
18
|
import { getConfig } from './registry/config.mjs'
|
|
19
|
+
import { isUnsafeWrite, projectFiles } from './safe-fs.mjs'
|
|
20
|
+
import { rewriteBelowGeneratedTag } from '../utils/generated-tag.mjs'
|
|
19
21
|
|
|
20
22
|
const __filename = fileURLToPath(import.meta.url)
|
|
21
23
|
const __dirname = path.dirname(__filename)
|
|
@@ -70,8 +72,8 @@ ${expectedImport}
|
|
|
70
72
|
`
|
|
71
73
|
|
|
72
74
|
// Check if file exists and has correct import
|
|
73
|
-
if (
|
|
74
|
-
const currentContent =
|
|
75
|
+
if (existsSync(appGlobalsCssPath)) {
|
|
76
|
+
const currentContent = readFileSync(appGlobalsCssPath, 'utf8')
|
|
75
77
|
|
|
76
78
|
// Extract current import (handle various formats)
|
|
77
79
|
const importMatch = currentContent.match(/@import\s+["']([^"']+)["'];?/)
|
|
@@ -84,20 +86,21 @@ ${expectedImport}
|
|
|
84
86
|
return false // Already correct, no update needed
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
// Update only the import line, preserving any custom comments
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
// Update only the import line, preserving any custom comments. A generated
|
|
90
|
+
// tag sync:app put on the file keeps telling the truth: rehashed when the
|
|
91
|
+
// file still matched it, left as it was when the project had changed it.
|
|
92
|
+
const updatedContent = rewriteBelowGeneratedTag(currentContent, body =>
|
|
93
|
+
body.replace(/@import\s+["'][^"']+["'];?/, expectedImport)
|
|
91
94
|
)
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
projectFiles(config.projectRoot).writeFileSync(appGlobalsCssPath, updatedContent)
|
|
94
97
|
console.log(` 🔄 Updated app/globals.css import: ${relativePath}`)
|
|
95
98
|
return true
|
|
96
99
|
}
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
// File doesn't exist or has no import - write full template
|
|
100
|
-
|
|
103
|
+
projectFiles(config.projectRoot).writeFileSync(appGlobalsCssPath, template)
|
|
101
104
|
console.log(` ✅ Created app/globals.css with import: ${relativePath}`)
|
|
102
105
|
return true
|
|
103
106
|
}
|
|
@@ -106,16 +109,16 @@ ${expectedImport}
|
|
|
106
109
|
* Recursively copy directory contents
|
|
107
110
|
*/
|
|
108
111
|
function copyRecursive(src, dest) {
|
|
109
|
-
const stats =
|
|
112
|
+
const stats = statSync(src)
|
|
110
113
|
|
|
111
114
|
if (stats.isDirectory()) {
|
|
112
115
|
// Create directory if it doesn't exist
|
|
113
|
-
if (!
|
|
114
|
-
|
|
116
|
+
if (!existsSync(dest)) {
|
|
117
|
+
projectFiles(rootDir).mkdirSync(dest, { recursive: true })
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
// Read directory contents
|
|
118
|
-
const entries =
|
|
121
|
+
const entries = readdirSync(src, { withFileTypes: true })
|
|
119
122
|
|
|
120
123
|
for (const entry of entries) {
|
|
121
124
|
const srcPath = path.join(src, entry.name)
|
|
@@ -124,11 +127,11 @@ function copyRecursive(src, dest) {
|
|
|
124
127
|
if (entry.isDirectory()) {
|
|
125
128
|
copyRecursive(srcPath, destPath)
|
|
126
129
|
} else {
|
|
127
|
-
|
|
130
|
+
projectFiles(rootDir).copyFileSync(srcPath, destPath)
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
133
|
} else {
|
|
131
|
-
|
|
134
|
+
projectFiles(rootDir).copyFileSync(src, dest)
|
|
132
135
|
}
|
|
133
136
|
}
|
|
134
137
|
|
|
@@ -138,11 +141,11 @@ function copyRecursive(src, dest) {
|
|
|
138
141
|
function countFiles(dir) {
|
|
139
142
|
let count = 0
|
|
140
143
|
|
|
141
|
-
if (!
|
|
144
|
+
if (!existsSync(dir)) {
|
|
142
145
|
return 0
|
|
143
146
|
}
|
|
144
147
|
|
|
145
|
-
const entries =
|
|
148
|
+
const entries = readdirSync(dir, { withFileTypes: true })
|
|
146
149
|
|
|
147
150
|
for (const entry of entries) {
|
|
148
151
|
if (entry.isDirectory()) {
|
|
@@ -163,32 +166,32 @@ function copyBlockThumbnails(themePath, activeTheme) {
|
|
|
163
166
|
const blocksPath = path.join(themePath, 'blocks')
|
|
164
167
|
const publicBlocksPath = path.join(rootDir, 'public', 'theme', 'blocks')
|
|
165
168
|
|
|
166
|
-
if (!
|
|
169
|
+
if (!existsSync(blocksPath)) {
|
|
167
170
|
return 0
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
// Ensure public/theme/blocks directory exists
|
|
171
|
-
if (!
|
|
172
|
-
|
|
174
|
+
if (!existsSync(publicBlocksPath)) {
|
|
175
|
+
projectFiles(rootDir).mkdirSync(publicBlocksPath, { recursive: true })
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
let copiedCount = 0
|
|
176
|
-
const blockDirs =
|
|
179
|
+
const blockDirs = readdirSync(blocksPath, { withFileTypes: true })
|
|
177
180
|
.filter(dirent => dirent.isDirectory())
|
|
178
181
|
|
|
179
182
|
for (const blockDir of blockDirs) {
|
|
180
183
|
const thumbnailSrc = path.join(blocksPath, blockDir.name, 'thumbnail.png')
|
|
181
184
|
|
|
182
|
-
if (
|
|
185
|
+
if (existsSync(thumbnailSrc)) {
|
|
183
186
|
const thumbnailDestDir = path.join(publicBlocksPath, blockDir.name)
|
|
184
187
|
const thumbnailDest = path.join(thumbnailDestDir, 'thumbnail.png')
|
|
185
188
|
|
|
186
189
|
// Create destination directory if needed
|
|
187
|
-
if (!
|
|
188
|
-
|
|
190
|
+
if (!existsSync(thumbnailDestDir)) {
|
|
191
|
+
projectFiles(rootDir).mkdirSync(thumbnailDestDir, { recursive: true })
|
|
189
192
|
}
|
|
190
193
|
|
|
191
|
-
|
|
194
|
+
projectFiles(rootDir).copyFileSync(thumbnailSrc, thumbnailDest)
|
|
192
195
|
copiedCount++
|
|
193
196
|
}
|
|
194
197
|
}
|
|
@@ -205,11 +208,11 @@ function copyThemePublicAssets(themePath, activeTheme) {
|
|
|
205
208
|
const publicThemePath = path.join(rootDir, 'public', 'theme')
|
|
206
209
|
|
|
207
210
|
// Ensure public/theme directory exists
|
|
208
|
-
if (!
|
|
209
|
-
|
|
211
|
+
if (!existsSync(publicThemePath)) {
|
|
212
|
+
projectFiles(rootDir).mkdirSync(publicThemePath, { recursive: true })
|
|
210
213
|
}
|
|
211
214
|
|
|
212
|
-
if (!
|
|
215
|
+
if (!existsSync(themePublicPath)) {
|
|
213
216
|
console.log(` ℹ️ No public assets directory found for theme: ${activeTheme}`)
|
|
214
217
|
return
|
|
215
218
|
}
|
|
@@ -225,6 +228,7 @@ function copyThemePublicAssets(themePath, activeTheme) {
|
|
|
225
228
|
console.log(` ✅ Copied ${fileCount} asset(s) to public/theme/`)
|
|
226
229
|
}
|
|
227
230
|
} catch (error) {
|
|
231
|
+
if (isUnsafeWrite(error)) throw error
|
|
228
232
|
console.error(` ❌ Failed to copy public assets:`, error)
|
|
229
233
|
}
|
|
230
234
|
}
|
|
@@ -279,8 +283,8 @@ export async function buildTheme(projectRoot = null) {
|
|
|
279
283
|
const themePath = path.join(config.contentsDir, 'themes', activeTheme)
|
|
280
284
|
|
|
281
285
|
// Ensure output directory exists
|
|
282
|
-
if (!
|
|
283
|
-
|
|
286
|
+
if (!existsSync(outputDir)) {
|
|
287
|
+
projectFiles(config.projectRoot).mkdirSync(outputDir, { recursive: true })
|
|
284
288
|
}
|
|
285
289
|
|
|
286
290
|
// Sync app/globals.css to import from the active theme
|
|
@@ -288,7 +292,7 @@ export async function buildTheme(projectRoot = null) {
|
|
|
288
292
|
syncAppGlobalsCss(config, activeTheme)
|
|
289
293
|
|
|
290
294
|
// Copy theme public assets (for all themes, including default)
|
|
291
|
-
if (
|
|
295
|
+
if (existsSync(themePath)) {
|
|
292
296
|
copyThemePublicAssets(themePath, activeTheme)
|
|
293
297
|
|
|
294
298
|
// Copy block thumbnails to public/theme/blocks/
|
|
@@ -299,22 +303,22 @@ export async function buildTheme(projectRoot = null) {
|
|
|
299
303
|
}
|
|
300
304
|
|
|
301
305
|
// Build theme (including default - all themes load their own styles)
|
|
302
|
-
if (!
|
|
306
|
+
if (!existsSync(themePath)) {
|
|
303
307
|
console.error(`❌ Theme directory not found: ${themePath}`)
|
|
304
308
|
console.log('📁 Available themes:')
|
|
305
309
|
|
|
306
310
|
const themesDir = path.join(rootDir, 'contents', 'themes')
|
|
307
|
-
const availableThemes =
|
|
311
|
+
const availableThemes = readdirSync(themesDir)
|
|
308
312
|
.filter(item => {
|
|
309
313
|
const itemPath = path.join(themesDir, item)
|
|
310
|
-
return
|
|
314
|
+
return statSync(itemPath).isDirectory() && item !== 'node_modules'
|
|
311
315
|
})
|
|
312
316
|
|
|
313
317
|
availableThemes.forEach(theme => console.log(` - ${theme}`))
|
|
314
318
|
|
|
315
319
|
// Fallback to empty (core styles only)
|
|
316
320
|
console.log('🔄 Falling back to core styles only')
|
|
317
|
-
|
|
321
|
+
projectFiles(config.projectRoot).writeFileSync(outputPath, `/* Fallback: Theme '${activeTheme}' not found, using core styles */\n`)
|
|
318
322
|
return
|
|
319
323
|
}
|
|
320
324
|
|
|
@@ -328,8 +332,8 @@ export async function buildTheme(projectRoot = null) {
|
|
|
328
332
|
let globalCSS = ''
|
|
329
333
|
let componentCSS = ''
|
|
330
334
|
|
|
331
|
-
if (
|
|
332
|
-
globalCSS =
|
|
335
|
+
if (existsSync(globalStylesPath)) {
|
|
336
|
+
globalCSS = readFileSync(globalStylesPath, 'utf8')
|
|
333
337
|
console.log(` ✅ Loaded globals.css (${globalCSS.length} chars)`)
|
|
334
338
|
|
|
335
339
|
// Validate that theme has all required sections
|
|
@@ -338,8 +342,8 @@ export async function buildTheme(projectRoot = null) {
|
|
|
338
342
|
console.warn(` ⚠️ globals.css not found: ${globalStylesPath}`)
|
|
339
343
|
}
|
|
340
344
|
|
|
341
|
-
if (
|
|
342
|
-
componentCSS =
|
|
345
|
+
if (existsSync(componentStylesPath)) {
|
|
346
|
+
componentCSS = readFileSync(componentStylesPath, 'utf8')
|
|
343
347
|
console.log(` ✅ Loaded components.css (${componentCSS.length} chars)`)
|
|
344
348
|
} else {
|
|
345
349
|
console.warn(` ⚠️ components.css not found: ${componentStylesPath}`)
|
|
@@ -362,7 +366,7 @@ ${componentCSS}
|
|
|
362
366
|
`
|
|
363
367
|
|
|
364
368
|
// Write final CSS to .next directory (for build cache)
|
|
365
|
-
|
|
369
|
+
projectFiles(config.projectRoot).writeFileSync(outputPath, finalCSS)
|
|
366
370
|
|
|
367
371
|
console.log(`✅ Theme built successfully!`)
|
|
368
372
|
console.log(`📄 Output: ${outputPath} (${finalCSS.length} chars)`)
|
|
@@ -379,7 +383,7 @@ ${componentCSS}
|
|
|
379
383
|
* Falling back to core styles only.
|
|
380
384
|
*/
|
|
381
385
|
`
|
|
382
|
-
|
|
386
|
+
projectFiles(config.projectRoot).writeFileSync(outputPath, errorCSS)
|
|
383
387
|
|
|
384
388
|
throw error
|
|
385
389
|
}
|
|
@@ -399,7 +403,7 @@ async function watchTheme() {
|
|
|
399
403
|
let debounceTimer = null
|
|
400
404
|
|
|
401
405
|
try {
|
|
402
|
-
const watcher =
|
|
406
|
+
const watcher = watch(stylesPath, { recursive: true }, (eventType, filename) => {
|
|
403
407
|
if (filename && (filename.endsWith('.css'))) {
|
|
404
408
|
clearTimeout(debounceTimer)
|
|
405
409
|
debounceTimer = setTimeout(async () => {
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
* Usage: node core/scripts/build/update-tsconfig.mjs
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import
|
|
18
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'fs'
|
|
19
19
|
import path from 'path'
|
|
20
20
|
import { fileURLToPath } from 'url'
|
|
21
21
|
import dotenv from 'dotenv'
|
|
22
|
+
import { projectFiles } from './safe-fs.mjs'
|
|
22
23
|
|
|
23
24
|
const __filename = fileURLToPath(import.meta.url)
|
|
24
25
|
const __dirname = path.dirname(__filename)
|
|
@@ -44,14 +45,14 @@ const PLUGINS_DIR = path.join(ROOT_DIR, 'contents/plugins')
|
|
|
44
45
|
* Get all theme directories
|
|
45
46
|
*/
|
|
46
47
|
function getAllThemes() {
|
|
47
|
-
if (!
|
|
48
|
+
if (!existsSync(THEMES_DIR)) {
|
|
48
49
|
return []
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
return
|
|
52
|
+
return readdirSync(THEMES_DIR)
|
|
52
53
|
.filter(item => {
|
|
53
54
|
const themePath = path.join(THEMES_DIR, item)
|
|
54
|
-
return
|
|
55
|
+
return statSync(themePath).isDirectory()
|
|
55
56
|
})
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -59,14 +60,14 @@ function getAllThemes() {
|
|
|
59
60
|
* Get all plugin directories
|
|
60
61
|
*/
|
|
61
62
|
function getAllPlugins() {
|
|
62
|
-
if (!
|
|
63
|
+
if (!existsSync(PLUGINS_DIR)) {
|
|
63
64
|
return []
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
return
|
|
67
|
+
return readdirSync(PLUGINS_DIR)
|
|
67
68
|
.filter(item => {
|
|
68
69
|
const pluginPath = path.join(PLUGINS_DIR, item)
|
|
69
|
-
return
|
|
70
|
+
return statSync(pluginPath).isDirectory()
|
|
70
71
|
})
|
|
71
72
|
}
|
|
72
73
|
|
|
@@ -76,20 +77,20 @@ function getAllPlugins() {
|
|
|
76
77
|
function getActivePlugins(activeTheme) {
|
|
77
78
|
const themeConfigPath = path.join(THEMES_DIR, activeTheme, 'theme.config.ts')
|
|
78
79
|
|
|
79
|
-
if (!
|
|
80
|
+
if (!existsSync(themeConfigPath)) {
|
|
80
81
|
// Try .js extension
|
|
81
82
|
const jsConfigPath = path.join(THEMES_DIR, activeTheme, 'theme.config.js')
|
|
82
|
-
if (!
|
|
83
|
+
if (!existsSync(jsConfigPath)) {
|
|
83
84
|
return []
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
try {
|
|
88
|
-
const configPath =
|
|
89
|
+
const configPath = existsSync(path.join(THEMES_DIR, activeTheme, 'theme.config.ts'))
|
|
89
90
|
? path.join(THEMES_DIR, activeTheme, 'theme.config.ts')
|
|
90
91
|
: path.join(THEMES_DIR, activeTheme, 'theme.config.js')
|
|
91
92
|
|
|
92
|
-
const configContent =
|
|
93
|
+
const configContent = readFileSync(configPath, 'utf8')
|
|
93
94
|
|
|
94
95
|
// Match plugins array: plugins: ['plugin1', 'plugin2']
|
|
95
96
|
const pluginsMatch = configContent.match(/plugins:\s*\[(.*?)\]/s)
|
|
@@ -119,14 +120,14 @@ function generateTsConfig() {
|
|
|
119
120
|
const activeTheme = process.env.NEXT_PUBLIC_ACTIVE_THEME
|
|
120
121
|
|
|
121
122
|
// Check if base template exists
|
|
122
|
-
if (!
|
|
123
|
+
if (!existsSync(TSCONFIG_BASE_PATH)) {
|
|
123
124
|
console.error('❌ tsconfig.base.json not found!')
|
|
124
125
|
console.error(' Please ensure tsconfig.base.json exists in the project root.')
|
|
125
126
|
process.exit(1)
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
// Read base template
|
|
129
|
-
const baseConfig = JSON.parse(
|
|
130
|
+
const baseConfig = JSON.parse(readFileSync(TSCONFIG_BASE_PATH, 'utf8'))
|
|
130
131
|
|
|
131
132
|
// In NPM mode, add @nextspark/* aliases for registry and core resolution
|
|
132
133
|
if (isNpmPackage) {
|
|
@@ -178,7 +179,7 @@ function generateTsConfig() {
|
|
|
178
179
|
console.warn('⚠️ NEXT_PUBLIC_ACTIVE_THEME not set')
|
|
179
180
|
console.warn(' Using base excludes only (all themes/plugins will be checked)')
|
|
180
181
|
baseConfig.exclude = baseExcludes
|
|
181
|
-
|
|
182
|
+
projectFiles(ROOT_DIR).writeFileSync(TSCONFIG_PATH, JSON.stringify(baseConfig, null, 2) + '\n', 'utf8')
|
|
182
183
|
console.log('📝 Generated tsconfig.json (no active theme)')
|
|
183
184
|
return
|
|
184
185
|
}
|
|
@@ -200,7 +201,7 @@ function generateTsConfig() {
|
|
|
200
201
|
baseConfig.exclude = [...baseExcludes, ...themeExcludes, ...pluginExcludes]
|
|
201
202
|
|
|
202
203
|
// Write generated tsconfig
|
|
203
|
-
|
|
204
|
+
projectFiles(ROOT_DIR).writeFileSync(TSCONFIG_PATH, JSON.stringify(baseConfig, null, 2) + '\n', 'utf8')
|
|
204
205
|
|
|
205
206
|
console.log('📝 Generated tsconfig.json')
|
|
206
207
|
console.log(` Active theme: ${activeTheme}`)
|