@nextsparkjs/core 0.1.0-beta.188 → 0.1.0-beta.189
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/ApiKeyDisplay.js +19 -19
- package/dist/components/api/keys/CreateApiKeyDialog.js +21 -21
- package/dist/components/app/layouts/PublicNavbar.d.ts.map +1 -1
- package/dist/components/app/layouts/PublicNavbar.js +34 -16
- package/dist/components/app/misc/ThemeToggle.d.ts.map +1 -1
- package/dist/components/app/misc/ThemeToggle.js +3 -4
- package/dist/components/auth/SessionCookieRefresher.d.ts +5 -0
- package/dist/components/auth/SessionCookieRefresher.d.ts.map +1 -1
- package/dist/components/auth/SessionCookieRefresher.js +7 -1
- package/dist/components/auth/forms/LoginForm.d.ts.map +1 -1
- package/dist/components/auth/forms/LoginForm.js +49 -12
- package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
- package/dist/components/auth/forms/SignupForm.js +4 -3
- package/dist/components/dashboard/block-editor/block-settings-panel.js +1 -1
- package/dist/components/dashboard/block-editor/floating-block-toolbar.js +1 -1
- package/dist/components/dashboard/block-editor/sortable-block.js +1 -1
- package/dist/components/dashboard/layouts/Sidebar.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/Sidebar.js +8 -6
- package/dist/components/dashboard/layouts/TopNavbar.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/TopNavbar.js +51 -54
- package/dist/components/dashboard/misc/NotificationsDropdown.d.ts.map +1 -1
- package/dist/components/dashboard/misc/NotificationsDropdown.js +17 -15
- package/dist/components/dashboard/misc/SearchDropdown.d.ts.map +1 -1
- package/dist/components/dashboard/misc/SearchDropdown.js +19 -22
- package/dist/components/dashboard/mobile/MobileBottomNav.js +1 -1
- package/dist/components/dashboard/mobile/MobileTopBar.js +2 -2
- package/dist/components/dashboard/navigation/DynamicNavigation.js +5 -5
- package/dist/components/devtools/BlockDetailViewer.js +1 -1
- package/dist/components/devtools/BlocksViewer.js +1 -1
- package/dist/components/entities/EntityChildManager.js +5 -5
- package/dist/components/entities/EntityDetailPage.d.ts.map +1 -1
- package/dist/components/entities/EntityDetailPage.js +16 -15
- package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
- package/dist/components/entities/EntityFieldRenderer.js +12 -7
- package/dist/components/entities/EntityPageHeader.js +5 -5
- package/dist/components/entities/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/entities/ErrorBoundary.js +20 -21
- package/dist/components/entities/wrappers/EntityDetailWrapper.js +5 -5
- package/dist/components/entities/wrappers/EntityFormWrapper.js +2 -2
- package/dist/components/entities/wrappers/EntityListWrapper.js +3 -3
- package/dist/components/permissions/NoPermission.js +10 -10
- package/dist/components/public/pageBuilder/PageRenderer.d.ts.map +1 -1
- package/dist/components/public/pageBuilder/PageRenderer.js +2 -1
- package/dist/components/settings/SettingsPageSkeleton.js +4 -4
- package/dist/components/settings/layouts/SettingsSidebar.js +6 -6
- package/dist/components/shared/MultiSelectFilter.d.ts +3 -1
- package/dist/components/shared/MultiSelectFilter.d.ts.map +1 -1
- package/dist/components/shared/MultiSelectFilter.js +7 -3
- package/dist/components/shared/SearchInput.d.ts +2 -0
- package/dist/components/shared/SearchInput.d.ts.map +1 -1
- package/dist/components/shared/SearchInput.js +64 -6
- package/dist/components/ui/address-input.js +18 -18
- package/dist/components/ui/audio-upload.js +9 -9
- package/dist/components/ui/combobox.d.ts.map +1 -1
- package/dist/components/ui/combobox.js +4 -4
- package/dist/components/ui/country-select.d.ts.map +1 -1
- package/dist/components/ui/country-select.js +3 -3
- package/dist/components/ui/currency-select.d.ts.map +1 -1
- package/dist/components/ui/currency-select.js +3 -3
- package/dist/components/ui/dynamic-icon.d.ts.map +1 -1
- package/dist/components/ui/dynamic-icon.js +3 -2
- package/dist/components/ui/file-upload.js +7 -7
- package/dist/components/ui/image-upload.js +8 -8
- package/dist/components/ui/multi-select.d.ts +2 -1
- package/dist/components/ui/multi-select.d.ts.map +1 -1
- package/dist/components/ui/multi-select.js +4 -3
- package/dist/components/ui/phone-input.d.ts.map +1 -1
- package/dist/components/ui/phone-input.js +3 -3
- package/dist/components/ui/relation-display.js +1 -1
- package/dist/components/ui/simple-relation-select.d.ts +2 -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/tags-input.d.ts.map +1 -1
- package/dist/components/ui/tags-input.js +1 -1
- package/dist/components/ui/timezone-select.d.ts.map +1 -1
- package/dist/components/ui/timezone-select.js +3 -3
- package/dist/components/ui/user-display.js +4 -4
- package/dist/components/ui/user-select.d.ts.map +1 -1
- package/dist/components/ui/user-select.js +9 -9
- package/dist/components/ui/video-upload.js +9 -9
- package/dist/contexts/SubscriptionContext.d.ts.map +1 -1
- package/dist/contexts/SubscriptionContext.js +5 -1
- package/dist/contexts/TeamContext.d.ts.map +1 -1
- package/dist/contexts/TeamContext.js +83 -26
- package/dist/emails/otp-verification.d.ts.map +1 -1
- package/dist/emails/otp-verification.js +3 -2
- package/dist/hooks/useAccountPreferencesSync.d.ts +13 -0
- package/dist/hooks/useAccountPreferencesSync.d.ts.map +1 -0
- package/dist/hooks/useAccountPreferencesSync.js +95 -0
- package/dist/hooks/useAuth.d.ts +202 -15
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +49 -13
- package/dist/hooks/useEntityConfig.js +2 -2
- package/dist/hooks/useEntitySearch.js +10 -10
- package/dist/hooks/useNotifications.js +12 -12
- package/dist/hooks/usePrefetch.d.ts.map +1 -1
- package/dist/hooks/usePrefetch.js +6 -2
- package/dist/hooks/useSearch.js +15 -15
- package/dist/hooks/useSessionCookieRefresh.d.ts +18 -1
- package/dist/hooks/useSessionCookieRefresh.d.ts.map +1 -1
- package/dist/hooks/useSessionCookieRefresh.js +17 -4
- package/dist/hooks/useSessionHint.d.ts +12 -0
- package/dist/hooks/useSessionHint.d.ts.map +1 -0
- package/dist/hooks/useSessionHint.js +15 -0
- package/dist/hooks/useTranslationPreload.js +6 -6
- package/dist/i18n.d.ts +13 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +2 -27
- package/dist/lib/actions/entity.actions.d.ts.map +1 -1
- package/dist/lib/actions/entity.actions.js +3 -2
- package/dist/lib/api/auth/dual-auth.d.ts +4 -1
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +5 -16
- package/dist/lib/api/entities.js +33 -33
- package/dist/lib/api/keys.d.ts +27 -27
- package/dist/lib/api/keys.js +27 -27
- package/dist/lib/auth/callback-url.d.ts +11 -0
- package/dist/lib/auth/callback-url.d.ts.map +1 -0
- package/dist/lib/auth/callback-url.js +14 -0
- package/dist/lib/auth/otp-config.d.ts +55 -0
- package/dist/lib/auth/otp-config.d.ts.map +1 -0
- package/dist/lib/auth/otp-config.js +48 -0
- package/dist/lib/auth/registration-helpers.d.ts.map +1 -1
- package/dist/lib/auth/registration-helpers.js +3 -1
- package/dist/lib/auth/session-hint.d.ts +31 -0
- package/dist/lib/auth/session-hint.d.ts.map +1 -0
- package/dist/lib/auth/session-hint.js +37 -0
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +6 -4
- package/dist/lib/blocks/loader.d.ts +3 -11
- package/dist/lib/blocks/loader.d.ts.map +1 -1
- package/dist/lib/blocks/loader.js +1 -9
- package/dist/lib/blocks/loader.server.d.ts +18 -0
- package/dist/lib/blocks/loader.server.d.ts.map +1 -0
- package/dist/lib/blocks/loader.server.js +11 -0
- package/dist/lib/blocks/sanitize-html.d.ts +31 -0
- package/dist/lib/blocks/sanitize-html.d.ts.map +1 -0
- package/dist/lib/blocks/sanitize-html.js +60 -0
- package/dist/lib/config/config-sync.d.ts +1 -0
- package/dist/lib/config/config-sync.d.ts.map +1 -1
- package/dist/lib/config/config-sync.js +4 -1
- package/dist/lib/config/types.d.ts +48 -0
- package/dist/lib/config/types.d.ts.map +1 -1
- package/dist/lib/email/types.d.ts +6 -0
- package/dist/lib/email/types.d.ts.map +1 -1
- package/dist/lib/entities/serialization.d.ts.map +1 -1
- package/dist/lib/entities/serialization.js +3 -6
- package/dist/lib/icons.d.ts +12 -0
- package/dist/lib/icons.d.ts.map +1 -0
- package/dist/lib/icons.js +30 -0
- package/dist/lib/locale.d.ts +25 -1
- package/dist/lib/locale.d.ts.map +1 -1
- package/dist/lib/locale.js +49 -35
- package/dist/lib/mcp/engine.js +1 -1
- package/dist/lib/mcp/errors.js +17 -17
- package/dist/lib/mcp/schema-builder.js +16 -16
- package/dist/lib/mcp/tool-generator.js +18 -18
- package/dist/lib/search-highlighting.d.ts +7 -0
- package/dist/lib/search-highlighting.d.ts.map +1 -1
- package/dist/lib/search-highlighting.js +6 -2
- package/dist/lib/selectors/core-selectors.d.ts +2 -0
- package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/auth.selectors.d.ts +1 -0
- package/dist/lib/selectors/domains/auth.selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/auth.selectors.js +1 -0
- package/dist/lib/selectors/domains/entities.selectors.d.ts +2 -0
- package/dist/lib/selectors/domains/entities.selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/entities.selectors.js +2 -0
- package/dist/lib/selectors/selectors.d.ts +4 -0
- package/dist/lib/selectors/selectors.d.ts.map +1 -1
- package/dist/lib/services/block.service.js +1 -1
- package/dist/lib/teams/active-team-cookie.d.ts +24 -0
- package/dist/lib/teams/active-team-cookie.d.ts.map +1 -0
- package/dist/lib/teams/active-team-cookie.js +17 -0
- package/dist/lib/teams/dashboard-team.d.ts +20 -0
- package/dist/lib/teams/dashboard-team.d.ts.map +1 -0
- package/dist/lib/teams/dashboard-team.js +38 -0
- package/dist/lib/teams/schema.d.ts.map +1 -1
- package/dist/lib/teams/schema.js +1 -1
- package/dist/lib/template-resolver.d.ts.map +1 -1
- package/dist/lib/template-resolver.js +0 -1
- package/dist/lib/theme/get-default-theme-mode.d.ts +7 -4
- package/dist/lib/theme/get-default-theme-mode.d.ts.map +1 -1
- package/dist/lib/theme/get-default-theme-mode.js +2 -44
- package/dist/messages/de/auth.json +4 -2
- package/dist/messages/de/common.json +83 -3
- package/dist/messages/de/email.json +1 -1
- package/dist/messages/de/index.d.ts +87 -0
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/de/settings.json +5 -0
- package/dist/messages/en/auth.json +4 -2
- package/dist/messages/en/common.json +83 -3
- package/dist/messages/en/email.json +1 -1
- package/dist/messages/en/index.d.ts +86 -0
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/en/settings.json +4 -0
- package/dist/messages/es/auth.json +4 -2
- package/dist/messages/es/common.json +83 -3
- package/dist/messages/es/email.json +1 -1
- package/dist/messages/es/index.d.ts +86 -0
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/es/settings.json +4 -0
- package/dist/messages/fr/auth.json +4 -2
- package/dist/messages/fr/common.json +83 -3
- package/dist/messages/fr/email.json +1 -1
- package/dist/messages/fr/index.d.ts +87 -0
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/fr/settings.json +5 -0
- package/dist/messages/it/auth.json +4 -2
- package/dist/messages/it/common.json +83 -3
- package/dist/messages/it/email.json +1 -1
- package/dist/messages/it/index.d.ts +87 -0
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/it/settings.json +5 -0
- package/dist/messages/pt/auth.json +4 -2
- package/dist/messages/pt/common.json +83 -3
- package/dist/messages/pt/email.json +1 -1
- package/dist/messages/pt/index.d.ts +87 -0
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/messages/pt/settings.json +5 -0
- package/dist/nextspark-registries.d.ts +54 -0
- package/dist/providers/DashboardProviders.d.ts +13 -10
- package/dist/providers/DashboardProviders.d.ts.map +1 -1
- package/dist/providers/DashboardProviders.js +7 -1
- package/dist/templates/app/(auth)/forgot-password/page.tsx +2 -2
- package/dist/templates/app/(auth)/reset-password/page.tsx +6 -6
- package/dist/templates/app/403/page.tsx +6 -6
- package/dist/templates/app/api/v1/teams/switch/route.ts +24 -5
- package/dist/templates/app/dashboard/(main)/[entity]/layout.tsx +9 -7
- package/dist/templates/app/dashboard/(main)/layout.tsx +4 -3
- package/dist/templates/app/dashboard/settings/api-keys/page.tsx +36 -34
- package/dist/templates/app/dashboard/settings/profile/page.tsx +7 -6
- package/dist/templates/app/dashboard/settings/security/page.tsx +5 -4
- package/dist/templates/app/layout.ppr.tsx +8 -8
- package/dist/templates/app/layout.tsx +12 -17
- package/dist/templates/blocks/cta-section/component.tsx +5 -2
- package/dist/templates/blocks/cta-section/config.ts +3 -2
- package/dist/templates/blocks/cta-section/fields.ts +2 -2
- package/dist/templates/blocks/cta-section/schema.ts +2 -2
- package/dist/templates/blocks/features-grid/component.tsx +10 -2
- package/dist/templates/blocks/features-grid/config.ts +3 -2
- package/dist/templates/blocks/features-grid/fields.ts +2 -2
- package/dist/templates/blocks/features-grid/schema.ts +2 -2
- package/dist/templates/blocks/hero/component.tsx +6 -3
- package/dist/templates/blocks/hero/config.ts +3 -2
- package/dist/templates/blocks/hero/fields.ts +2 -2
- package/dist/templates/blocks/hero/schema.ts +2 -2
- package/dist/templates/blocks/testimonials/component.tsx +6 -3
- package/dist/templates/blocks/testimonials/config.ts +3 -2
- package/dist/templates/blocks/testimonials/fields.ts +2 -2
- package/dist/templates/blocks/testimonials/schema.ts +2 -2
- package/dist/templates/blocks/text-content/component.tsx +7 -3
- package/dist/templates/blocks/text-content/config.ts +3 -2
- package/dist/templates/blocks/text-content/fields.ts +2 -2
- package/dist/templates/blocks/text-content/schema.ts +2 -2
- package/dist/templates/contents/themes/starter/blocks/cta-section/component.tsx +102 -0
- package/dist/templates/contents/themes/starter/blocks/cta-section/config.ts +11 -0
- package/dist/templates/contents/themes/starter/blocks/cta-section/fields.ts +89 -0
- package/dist/templates/contents/themes/starter/blocks/cta-section/index.ts +6 -0
- package/dist/templates/contents/themes/starter/blocks/cta-section/schema.ts +32 -0
- package/dist/templates/contents/themes/starter/blocks/cta-section/thumbnail.png +0 -0
- package/dist/templates/contents/themes/starter/blocks/features-grid/component.tsx +119 -0
- package/dist/templates/contents/themes/starter/blocks/features-grid/config.ts +11 -0
- package/dist/templates/contents/themes/starter/blocks/features-grid/fields.ts +97 -0
- package/dist/templates/contents/themes/starter/blocks/features-grid/index.ts +6 -0
- package/dist/templates/contents/themes/starter/blocks/features-grid/schema.ts +40 -0
- package/dist/templates/contents/themes/starter/blocks/features-grid/thumbnail.png +0 -0
- package/dist/templates/contents/themes/starter/blocks/hero/component.tsx +105 -0
- package/dist/templates/contents/themes/starter/blocks/hero/config.ts +11 -0
- package/dist/templates/contents/themes/starter/blocks/hero/fields.ts +60 -0
- package/dist/templates/contents/themes/starter/blocks/hero/index.ts +6 -0
- package/dist/templates/contents/themes/starter/blocks/hero/schema.ts +33 -0
- package/dist/templates/contents/themes/starter/blocks/hero/thumbnail.png +0 -0
- package/dist/templates/contents/themes/starter/blocks/testimonials/component.tsx +116 -0
- package/dist/templates/contents/themes/starter/blocks/testimonials/config.ts +11 -0
- package/dist/templates/contents/themes/starter/blocks/testimonials/fields.ts +105 -0
- package/dist/templates/contents/themes/starter/blocks/testimonials/index.ts +6 -0
- package/dist/templates/contents/themes/starter/blocks/testimonials/schema.ts +41 -0
- package/dist/templates/contents/themes/starter/blocks/testimonials/thumbnail.png +0 -0
- package/dist/templates/contents/themes/starter/blocks/text-content/component.tsx +100 -0
- package/dist/templates/contents/themes/starter/blocks/text-content/config.ts +11 -0
- package/dist/templates/contents/themes/starter/blocks/text-content/fields.ts +88 -0
- package/dist/templates/contents/themes/starter/blocks/text-content/index.ts +6 -0
- package/dist/templates/contents/themes/starter/blocks/text-content/schema.ts +30 -0
- package/dist/templates/contents/themes/starter/blocks/text-content/thumbnail.png +0 -0
- package/dist/templates/contents/themes/starter/config/app.config.ts +8 -0
- package/dist/templates/contents/themes/starter/entities/tasks/tasks.config.ts +7 -0
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/devtools/access.cy.ts +9 -1
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/api/entities/tasks/TaskAPIController.js +2 -2
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.cy.ts +45 -38
- package/dist/templates/contents/themes/starter/tests/cypress/fixtures/entities.json +16 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/components/DevKeyringPOM.ts +3 -1
- package/dist/templates/contents/themes/starter/tests/cypress/src/core/BlockEditorBasePOM.ts +3 -3
- package/dist/templates/contents/themes/starter/tests/cypress/src/entities/PagesPOM.ts +137 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/entities/index.ts +2 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/index.ts +1 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/session-helpers.ts +42 -11
- package/dist/templates/contents/themes/starter/tests/cypress/support/commands.ts +29 -18
- package/dist/templates/features/blog/blocks/post-content/component.tsx +5 -1
- package/dist/templates/features/blog/blocks/post-content/schema.ts +1 -1
- package/dist/templates/features/pages/blocks/hero/component.tsx +3 -0
- package/dist/templates/features/pages/blocks/hero/schema.ts +1 -1
- package/dist/templates/proxy.ts +138 -53
- package/dist/types/blocks.d.ts.map +1 -1
- package/dist/types/blocks.js +1 -1
- package/dist/types/task.types.d.ts +1 -1
- package/dist/types/task.types.js +7 -7
- package/dist/utils/dev/TranslationDebugger.js +2 -2
- package/package.json +14 -5
- package/scripts/build/registry/__tests__/block-registry.test.mjs +73 -0
- package/scripts/build/registry/__tests__/icons-discovery.test.mjs +130 -0
- package/scripts/build/registry/__tests__/identifier.test.mjs +65 -0
- package/scripts/build/registry/__tests__/page-generator.test.mjs +430 -0
- package/scripts/build/registry/__tests__/scheduled-actions-registry.test.mjs +41 -0
- package/scripts/build/registry/__tests__/template-registry.test.mjs +89 -0
- package/scripts/build/registry/discovery/icons.mjs +275 -0
- package/scripts/build/registry/discovery/index.mjs +1 -0
- package/scripts/build/registry/generators/block-registry.mjs +203 -55
- package/scripts/build/registry/generators/feature-registry.mjs +9 -2
- package/scripts/build/registry/generators/icon-registry.mjs +56 -0
- package/scripts/build/registry/generators/identifier.mjs +28 -0
- package/scripts/build/registry/generators/index.mjs +1 -0
- package/scripts/build/registry/generators/plugin-registry.mjs +4 -3
- package/scripts/build/registry/generators/scheduled-actions-registry.mjs +4 -2
- package/scripts/build/registry/generators/template-registry.mjs +49 -41
- package/scripts/build/registry/generators/theme-registry.mjs +6 -7
- package/scripts/build/registry/post-build/page-generator.mjs +380 -59
- package/scripts/build/registry.mjs +9 -1
- package/templates/app/(auth)/forgot-password/page.tsx +2 -2
- package/templates/app/(auth)/reset-password/page.tsx +6 -6
- package/templates/app/403/page.tsx +6 -6
- package/templates/app/api/v1/teams/switch/route.ts +24 -5
- package/templates/app/dashboard/(main)/[entity]/layout.tsx +9 -7
- package/templates/app/dashboard/(main)/layout.tsx +4 -3
- package/templates/app/dashboard/settings/api-keys/page.tsx +36 -34
- package/templates/app/dashboard/settings/profile/page.tsx +7 -6
- package/templates/app/dashboard/settings/security/page.tsx +5 -4
- package/templates/app/layout.ppr.tsx +8 -8
- package/templates/app/layout.tsx +12 -17
- package/templates/blocks/cta-section/component.tsx +5 -2
- package/templates/blocks/cta-section/config.ts +3 -2
- package/templates/blocks/cta-section/fields.ts +2 -2
- package/templates/blocks/cta-section/schema.ts +2 -2
- package/templates/blocks/features-grid/component.tsx +10 -2
- package/templates/blocks/features-grid/config.ts +3 -2
- package/templates/blocks/features-grid/fields.ts +2 -2
- package/templates/blocks/features-grid/schema.ts +2 -2
- package/templates/blocks/hero/component.tsx +6 -3
- package/templates/blocks/hero/config.ts +3 -2
- package/templates/blocks/hero/fields.ts +2 -2
- package/templates/blocks/hero/schema.ts +2 -2
- package/templates/blocks/testimonials/component.tsx +6 -3
- package/templates/blocks/testimonials/config.ts +3 -2
- package/templates/blocks/testimonials/fields.ts +2 -2
- package/templates/blocks/testimonials/schema.ts +2 -2
- package/templates/blocks/text-content/component.tsx +7 -3
- package/templates/blocks/text-content/config.ts +3 -2
- package/templates/blocks/text-content/fields.ts +2 -2
- package/templates/blocks/text-content/schema.ts +2 -2
- package/templates/contents/themes/starter/blocks/cta-section/component.tsx +102 -0
- package/templates/contents/themes/starter/blocks/cta-section/config.ts +11 -0
- package/templates/contents/themes/starter/blocks/cta-section/fields.ts +89 -0
- package/templates/contents/themes/starter/blocks/cta-section/index.ts +6 -0
- package/templates/contents/themes/starter/blocks/cta-section/schema.ts +32 -0
- package/templates/contents/themes/starter/blocks/cta-section/thumbnail.png +0 -0
- package/templates/contents/themes/starter/blocks/features-grid/component.tsx +119 -0
- package/templates/contents/themes/starter/blocks/features-grid/config.ts +11 -0
- package/templates/contents/themes/starter/blocks/features-grid/fields.ts +97 -0
- package/templates/contents/themes/starter/blocks/features-grid/index.ts +6 -0
- package/templates/contents/themes/starter/blocks/features-grid/schema.ts +40 -0
- package/templates/contents/themes/starter/blocks/features-grid/thumbnail.png +0 -0
- package/templates/contents/themes/starter/blocks/hero/component.tsx +105 -0
- package/templates/contents/themes/starter/blocks/hero/config.ts +11 -0
- package/templates/contents/themes/starter/blocks/hero/fields.ts +60 -0
- package/templates/contents/themes/starter/blocks/hero/index.ts +6 -0
- package/templates/contents/themes/starter/blocks/hero/schema.ts +33 -0
- package/templates/contents/themes/starter/blocks/hero/thumbnail.png +0 -0
- package/templates/contents/themes/starter/blocks/testimonials/component.tsx +116 -0
- package/templates/contents/themes/starter/blocks/testimonials/config.ts +11 -0
- package/templates/contents/themes/starter/blocks/testimonials/fields.ts +105 -0
- package/templates/contents/themes/starter/blocks/testimonials/index.ts +6 -0
- package/templates/contents/themes/starter/blocks/testimonials/schema.ts +41 -0
- package/templates/contents/themes/starter/blocks/testimonials/thumbnail.png +0 -0
- package/templates/contents/themes/starter/blocks/text-content/component.tsx +100 -0
- package/templates/contents/themes/starter/blocks/text-content/config.ts +11 -0
- package/templates/contents/themes/starter/blocks/text-content/fields.ts +88 -0
- package/templates/contents/themes/starter/blocks/text-content/index.ts +6 -0
- package/templates/contents/themes/starter/blocks/text-content/schema.ts +30 -0
- package/templates/contents/themes/starter/blocks/text-content/thumbnail.png +0 -0
- package/templates/contents/themes/starter/config/app.config.ts +8 -0
- package/templates/contents/themes/starter/entities/tasks/tasks.config.ts +7 -0
- package/templates/contents/themes/starter/tests/cypress/e2e/_utils/devtools/access.cy.ts +9 -1
- package/templates/contents/themes/starter/tests/cypress/e2e/api/entities/tasks/TaskAPIController.js +2 -2
- package/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.cy.ts +45 -38
- package/templates/contents/themes/starter/tests/cypress/fixtures/entities.json +16 -0
- package/templates/contents/themes/starter/tests/cypress/src/components/DevKeyringPOM.ts +3 -1
- package/templates/contents/themes/starter/tests/cypress/src/core/BlockEditorBasePOM.ts +3 -3
- package/templates/contents/themes/starter/tests/cypress/src/entities/PagesPOM.ts +137 -0
- package/templates/contents/themes/starter/tests/cypress/src/entities/index.ts +2 -0
- package/templates/contents/themes/starter/tests/cypress/src/index.ts +1 -0
- package/templates/contents/themes/starter/tests/cypress/src/session-helpers.ts +42 -11
- package/templates/contents/themes/starter/tests/cypress/support/commands.ts +29 -18
- package/templates/features/blog/blocks/post-content/component.tsx +5 -1
- package/templates/features/blog/blocks/post-content/schema.ts +1 -1
- package/templates/features/pages/blocks/hero/component.tsx +3 -0
- package/templates/features/pages/blocks/hero/schema.ts +1 -1
- package/templates/proxy.ts +138 -53
- package/tests/jest/__mocks__/@nextsparkjs/registries/block-registry.client.ts +9 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/block-registry.lazy.ts +5 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/icon-registry.ts +42 -0
- package/tests/jest/__mocks__/next-intl-server.js +85 -5
- package/tests/jest/__mocks__/next-server.js +32 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the template-registry generator.
|
|
3
|
+
*
|
|
4
|
+
* What matters here is the BINDING the generator emits. A static
|
|
5
|
+
* `import Template_0 from '...'` per template makes every route that touches
|
|
6
|
+
* the registry pull in the whole template graph (#183), so both registries
|
|
7
|
+
* have to emit a deferred import instead — and the server one has to do it
|
|
8
|
+
* without a Suspense boundary, which would let a response commit a 200 before
|
|
9
|
+
* a template's own notFound() runs (#129).
|
|
10
|
+
*
|
|
11
|
+
* Run: node --test packages/core/scripts/build/registry/__tests__/template-registry.test.mjs
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { test } from 'node:test'
|
|
15
|
+
import assert from 'node:assert/strict'
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
generateTemplateRegistry,
|
|
19
|
+
generateTemplateRegistryClient
|
|
20
|
+
} from '../generators/template-registry.mjs'
|
|
21
|
+
|
|
22
|
+
const pageTemplate = {
|
|
23
|
+
name: '(public)/page',
|
|
24
|
+
themeName: 'default',
|
|
25
|
+
templateType: 'page',
|
|
26
|
+
fileName: 'page.tsx',
|
|
27
|
+
relativePath: '(public)/page.tsx',
|
|
28
|
+
appPath: 'app/(public)/page.tsx',
|
|
29
|
+
templatePath: '@/contents/themes/default/templates/(public)/page.tsx',
|
|
30
|
+
priority: 10
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const protectedTemplate = {
|
|
34
|
+
...pageTemplate,
|
|
35
|
+
name: 'dashboard/layout',
|
|
36
|
+
templateType: 'layout',
|
|
37
|
+
fileName: 'layout.meta.ts',
|
|
38
|
+
relativePath: 'dashboard/layout.meta.ts',
|
|
39
|
+
appPath: 'app/dashboard/layout.tsx',
|
|
40
|
+
templatePath: '@/contents/themes/default/templates/dashboard/layout.meta.ts'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const config = { outputDir: '/tmp/registries', projectRoot: '/tmp/project' }
|
|
44
|
+
|
|
45
|
+
test('server registry defers the import instead of hoisting a static one', () => {
|
|
46
|
+
const out = generateTemplateRegistry([pageTemplate], config)
|
|
47
|
+
|
|
48
|
+
assert.match(
|
|
49
|
+
out,
|
|
50
|
+
/component: lazyTemplate\('app\/\(public\)\/page\.tsx', \(\) => import\('@\/contents\/themes\/default\/templates\/\(public\)\/page'\)\)/
|
|
51
|
+
)
|
|
52
|
+
assert.doesNotMatch(out, /^import Template_\d+ from/m)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('names the template in the error when it has no default export', () => {
|
|
56
|
+
const out = generateTemplateRegistry([pageTemplate], config)
|
|
57
|
+
|
|
58
|
+
// Without the module loaded up front, React would otherwise fail with a bare
|
|
59
|
+
// "invalid element type" and no clue which template is at fault
|
|
60
|
+
assert.match(out, /has no default export/)
|
|
61
|
+
assert.match(out, /if \(!templateModule\.default\)/)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('server registry loads templates without a Suspense boundary', () => {
|
|
65
|
+
const out = generateTemplateRegistry([pageTemplate], config)
|
|
66
|
+
|
|
67
|
+
// An async Server Component awaits inline; next/dynamic would wrap the
|
|
68
|
+
// template in Suspense and break notFound()'s hold on the status code.
|
|
69
|
+
assert.match(out, /return async function TemplateOverride/)
|
|
70
|
+
assert.doesNotMatch(out, /^import .* from 'next\/dynamic'/m)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('metadata-only templates still carry no component', () => {
|
|
74
|
+
const out = generateTemplateRegistry([protectedTemplate], config)
|
|
75
|
+
|
|
76
|
+
assert.match(out, /component: null/)
|
|
77
|
+
assert.doesNotMatch(out, /layout\.meta'\)\)/)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('client registry defers the import through next/dynamic', async () => {
|
|
81
|
+
const out = await generateTemplateRegistryClient([pageTemplate], config)
|
|
82
|
+
|
|
83
|
+
assert.match(out, /import dynamic from 'next\/dynamic'/)
|
|
84
|
+
assert.match(
|
|
85
|
+
out,
|
|
86
|
+
/'app\/\(public\)\/page\.tsx': dynamic\(\(\) => import\('@\/contents\/themes\/default\/templates\/\(public\)\/page'\)\)/
|
|
87
|
+
)
|
|
88
|
+
assert.doesNotMatch(out, /^import ClientTemplate_\d+ from/m)
|
|
89
|
+
})
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon Discovery
|
|
3
|
+
*
|
|
4
|
+
* Collects every lucide-react icon name the app can ask for by string at
|
|
5
|
+
* runtime, so the generated icon registry can import exactly those and let
|
|
6
|
+
* the bundler drop the rest of the icon set.
|
|
7
|
+
*
|
|
8
|
+
* Sources are exactly what resolveIcon can be handed, and all of them are
|
|
9
|
+
* source files (never the database):
|
|
10
|
+
* - entity configs (`icon: Users` — an identifier imported from lucide-react)
|
|
11
|
+
* - a theme's app.config (`customSidebarSections[].icon` — a string)
|
|
12
|
+
* - block configs (`icon: 'Grid'` — the block's own icon in the editor)
|
|
13
|
+
*
|
|
14
|
+
* What a block RENDERS is not here: page content names those icons in the page
|
|
15
|
+
* builder, so they live in the database. Those blocks keep resolving through
|
|
16
|
+
* the lucide namespace — deliberately, see themes' features-grid component.
|
|
17
|
+
*
|
|
18
|
+
* @module core/scripts/build/registry/discovery/icons
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { readdir, readFile } from 'fs/promises'
|
|
22
|
+
import { existsSync } from 'fs'
|
|
23
|
+
import { createRequire } from 'module'
|
|
24
|
+
import { join, dirname } from 'path'
|
|
25
|
+
|
|
26
|
+
import { verbose, log } from '../../../utils/index.mjs'
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Icons the resolver falls back to when a name doesn't resolve. They are
|
|
30
|
+
* referenced from core components, not from any config, so nothing would
|
|
31
|
+
* discover them.
|
|
32
|
+
*/
|
|
33
|
+
const FALLBACK_ICONS = ['Box', 'Circle', 'Folder', 'LayoutGrid']
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Names lucide-react actually exports, read from its own barrel.
|
|
37
|
+
* A name that isn't there would become a broken named import in the
|
|
38
|
+
* generated registry, so unknown names are dropped with a warning instead.
|
|
39
|
+
* @param {object} config
|
|
40
|
+
* @returns {Set<string>|null} null when lucide-react can't be resolved
|
|
41
|
+
*/
|
|
42
|
+
async function readLucideExportNames(config) {
|
|
43
|
+
try {
|
|
44
|
+
const requireFromProject = createRequire(join(config.projectRoot, 'package.json'))
|
|
45
|
+
const pkgPath = requireFromProject.resolve('lucide-react/package.json')
|
|
46
|
+
const barrelPath = join(dirname(pkgPath), 'dist/esm/lucide-react.js')
|
|
47
|
+
|
|
48
|
+
if (!existsSync(barrelPath)) {
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const barrel = await readFile(barrelPath, 'utf8')
|
|
53
|
+
const names = new Set()
|
|
54
|
+
for (const match of barrel.matchAll(/default as ([A-Za-z_$][\w$]*)/g)) {
|
|
55
|
+
names.add(match[1])
|
|
56
|
+
}
|
|
57
|
+
return names.size > 0 ? names : null
|
|
58
|
+
} catch {
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Which config files hold icons resolveIcon can be handed. Exported for tests.
|
|
65
|
+
*
|
|
66
|
+
* Separators are normalised first: the generator also runs on Windows, where
|
|
67
|
+
* join() produces '\\' and a '/'-shaped match would silently find nothing —
|
|
68
|
+
* leaving a registry with only its fallbacks, and every entity icon rendering
|
|
69
|
+
* as a Box.
|
|
70
|
+
*/
|
|
71
|
+
export function isIconSourcePath(filePath) {
|
|
72
|
+
const normalised = filePath.replace(/\\/g, '/')
|
|
73
|
+
return (
|
|
74
|
+
(normalised.includes('/entities/') && normalised.endsWith('.config.ts')) ||
|
|
75
|
+
(normalised.includes('/blocks/') && normalised.endsWith('/config.ts')) ||
|
|
76
|
+
normalised.endsWith('/config/app.config.ts')
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Map of local name -> exported lucide name for a file's lucide imports.
|
|
82
|
+
* `import { Home as HouseIcon }` means the config's `icon: HouseIcon` is
|
|
83
|
+
* lucide's `Home`.
|
|
84
|
+
*/
|
|
85
|
+
function parseLucideImports(content) {
|
|
86
|
+
const imports = new Map()
|
|
87
|
+
|
|
88
|
+
for (const match of content.matchAll(/import\s*(?:type\s*)?\{([^}]*)\}\s*from\s*['"]lucide-react['"]/g)) {
|
|
89
|
+
for (const specifier of match[1].split(',')) {
|
|
90
|
+
const parts = specifier.trim().replace(/^type\s+/, '').split(/\s+as\s+/)
|
|
91
|
+
const exported = parts[0]?.trim()
|
|
92
|
+
const local = (parts[1] || parts[0])?.trim()
|
|
93
|
+
if (exported && local) {
|
|
94
|
+
imports.set(local, exported)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return imports
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Icon names referenced by a single config file. Exported for tests.
|
|
104
|
+
*/
|
|
105
|
+
export function extractIconNames(content) {
|
|
106
|
+
const lucideImports = parseLucideImports(content)
|
|
107
|
+
const names = []
|
|
108
|
+
|
|
109
|
+
// `icon: Users` or `'icon': Users` — only counts when the identifier came
|
|
110
|
+
// from lucide-react
|
|
111
|
+
for (const match of content.matchAll(/(?:\bicon|['"]icon['"])\s*:\s*([A-Za-z_$][\w$]*)/g)) {
|
|
112
|
+
const exported = lucideImports.get(match[1])
|
|
113
|
+
if (exported) {
|
|
114
|
+
names.push(exported)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// `icon: 'Users'` or `icon: 'pie-chart'` — a theme's sidebar sections and
|
|
119
|
+
// block configs use both spellings; the caller folds kebab-case into the
|
|
120
|
+
// lucide export name and validates the result against lucide's export list.
|
|
121
|
+
for (const match of content.matchAll(/(?:\bicon|['"]icon['"])\s*:\s*['"]([A-Za-z][\w$-]*)['"]/g)) {
|
|
122
|
+
names.push(match[1])
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return names
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Icon references in a config file that this build cannot turn into a name.
|
|
130
|
+
* Exported for tests.
|
|
131
|
+
*
|
|
132
|
+
* Only a direct named import of lucide-react and a string literal reach the
|
|
133
|
+
* registry. Anything else — a namespace access (`I.Users`), an identifier
|
|
134
|
+
* aliased through another module, a component of the project's own — is not
|
|
135
|
+
* resolvable by reading this file, so it never enters the registry and
|
|
136
|
+
* resolveIcon falls back. That is silent, hence the warning.
|
|
137
|
+
*/
|
|
138
|
+
export function findUnresolvedIconRefs(content) {
|
|
139
|
+
const lucideImports = parseLucideImports(content)
|
|
140
|
+
const unresolved = []
|
|
141
|
+
|
|
142
|
+
for (const match of content.matchAll(/(?:\bicon|['"]icon['"])\s*:\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/g)) {
|
|
143
|
+
const reference = match[1]
|
|
144
|
+
const isMember = reference.includes('.')
|
|
145
|
+
|
|
146
|
+
if (isMember || !lucideImports.has(reference)) {
|
|
147
|
+
unresolved.push(reference)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return unresolved
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function collectConfigFiles(dir, matcher, found = []) {
|
|
155
|
+
if (!existsSync(dir)) {
|
|
156
|
+
return found
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let entries
|
|
160
|
+
try {
|
|
161
|
+
entries = await readdir(dir, { withFileTypes: true })
|
|
162
|
+
} catch {
|
|
163
|
+
return found
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for (const entry of entries) {
|
|
167
|
+
const entryPath = join(dir, entry.name)
|
|
168
|
+
if (entry.isDirectory()) {
|
|
169
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue
|
|
170
|
+
await collectConfigFiles(entryPath, matcher, found)
|
|
171
|
+
} else if (matcher(entryPath)) {
|
|
172
|
+
found.push(entryPath)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return found
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function coreEntitiesDir(config) {
|
|
180
|
+
if (config.isNpmMode) {
|
|
181
|
+
return join(config.projectRoot, 'node_modules/@nextsparkjs/core/src/entities')
|
|
182
|
+
}
|
|
183
|
+
if (config.isMonorepoMode && config.monorepoRoot) {
|
|
184
|
+
return join(config.monorepoRoot, 'packages/core/src/entities')
|
|
185
|
+
}
|
|
186
|
+
return join(config.projectRoot, 'packages/core/src/entities')
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Discover every icon name the runtime can look up by string.
|
|
191
|
+
* @param {Array} blocks - Blocks from block discovery (each with an `icon` name)
|
|
192
|
+
* @param {object} config - Configuration object from getConfig()
|
|
193
|
+
* @returns {Promise<string[]>} Sorted, de-duplicated, validated icon names
|
|
194
|
+
*/
|
|
195
|
+
export async function discoverIcons(blocks, config) {
|
|
196
|
+
const candidates = new Set(FALLBACK_ICONS)
|
|
197
|
+
|
|
198
|
+
// Only the configs whose icons reach resolveIcon. Widening this would put
|
|
199
|
+
// icons in the dashboard bundle that nothing can ask for.
|
|
200
|
+
const iconSources = [
|
|
201
|
+
...(await collectConfigFiles(coreEntitiesDir(config), isIconSourcePath)),
|
|
202
|
+
...(await collectConfigFiles(config.themesDir, isIconSourcePath)),
|
|
203
|
+
...(await collectConfigFiles(config.pluginsDir, isIconSourcePath))
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
const unresolved = []
|
|
207
|
+
|
|
208
|
+
for (const configPath of iconSources) {
|
|
209
|
+
try {
|
|
210
|
+
const content = await readFile(configPath, 'utf8')
|
|
211
|
+
for (const name of extractIconNames(content)) {
|
|
212
|
+
candidates.add(name)
|
|
213
|
+
}
|
|
214
|
+
for (const reference of findUnresolvedIconRefs(content)) {
|
|
215
|
+
unresolved.push({ configPath, reference })
|
|
216
|
+
}
|
|
217
|
+
} catch {
|
|
218
|
+
verbose(`[icons] Could not read ${configPath}`)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
for (const { configPath, reference } of unresolved) {
|
|
223
|
+
log(
|
|
224
|
+
`[icons] ${configPath.replace(config.projectRoot, '')}: cannot resolve \`icon: ${reference}\` at build time, so it is not in the registry and will render the fallback. Name it with a string, or import it directly from lucide-react.`,
|
|
225
|
+
'warning'
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
for (const block of blocks || []) {
|
|
230
|
+
if (block.icon) {
|
|
231
|
+
candidates.add(block.icon)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const known = await readLucideExportNames(config)
|
|
236
|
+
if (!known) {
|
|
237
|
+
// Every name becomes a named import, so a kebab-case one would emit
|
|
238
|
+
// `import { pie-chart }` and break the build. Without lucide's list there
|
|
239
|
+
// is nothing to fold them against, so they are dropped instead.
|
|
240
|
+
const importable = [...candidates].filter(name => /^[A-Za-z_$][\w$]*$/.test(name))
|
|
241
|
+
log('Could not read lucide-react exports; icon registry will include every importable name found', 'warning')
|
|
242
|
+
return importable.sort()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Configs spell icons both ways: `CheckSquare` in entity configs, `pie-chart`
|
|
246
|
+
// in a theme's sidebar and block configs. The registry is keyed by the export
|
|
247
|
+
// name, so kebab-case names are folded into it rather than dropped.
|
|
248
|
+
const toPascalCase = name =>
|
|
249
|
+
name
|
|
250
|
+
.split(/[-_\s]+/)
|
|
251
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
252
|
+
.join('')
|
|
253
|
+
|
|
254
|
+
const valid = []
|
|
255
|
+
const unknown = []
|
|
256
|
+
for (const name of candidates) {
|
|
257
|
+
if (known.has(name)) {
|
|
258
|
+
valid.push(name)
|
|
259
|
+
continue
|
|
260
|
+
}
|
|
261
|
+
const pascalCased = toPascalCase(name)
|
|
262
|
+
if (known.has(pascalCased)) {
|
|
263
|
+
valid.push(pascalCased)
|
|
264
|
+
continue
|
|
265
|
+
}
|
|
266
|
+
unknown.push(name)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (unknown.length > 0) {
|
|
270
|
+
log(`Icons not exported by lucide-react, skipped: ${[...new Set(unknown)].sort().join(', ')}`, 'warning')
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
verbose(`[icons] ${valid.length} icon(s) referenced by name`)
|
|
274
|
+
return [...new Set(valid)].sort()
|
|
275
|
+
}
|
|
@@ -13,6 +13,7 @@ export { discoverCoreEntities } from './core-entities.mjs'
|
|
|
13
13
|
export { discoverThemes, discoverThemeRouteFiles } from './themes.mjs'
|
|
14
14
|
export { discoverTemplates, discoverThemeTemplates } from './templates.mjs'
|
|
15
15
|
export { discoverBlocks } from './blocks.mjs'
|
|
16
|
+
export { discoverIcons } from './icons.mjs'
|
|
16
17
|
export { discoverMiddlewares } from './middlewares.mjs'
|
|
17
18
|
export { discoverPermissionsConfig } from './permissions.mjs'
|
|
18
19
|
export { discoverAuthConfig } from './auth.mjs'
|
|
@@ -1,14 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Block Registry Generator
|
|
3
3
|
*
|
|
4
|
-
* Generates block-registry.ts
|
|
4
|
+
* Generates block-registry.ts, block-registry.client.ts and block-registry.lazy.ts
|
|
5
|
+
*
|
|
6
|
+
* The split is what keeps block components out of the browser bundle. A block
|
|
7
|
+
* component is a server component and may import server-only code; the editor,
|
|
8
|
+
* the picker and the devtools viewers are client components that only ever read
|
|
9
|
+
* the configs. Importing both from one module drags every component — and
|
|
10
|
+
* whatever it imports — into the client build, which fails outright when one of
|
|
11
|
+
* them is server-only.
|
|
5
12
|
*
|
|
6
13
|
* @module core/scripts/build/registry/generators/block-registry
|
|
7
14
|
*/
|
|
8
15
|
|
|
9
16
|
import { join } from 'path'
|
|
17
|
+
import { toSafeIdentifier } from './identifier.mjs'
|
|
10
18
|
import { convertCorePath } from '../config.mjs'
|
|
11
19
|
|
|
20
|
+
/**
|
|
21
|
+
* The registry entries both halves emit, so the configs cannot drift apart.
|
|
22
|
+
*
|
|
23
|
+
* @param {Array} blocks - Discovered blocks
|
|
24
|
+
* @returns {string} The entries, ready to sit inside the object literal
|
|
25
|
+
*/
|
|
26
|
+
function buildRegistryEntries(blocks) {
|
|
27
|
+
return blocks.map(block => {
|
|
28
|
+
|
|
29
|
+
const slugVar = toSafeIdentifier(block.slug)
|
|
30
|
+
const scopeValue = block.scope
|
|
31
|
+
? `[${block.scope.map(s => `'${s}'`).join(', ')}]`
|
|
32
|
+
: 'undefined'
|
|
33
|
+
const examplesValue = block.hasExamples ? `${slugVar}_examples` : '[]'
|
|
34
|
+
const allowInPatternsValue = block.allowInPatterns !== undefined
|
|
35
|
+
? block.allowInPatterns.toString()
|
|
36
|
+
: 'undefined'
|
|
37
|
+
|
|
38
|
+
return ` '${block.slug}': {
|
|
39
|
+
slug: '${block.slug}',
|
|
40
|
+
name: '${block.name}',
|
|
41
|
+
description: '${block.description}',
|
|
42
|
+
category: '${block.category}' as BlockCategory,
|
|
43
|
+
icon: '${block.icon}',
|
|
44
|
+
componentPath: '${block.paths.component}',
|
|
45
|
+
schemaPath: '${block.paths.schema}',
|
|
46
|
+
fieldsPath: '${block.paths.fields}',
|
|
47
|
+
thumbnail: ${block.paths.thumbnail ? `'${block.paths.thumbnail}'` : 'undefined'},
|
|
48
|
+
fieldDefinitions: ${slugVar}_fields,
|
|
49
|
+
examples: ${examplesValue},
|
|
50
|
+
scope: ${scopeValue},
|
|
51
|
+
allowInPatterns: ${allowInPatternsValue},
|
|
52
|
+
isCore: false,
|
|
53
|
+
source: 'theme' as const,
|
|
54
|
+
sourceId: '${block.themeName}'
|
|
55
|
+
}`
|
|
56
|
+
}).join(',\n')
|
|
57
|
+
}
|
|
58
|
+
|
|
12
59
|
/**
|
|
13
60
|
* Generate the block registry file
|
|
14
61
|
* @param {Array} blocks - Discovered blocks
|
|
@@ -56,11 +103,8 @@ type BlockConfig = {
|
|
|
56
103
|
replacedBy?: string
|
|
57
104
|
}
|
|
58
105
|
|
|
59
|
-
export
|
|
60
|
-
|
|
61
|
-
export const BLOCK_CATEGORIES: string[] = []
|
|
62
|
-
|
|
63
|
-
export const BLOCK_COMPONENTS: Record<string, React.LazyExoticComponent<React.ComponentType<unknown>>> = {}
|
|
106
|
+
export * from './block-registry.client'
|
|
107
|
+
export { BLOCK_COMPONENTS } from './block-registry.lazy'
|
|
64
108
|
|
|
65
109
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
110
|
export const BLOCK_COMPONENTS_SSR: Record<string, React.ComponentType<any>> = {}
|
|
@@ -80,7 +124,7 @@ export const BLOCK_METADATA = {
|
|
|
80
124
|
const ssrImports = []
|
|
81
125
|
|
|
82
126
|
blocks.forEach(block => {
|
|
83
|
-
const slugVar = block.slug
|
|
127
|
+
const slugVar = toSafeIdentifier(block.slug)
|
|
84
128
|
|
|
85
129
|
imports.push(`import { fieldDefinitions as ${slugVar}_fields } from '${block.paths.fields}'`)
|
|
86
130
|
ssrImports.push(`import { default as ${slugVar}_component } from '${block.paths.component}'`)
|
|
@@ -93,35 +137,7 @@ export const BLOCK_METADATA = {
|
|
|
93
137
|
const fieldImports = imports.join('\n')
|
|
94
138
|
const ssrComponentImports = ssrImports.join('\n')
|
|
95
139
|
|
|
96
|
-
const registryEntries = blocks
|
|
97
|
-
const slugVar = block.slug.replace(/-/g, '_')
|
|
98
|
-
const scopeValue = block.scope
|
|
99
|
-
? `[${block.scope.map(s => `'${s}'`).join(', ')}]`
|
|
100
|
-
: 'undefined'
|
|
101
|
-
const examplesValue = block.hasExamples ? `${slugVar}_examples` : '[]'
|
|
102
|
-
const allowInPatternsValue = block.allowInPatterns !== undefined
|
|
103
|
-
? block.allowInPatterns.toString()
|
|
104
|
-
: 'undefined'
|
|
105
|
-
|
|
106
|
-
return ` '${block.slug}': {
|
|
107
|
-
slug: '${block.slug}',
|
|
108
|
-
name: '${block.name}',
|
|
109
|
-
description: '${block.description}',
|
|
110
|
-
category: '${block.category}' as BlockCategory,
|
|
111
|
-
icon: '${block.icon}',
|
|
112
|
-
componentPath: '${block.paths.component}',
|
|
113
|
-
schemaPath: '${block.paths.schema}',
|
|
114
|
-
fieldsPath: '${block.paths.fields}',
|
|
115
|
-
thumbnail: ${block.paths.thumbnail ? `'${block.paths.thumbnail}'` : 'undefined'},
|
|
116
|
-
fieldDefinitions: ${slugVar}_fields,
|
|
117
|
-
examples: ${examplesValue},
|
|
118
|
-
scope: ${scopeValue},
|
|
119
|
-
allowInPatterns: ${allowInPatternsValue},
|
|
120
|
-
isCore: false,
|
|
121
|
-
source: 'theme' as const,
|
|
122
|
-
sourceId: '${block.themeName}'
|
|
123
|
-
}`
|
|
124
|
-
}).join(',\n')
|
|
140
|
+
const registryEntries = buildRegistryEntries(blocks)
|
|
125
141
|
|
|
126
142
|
const categories = [...new Set(blocks.map(b => b.category))]
|
|
127
143
|
|
|
@@ -139,29 +155,17 @@ export const BLOCK_METADATA = {
|
|
|
139
155
|
*/
|
|
140
156
|
|
|
141
157
|
import React from 'react'
|
|
142
|
-
import type { BlockConfig, BlockCategory } from '${convertCorePath('@/core/types', outputFilePath, config)}'
|
|
143
158
|
|
|
144
|
-
|
|
159
|
+
// Configs live in the client half so a client component can read them without
|
|
160
|
+
// pulling in a single block component.
|
|
161
|
+
export * from './block-registry.client'
|
|
162
|
+
// Lazy components live in their own module, which a client component can import
|
|
163
|
+
// without the static imports below.
|
|
164
|
+
export { BLOCK_COMPONENTS } from './block-registry.lazy'
|
|
145
165
|
|
|
146
166
|
// Direct component imports for SSR — zero CLS, synchronous hydration
|
|
147
167
|
${ssrComponentImports}
|
|
148
168
|
|
|
149
|
-
export const BLOCK_REGISTRY: Record<string, BlockConfig> = {
|
|
150
|
-
${registryEntries}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export const BLOCK_CATEGORIES: BlockCategory[] = [${categories.map(c => `'${c}'`).join(', ')}]
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Lazy-loaded block components for client-side rendering (admin UI, block picker).
|
|
157
|
-
* Each component is wrapped with React.lazy for code-splitting.
|
|
158
|
-
*/
|
|
159
|
-
export const BLOCK_COMPONENTS: Record<string, React.LazyExoticComponent<React.ComponentType<any>>> = {
|
|
160
|
-
${blocks.map(block => {
|
|
161
|
-
return ` '${block.slug}': React.lazy(() => import('${block.paths.component}').then(mod => ({ default: Object.values(mod)[0] as React.ComponentType<any> })))`
|
|
162
|
-
}).join(',\n')}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
169
|
/**
|
|
166
170
|
* Direct-imported block components for SSR rendering.
|
|
167
171
|
* Static imports ensure synchronous hydration — zero CLS.
|
|
@@ -170,7 +174,7 @@ ${blocks.map(block => {
|
|
|
170
174
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
171
175
|
export const BLOCK_COMPONENTS_SSR: Record<string, React.ComponentType<any>> = {
|
|
172
176
|
${blocks.map(block => {
|
|
173
|
-
const slugVar = block.slug
|
|
177
|
+
const slugVar = toSafeIdentifier(block.slug)
|
|
174
178
|
return ` '${block.slug}': ${slugVar}_component`
|
|
175
179
|
}).join(',\n')}
|
|
176
180
|
}
|
|
@@ -192,3 +196,147 @@ ${categories.map(cat => {
|
|
|
192
196
|
}
|
|
193
197
|
`
|
|
194
198
|
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Generate the client-safe half: block configs, with no component imports.
|
|
202
|
+
*
|
|
203
|
+
* This is what a client component reads. Keeping it free of component imports
|
|
204
|
+
* is the whole point — a block component is a server component, and pulling one
|
|
205
|
+
* into the client build fails the build when it imports server-only code.
|
|
206
|
+
*
|
|
207
|
+
* @param {Array} blocks - Discovered blocks
|
|
208
|
+
* @param {object} config - Configuration object from getConfig()
|
|
209
|
+
* @returns {string} Generated TypeScript content
|
|
210
|
+
*/
|
|
211
|
+
export function generateBlockRegistryClient(blocks, config) {
|
|
212
|
+
const outputFilePath = join(config.outputDir, 'block-registry.client.ts')
|
|
213
|
+
const header = `/**
|
|
214
|
+
* Auto-generated Block Registry (client-safe)
|
|
215
|
+
*
|
|
216
|
+
* Generated at: ${new Date().toISOString()}
|
|
217
|
+
* Blocks discovered: ${blocks.length}
|
|
218
|
+
*
|
|
219
|
+
* Block configs only: no component imports, so a client component can read the
|
|
220
|
+
* registry without the block components reaching the browser bundle.
|
|
221
|
+
*
|
|
222
|
+
* DO NOT EDIT - This file is auto-generated by scripts/build-registry.mjs
|
|
223
|
+
*/
|
|
224
|
+
`
|
|
225
|
+
|
|
226
|
+
if (blocks.length === 0) {
|
|
227
|
+
return `${header}
|
|
228
|
+
// Inline type definition for empty registry (avoids import issues in npm mode)
|
|
229
|
+
type BlockConfig = {
|
|
230
|
+
slug: string
|
|
231
|
+
name: string
|
|
232
|
+
description?: string
|
|
233
|
+
category: string
|
|
234
|
+
icon?: string
|
|
235
|
+
componentPath?: string
|
|
236
|
+
schemaPath?: string
|
|
237
|
+
fieldsPath?: string
|
|
238
|
+
thumbnail?: string
|
|
239
|
+
fieldDefinitions: unknown[]
|
|
240
|
+
examples: unknown[]
|
|
241
|
+
scope?: string[]
|
|
242
|
+
allowInPatterns?: boolean
|
|
243
|
+
schemaType?: string
|
|
244
|
+
tags?: string[]
|
|
245
|
+
isCore?: boolean
|
|
246
|
+
source?: string
|
|
247
|
+
sourceId?: string
|
|
248
|
+
version?: string
|
|
249
|
+
deprecated?: boolean
|
|
250
|
+
replacedBy?: string
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export const BLOCK_REGISTRY: Record<string, BlockConfig> = {}
|
|
254
|
+
|
|
255
|
+
export const BLOCK_CATEGORIES: string[] = []
|
|
256
|
+
|
|
257
|
+
export const BLOCK_METADATA = {
|
|
258
|
+
totalBlocks: 0,
|
|
259
|
+
categories: [],
|
|
260
|
+
blocksByCategory: {},
|
|
261
|
+
generatedAt: '${new Date().toISOString()}',
|
|
262
|
+
blocks: []
|
|
263
|
+
}
|
|
264
|
+
`
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const imports = []
|
|
268
|
+
blocks.forEach(block => {
|
|
269
|
+
const slugVar = toSafeIdentifier(block.slug)
|
|
270
|
+
imports.push(`import { fieldDefinitions as ${slugVar}_fields } from '${block.paths.fields}'`)
|
|
271
|
+
if (block.hasExamples) {
|
|
272
|
+
imports.push(`import { examples as ${slugVar}_examples } from '${block.paths.examples}'`)
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
const registryEntries = buildRegistryEntries(blocks)
|
|
277
|
+
const categories = [...new Set(blocks.map(b => b.category))]
|
|
278
|
+
|
|
279
|
+
return `${header}
|
|
280
|
+
import type { BlockConfig, BlockCategory } from '${convertCorePath('@/core/types', outputFilePath, config)}'
|
|
281
|
+
|
|
282
|
+
${imports.join('\n')}
|
|
283
|
+
|
|
284
|
+
export const BLOCK_REGISTRY: Record<string, BlockConfig> = {
|
|
285
|
+
${registryEntries}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export const BLOCK_CATEGORIES: BlockCategory[] = [${categories.map(c => `'${c}'`).join(', ')}]
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Block registry metadata
|
|
292
|
+
*/
|
|
293
|
+
export const BLOCK_METADATA = {
|
|
294
|
+
totalBlocks: ${blocks.length},
|
|
295
|
+
categories: [${categories.map(c => `'${c}'`).join(', ')}],
|
|
296
|
+
blocksByCategory: {
|
|
297
|
+
${categories.map(cat => ` '${cat}': ${blocks.filter(b => b.category === cat).length}`).join(',\n')}
|
|
298
|
+
},
|
|
299
|
+
generatedAt: '${new Date().toISOString()}',
|
|
300
|
+
blocks: [${blocks.map(b => `'${b.slug}'`).join(', ')}]
|
|
301
|
+
}
|
|
302
|
+
`
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Generate the lazy half: every block component behind React.lazy, and no
|
|
307
|
+
* static import.
|
|
308
|
+
*
|
|
309
|
+
* Client components that render blocks (the editor canvas, pattern previews,
|
|
310
|
+
* devtools) read this, so each block stays in a chunk of its own and loads
|
|
311
|
+
* when it is shown. Reaching the components through block-registry.ts instead
|
|
312
|
+
* carries its static SSR imports along, and with them every block and all a
|
|
313
|
+
* block imports, into that client component's bundle.
|
|
314
|
+
*
|
|
315
|
+
* @param {Array} blocks - Discovered blocks
|
|
316
|
+
* @returns {string} Generated TypeScript content
|
|
317
|
+
*/
|
|
318
|
+
export function generateBlockRegistryLazy(blocks) {
|
|
319
|
+
const entries = blocks.map(block =>
|
|
320
|
+
` '${block.slug}': React.lazy(() => import('${block.paths.component}').then(mod => ({ default: Object.values(mod)[0] as React.ComponentType<any> })))`
|
|
321
|
+
).join(',\n')
|
|
322
|
+
|
|
323
|
+
return `/**
|
|
324
|
+
* Auto-generated Block Registry (lazy components)
|
|
325
|
+
*
|
|
326
|
+
* Generated at: ${new Date().toISOString()}
|
|
327
|
+
* Blocks discovered: ${blocks.length}
|
|
328
|
+
*
|
|
329
|
+
* Block components behind React.lazy and nothing imported statically, so a
|
|
330
|
+
* client component can render blocks while each one stays in its own chunk.
|
|
331
|
+
*
|
|
332
|
+
* DO NOT EDIT - This file is auto-generated by scripts/build-registry.mjs
|
|
333
|
+
*/
|
|
334
|
+
|
|
335
|
+
import React from 'react'
|
|
336
|
+
|
|
337
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
338
|
+
export const BLOCK_COMPONENTS: Record<string, React.LazyExoticComponent<React.ComponentType<any>>> = {
|
|
339
|
+
${entries}
|
|
340
|
+
}
|
|
341
|
+
`
|
|
342
|
+
}
|