fimo 0.7.0-experimental.2 → 0.7.0-experimental.4
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/README.md +1 -1
- package/assets/content-templates/hooks-template.eta +27 -73
- package/assets/content-templates/types-template.eta +1 -4
- package/assets/skills/fimo/SKILL.md +2 -2
- package/assets/skills/fimo/references/content.md +42 -46
- package/assets/skills/fimo/references/locales.md +0 -8
- package/assets/skills/fimo/references/setup-react-router.md +2 -2
- package/assets/skills/fimo/references/ui.md +1 -1
- package/assets/skills/fimo-cli/references/content.md +14 -17
- package/assets/skills/fimo-cli/references/domains.md +1 -1
- package/assets/skills/fimo-cli/references/locales.md +0 -6
- package/assets/skills/fimo-migration/SKILL.md +22 -2
- package/assets/skills/fimo-migration/references/content.md +8 -0
- package/assets/skills/fimo-migration/references/manifest.md +35 -0
- package/assets/skills/fimo-migration/references/qa.md +24 -2
- package/assets/skills/fimo-migration/references/workflow.md +12 -1
- package/assets/skills/fimo-migration/references/wrap.md +582 -0
- package/assets/skills/fimo-studio/references/custom-domains.md +1 -1
- package/assets/skills/fimo-studio/references/visual-editing.md +5 -3
- package/dist/build/vite/index.d.ts.map +1 -0
- package/dist/build/vite/plugins/content-sync.d.ts.map +1 -0
- package/dist/build/vite/plugins/content-sync.test.d.ts +2 -0
- package/dist/build/vite/plugins/content-sync.test.d.ts.map +1 -0
- package/dist/build/vite/plugins/content-sync.test.js +64 -0
- package/dist/build/vite/plugins/data-id.d.ts.map +1 -0
- package/dist/build/vite/plugins/fimo-config.d.ts.map +1 -0
- package/dist/build/vite/plugins/fimo-config.test.d.ts +2 -0
- package/dist/build/vite/plugins/fimo-config.test.d.ts.map +1 -0
- package/dist/build/vite/plugins/fimo-config.test.js +46 -0
- package/dist/build/vite/plugins/overlay.d.ts.map +1 -0
- package/dist/build/vite/plugins/robots.d.ts.map +1 -0
- package/dist/build/vite/plugins/seo.d.ts.map +1 -0
- package/dist/build/vite/plugins/server-config.d.ts.map +1 -0
- package/dist/build/vite/plugins/sitemap.d.ts.map +1 -0
- package/dist/build/vite/plugins/translations.d.ts +2 -8
- package/dist/build/vite/plugins/translations.d.ts.map +1 -0
- package/dist/build/vite/plugins/translations.js +3 -121
- package/dist/build/vite/plugins/translations.test.d.ts +2 -0
- package/dist/build/vite/plugins/translations.test.d.ts.map +1 -0
- package/dist/build/vite/plugins/translations.test.js +266 -0
- package/dist/cli/bundle.json +2 -3
- package/dist/cli/index.js +117939 -1186
- package/dist/runtime/app/FimoProviders.d.ts +1 -11
- package/dist/runtime/app/FimoProviders.d.ts.map +1 -0
- package/dist/runtime/app/FimoProviders.js +2 -23
- package/dist/runtime/app/FimoScripts.d.ts.map +1 -0
- package/dist/runtime/app/index.d.ts.map +1 -0
- package/dist/runtime/app/prefetch.d.ts.map +1 -0
- package/dist/runtime/astro/index.d.ts +2 -0
- package/dist/runtime/astro/index.d.ts.map +1 -0
- package/dist/runtime/astro/index.js +3 -0
- package/dist/runtime/astro/server.d.ts +3 -0
- package/dist/runtime/astro/server.d.ts.map +1 -0
- package/dist/runtime/astro/server.js +6 -0
- package/dist/runtime/content/DataSourcesProvider.d.ts.map +1 -0
- package/dist/runtime/content/RefreshContentProvider.d.ts.map +1 -0
- package/dist/runtime/content/RefreshContentProvider.test.d.ts +2 -0
- package/dist/runtime/content/RefreshContentProvider.test.d.ts.map +1 -0
- package/dist/runtime/content/RefreshContentProvider.test.js +33 -0
- package/dist/runtime/content/parseCollectionResponse.d.ts.map +1 -0
- package/dist/runtime/content/parseCollectionResponse.test.d.ts +2 -0
- package/dist/runtime/content/parseCollectionResponse.test.d.ts.map +1 -0
- package/dist/runtime/content/parseCollectionResponse.test.js +20 -0
- package/dist/runtime/content/utils/retrieve-data-sources.d.ts +0 -1
- package/dist/runtime/content/utils/retrieve-data-sources.d.ts.map +1 -0
- package/dist/runtime/content/utils/retrieve-data-sources.js +4 -27
- package/dist/runtime/core/AppErrorBoundary.d.ts.map +1 -0
- package/dist/runtime/core/ViteLifecycles.d.ts.map +1 -0
- package/dist/runtime/i18n/FimoLocaleContext.d.ts.map +1 -0
- package/dist/runtime/i18n/FimoLocaleContext.js +1 -0
- package/dist/runtime/i18n/locale.d.ts.map +1 -0
- package/dist/runtime/i18n/locale.test.d.ts +2 -0
- package/dist/runtime/i18n/locale.test.d.ts.map +1 -0
- package/dist/runtime/i18n/locale.test.js +58 -0
- package/dist/runtime/iframe/ConsoleErrorProvider.d.ts.map +1 -0
- package/dist/runtime/iframe/ScreenshotProvider.d.ts.map +1 -0
- package/dist/runtime/iframe/runtime/overlay-hmr.d.ts.map +1 -0
- package/dist/runtime/iframe/runtime/overlay-hmr.js +0 -3
- package/dist/runtime/iframe/runtime/overlay-runtime.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +0 -3
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +0 -2
- package/dist/runtime/next/FimoNextProviders.d.ts +24 -0
- package/dist/runtime/next/FimoNextProviders.d.ts.map +1 -0
- package/dist/runtime/next/FimoNextProviders.js +20 -0
- package/dist/runtime/next/FimoNextScripts.d.ts +10 -0
- package/dist/runtime/next/FimoNextScripts.d.ts.map +1 -0
- package/dist/runtime/next/FimoNextScripts.js +19 -0
- package/dist/runtime/next/index.d.ts +14 -0
- package/dist/runtime/next/index.d.ts.map +1 -0
- package/dist/runtime/next/index.js +14 -0
- package/dist/runtime/next/server.d.ts +2 -0
- package/dist/runtime/next/server.d.ts.map +1 -0
- package/dist/runtime/next/server.js +4 -0
- package/dist/runtime/nuxt/composables.d.ts +9 -0
- package/dist/runtime/nuxt/composables.d.ts.map +1 -0
- package/dist/runtime/nuxt/composables.js +57 -0
- package/dist/runtime/nuxt/index.d.ts +3 -0
- package/dist/runtime/nuxt/index.d.ts.map +1 -0
- package/dist/runtime/nuxt/index.js +6 -0
- package/dist/runtime/nuxt/server.d.ts +3 -0
- package/dist/runtime/nuxt/server.d.ts.map +1 -0
- package/dist/runtime/nuxt/server.js +6 -0
- package/dist/runtime/paths/get-fimo-paths.d.ts.map +1 -0
- package/dist/runtime/paths/get-fimo-paths.js +5 -15
- package/dist/runtime/paths/index.d.ts.map +1 -0
- package/dist/runtime/paths/types.d.ts.map +1 -0
- package/dist/runtime/primitives/components/Boolean.d.ts.map +1 -0
- package/dist/runtime/primitives/components/Date.d.ts.map +1 -0
- package/dist/runtime/primitives/components/Image.d.ts.map +1 -0
- package/dist/runtime/primitives/components/RichText.d.ts.map +1 -0
- package/dist/runtime/primitives/components/StaticImage.d.ts.map +1 -0
- package/dist/runtime/primitives/components/Text.d.ts.map +1 -0
- package/dist/runtime/primitives/components/Video.d.ts.map +1 -0
- package/dist/runtime/primitives/components/index.d.ts.map +1 -0
- package/dist/runtime/primitives/index.d.ts.map +1 -0
- package/dist/runtime/primitives/labels-hook.d.ts +33 -0
- package/dist/runtime/primitives/labels-hook.d.ts.map +1 -0
- package/dist/runtime/primitives/labels-hook.js +41 -0
- package/dist/runtime/primitives/labels-store.d.ts +12 -0
- package/dist/runtime/primitives/labels-store.d.ts.map +1 -0
- package/dist/runtime/primitives/labels-store.js +60 -0
- package/dist/runtime/primitives/labels-store.test.d.ts +2 -0
- package/dist/runtime/primitives/labels-store.test.d.ts.map +1 -0
- package/dist/runtime/primitives/labels-store.test.js +66 -0
- package/dist/runtime/primitives/lib/image-optimization.d.ts.map +1 -0
- package/dist/runtime/primitives/lib/index.d.ts.map +1 -0
- package/dist/runtime/primitives/lib/primitives.d.ts +2 -2
- package/dist/runtime/primitives/lib/primitives.d.ts.map +1 -0
- package/dist/runtime/primitives/lib/query.d.ts +3 -5
- package/dist/runtime/primitives/lib/query.d.ts.map +1 -0
- package/dist/runtime/primitives/lib/template.d.ts.map +1 -0
- package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts +5 -3
- package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts.map +1 -0
- package/dist/runtime/primitives/lib/useFimoLiveValue.js +11 -4
- package/dist/runtime/primitives/public-surface.test.d.ts +2 -0
- package/dist/runtime/primitives/public-surface.test.d.ts.map +1 -0
- package/dist/runtime/primitives/public-surface.test.js +45 -0
- package/dist/runtime/primitives/translations.d.ts +1 -27
- package/dist/runtime/primitives/translations.d.ts.map +1 -0
- package/dist/runtime/primitives/translations.js +7 -85
- package/dist/runtime/react/FimoSpaProviders.d.ts +36 -0
- package/dist/runtime/react/FimoSpaProviders.d.ts.map +1 -0
- package/dist/runtime/react/FimoSpaProviders.js +63 -0
- package/dist/runtime/react/index.d.ts +33 -0
- package/dist/runtime/react/index.d.ts.map +1 -0
- package/dist/runtime/react/index.js +39 -0
- package/dist/runtime/react-router/index.d.ts.map +1 -0
- package/dist/runtime/react-router/navigation-utils.d.ts.map +1 -0
- package/dist/runtime/react-router/navigation.d.ts.map +1 -0
- package/dist/runtime/react-router/navigation.test.d.ts +2 -0
- package/dist/runtime/react-router/navigation.test.d.ts.map +1 -0
- package/dist/runtime/react-router/navigation.test.js +35 -0
- package/dist/runtime/react-router/routes.d.ts.map +1 -0
- package/dist/runtime/react-router/routes.test.d.ts +2 -0
- package/dist/runtime/react-router/routes.test.d.ts.map +1 -0
- package/dist/runtime/react-router/routes.test.js +67 -0
- package/dist/runtime/remix/FimoRemixProviders.d.ts +16 -0
- package/dist/runtime/remix/FimoRemixProviders.d.ts.map +1 -0
- package/dist/runtime/remix/FimoRemixProviders.js +12 -0
- package/dist/runtime/remix/FimoRemixScripts.d.ts +10 -0
- package/dist/runtime/remix/FimoRemixScripts.d.ts.map +1 -0
- package/dist/runtime/remix/FimoRemixScripts.js +19 -0
- package/dist/runtime/remix/index.d.ts +12 -0
- package/dist/runtime/remix/index.d.ts.map +1 -0
- package/dist/runtime/remix/index.js +11 -0
- package/dist/runtime/remix/server.d.ts +2 -0
- package/dist/runtime/remix/server.d.ts.map +1 -0
- package/dist/runtime/remix/server.js +2 -0
- package/dist/runtime/routing/AppResetScrollOnNavigate.d.ts.map +1 -0
- package/dist/runtime/routing/AppRoutesProvider.d.ts.map +1 -0
- package/dist/runtime/seo/buildSeoForPath.d.ts +3 -1
- package/dist/runtime/seo/buildSeoForPath.d.ts.map +1 -0
- package/dist/runtime/seo/buildSeoForPath.js +1 -4
- package/dist/runtime/seo/htmlProps.d.ts.map +1 -0
- package/dist/runtime/seo/index.d.ts.map +1 -0
- package/dist/runtime/seo/resolver.d.ts.map +1 -0
- package/dist/runtime/shared/display-routes.d.ts +14 -0
- package/dist/runtime/shared/display-routes.d.ts.map +1 -0
- package/dist/runtime/shared/display-routes.js +25 -0
- package/dist/runtime/shared/fimo-config.server.d.ts.map +1 -0
- package/dist/runtime/shared/fimo-config.server.js +1 -0
- package/dist/runtime/shared/fimo-config.types.d.ts +2 -0
- package/dist/runtime/shared/fimo-config.types.d.ts.map +1 -0
- package/dist/runtime/shared/i18n-messages.d.ts +29 -0
- package/dist/runtime/shared/i18n-messages.d.ts.map +1 -0
- package/dist/runtime/shared/i18n-messages.js +79 -0
- package/dist/runtime/shared/i18n-messages.test.d.ts +2 -0
- package/dist/runtime/shared/i18n-messages.test.d.ts.map +1 -0
- package/dist/runtime/shared/i18n-messages.test.js +39 -0
- package/dist/runtime/shared/labels-fetch.pure.d.ts +17 -0
- package/dist/runtime/shared/labels-fetch.pure.d.ts.map +1 -0
- package/dist/runtime/shared/labels-fetch.pure.js +71 -0
- package/dist/runtime/shared/labels-fetch.server.d.ts +7 -0
- package/dist/runtime/shared/labels-fetch.server.d.ts.map +1 -0
- package/dist/runtime/shared/labels-fetch.server.js +97 -0
- package/dist/runtime/shared/labels-t.pure.d.ts +2 -0
- package/dist/runtime/shared/labels-t.pure.d.ts.map +1 -0
- package/dist/runtime/shared/labels-t.pure.js +10 -0
- package/dist/runtime/svelte/index.d.ts +3 -0
- package/dist/runtime/svelte/index.d.ts.map +1 -0
- package/dist/runtime/svelte/index.js +7 -0
- package/dist/runtime/svelte/server.d.ts +3 -0
- package/dist/runtime/svelte/server.d.ts.map +1 -0
- package/dist/runtime/svelte/server.js +6 -0
- package/dist/runtime/t/OptionalTracking.d.ts.map +1 -0
- package/dist/runtime/t/OptionalTracking.test.d.ts +2 -0
- package/dist/runtime/t/OptionalTracking.test.d.ts.map +1 -0
- package/dist/runtime/t/OptionalTracking.test.js +11 -0
- package/dist/runtime/t/Pageview.d.ts.map +1 -0
- package/dist/runtime/templates.d.ts.map +1 -0
- package/dist/runtime/vue/index.d.ts +3 -0
- package/dist/runtime/vue/index.d.ts.map +1 -0
- package/dist/runtime/vue/index.js +5 -0
- package/dist/runtime/vue/server.d.ts +3 -0
- package/dist/runtime/vue/server.d.ts.map +1 -0
- package/dist/runtime/vue/server.js +6 -0
- package/dist/runtime/wrapped/FimoFileLink.d.ts +17 -0
- package/dist/runtime/wrapped/FimoFileLink.d.ts.map +1 -0
- package/dist/runtime/wrapped/FimoFileLink.js +19 -0
- package/dist/runtime/wrapped/FimoMediaImage.d.ts +14 -0
- package/dist/runtime/wrapped/FimoMediaImage.d.ts.map +1 -0
- package/dist/runtime/wrapped/FimoMediaImage.js +20 -0
- package/dist/runtime/wrapped/FimoMediaVideo.d.ts +14 -0
- package/dist/runtime/wrapped/FimoMediaVideo.d.ts.map +1 -0
- package/dist/runtime/wrapped/FimoMediaVideo.js +20 -0
- package/dist/runtime/wrapped/FimoRichTextBlock.d.ts +23 -0
- package/dist/runtime/wrapped/FimoRichTextBlock.d.ts.map +1 -0
- package/dist/runtime/wrapped/FimoRichTextBlock.js +22 -0
- package/dist/runtime/wrapped/FimoText.d.ts +22 -0
- package/dist/runtime/wrapped/FimoText.d.ts.map +1 -0
- package/dist/runtime/wrapped/FimoText.js +20 -0
- package/dist/runtime/wrapped/WrappedContentContext.d.ts +28 -0
- package/dist/runtime/wrapped/WrappedContentContext.d.ts.map +1 -0
- package/dist/runtime/wrapped/WrappedContentContext.js +30 -0
- package/dist/runtime/wrapped/WrappedProviders.d.ts +33 -0
- package/dist/runtime/wrapped/WrappedProviders.d.ts.map +1 -0
- package/dist/runtime/wrapped/WrappedProviders.js +101 -0
- package/dist/runtime/wrapped/dom.d.ts +4 -0
- package/dist/runtime/wrapped/dom.d.ts.map +1 -0
- package/dist/runtime/wrapped/dom.js +23 -0
- package/dist/runtime/wrapped/dom.test.d.ts +2 -0
- package/dist/runtime/wrapped/dom.test.d.ts.map +1 -0
- package/dist/runtime/wrapped/dom.test.js +16 -0
- package/dist/runtime/wrapped/server.d.ts +77 -0
- package/dist/runtime/wrapped/server.d.ts.map +1 -0
- package/dist/runtime/wrapped/server.js +237 -0
- package/dist/runtime/wrapped/server.test.d.ts +2 -0
- package/dist/runtime/wrapped/server.test.d.ts.map +1 -0
- package/dist/runtime/wrapped/server.test.js +91 -0
- package/dist/scripts/apply-wrap-templates.d.ts +61 -0
- package/dist/scripts/apply-wrap-templates.js +132 -0
- package/dist/scripts/apply-wrap-templates.test.js +199 -0
- package/dist/scripts/apply-wrap.d.ts +46 -0
- package/dist/scripts/apply-wrap.js +278 -0
- package/dist/scripts/apply-wrap.test.d.ts +1 -0
- package/dist/scripts/apply-wrap.test.js +226 -0
- package/dist/scripts/cms/discover.d.ts +2 -0
- package/dist/scripts/cms/discover.js +17 -0
- package/dist/scripts/cms/example-content.d.ts +32 -0
- package/dist/scripts/cms/example-content.js +87 -0
- package/dist/scripts/cms/example-content.test.d.ts +1 -0
- package/dist/scripts/cms/example-content.test.js +57 -0
- package/dist/scripts/cms/strapi/detect.d.ts +2 -0
- package/dist/scripts/cms/strapi/detect.js +185 -0
- package/dist/scripts/cms/strapi/detect.test.d.ts +1 -0
- package/dist/scripts/cms/strapi/detect.test.js +56 -0
- package/dist/scripts/cms/strapi/index.d.ts +3 -0
- package/dist/scripts/cms/strapi/index.js +15 -0
- package/dist/scripts/cms/strapi/list-source.d.ts +17 -0
- package/dist/scripts/cms/strapi/list-source.js +90 -0
- package/dist/scripts/cms/strapi/list-source.test.d.ts +1 -0
- package/dist/scripts/cms/strapi/list-source.test.js +33 -0
- package/dist/scripts/cms/strapi/schema-map.d.ts +9 -0
- package/dist/scripts/cms/strapi/schema-map.js +138 -0
- package/dist/scripts/cms/strapi/schema-map.test.d.ts +1 -0
- package/dist/scripts/cms/strapi/schema-map.test.js +87 -0
- package/dist/scripts/cms/types.d.ts +76 -0
- package/dist/scripts/cms/types.js +11 -0
- package/dist/scripts/detect-framework.d.ts +39 -0
- package/dist/scripts/detect-framework.js +133 -0
- package/dist/scripts/detect-framework.test.d.ts +1 -0
- package/dist/scripts/detect-framework.test.js +122 -0
- package/dist/scripts/draft-rows.d.ts +19 -0
- package/dist/scripts/draft-rows.js +135 -0
- package/dist/scripts/draft-rows.test.d.ts +1 -0
- package/dist/scripts/draft-rows.test.js +113 -0
- package/dist/scripts/draft-schemas.d.ts +27 -0
- package/dist/scripts/draft-schemas.js +188 -0
- package/dist/scripts/draft-schemas.test.d.ts +1 -0
- package/dist/scripts/draft-schemas.test.js +152 -0
- package/dist/scripts/frameworks/astro/routes.d.ts +3 -0
- package/dist/scripts/frameworks/astro/routes.js +55 -0
- package/dist/scripts/frameworks/astro/template-style.d.ts +7 -0
- package/dist/scripts/frameworks/astro/template-style.js +19 -0
- package/dist/scripts/frameworks/discover.d.ts +70 -0
- package/dist/scripts/frameworks/discover.js +171 -0
- package/dist/scripts/frameworks/next/rewire.d.ts +30 -0
- package/dist/scripts/frameworks/next/rewire.js +258 -0
- package/dist/scripts/frameworks/next/rewire.test.d.ts +1 -0
- package/dist/scripts/frameworks/next/rewire.test.js +134 -0
- package/dist/scripts/frameworks/next/routes.d.ts +17 -0
- package/dist/scripts/frameworks/next/routes.js +134 -0
- package/dist/scripts/frameworks/next/routes.test.d.ts +1 -0
- package/dist/scripts/frameworks/next/routes.test.js +64 -0
- package/dist/scripts/frameworks/next/wiring.d.ts +26 -0
- package/dist/scripts/frameworks/next/wiring.js +163 -0
- package/dist/scripts/frameworks/next/wiring.test.d.ts +1 -0
- package/dist/scripts/frameworks/next/wiring.test.js +146 -0
- package/dist/scripts/frameworks/nuxt/routes.d.ts +3 -0
- package/dist/scripts/frameworks/nuxt/routes.js +54 -0
- package/dist/scripts/frameworks/nuxt/template-style.d.ts +9 -0
- package/dist/scripts/frameworks/nuxt/template-style.js +27 -0
- package/dist/scripts/frameworks/remix/routes.d.ts +3 -0
- package/dist/scripts/frameworks/remix/routes.js +50 -0
- package/dist/scripts/frameworks/sveltekit/routes.d.ts +3 -0
- package/dist/scripts/frameworks/sveltekit/routes.js +50 -0
- package/dist/scripts/frameworks/sveltekit/template-style.d.ts +9 -0
- package/dist/scripts/frameworks/sveltekit/template-style.js +54 -0
- package/dist/scripts/frameworks/vite/routes.d.ts +15 -0
- package/dist/scripts/frameworks/vite/routes.js +93 -0
- package/dist/scripts/frameworks/vite/routes.test.d.ts +1 -0
- package/dist/scripts/frameworks/vite/routes.test.js +73 -0
- package/dist/scripts/frameworks/vite/wiring.d.ts +10 -0
- package/dist/scripts/frameworks/vite/wiring.js +99 -0
- package/dist/scripts/frameworks/vite/wiring.test.d.ts +1 -0
- package/dist/scripts/frameworks/vite/wiring.test.js +88 -0
- package/dist/scripts/generated-types.test.d.ts +1 -0
- package/dist/scripts/generated-types.test.js +212 -0
- package/dist/scripts/lib/validation-report.d.ts +1 -0
- package/dist/scripts/lib/validation-report.js +3 -0
- package/dist/scripts/lint-translation-keys.test.d.ts +1 -0
- package/dist/scripts/lint-translation-keys.test.js +16 -0
- package/dist/scripts/render-parity/capture.d.ts +56 -0
- package/dist/scripts/render-parity/capture.js +366 -0
- package/dist/scripts/render-parity/capture.test.d.ts +1 -0
- package/dist/scripts/render-parity/capture.test.js +306 -0
- package/dist/scripts/render-parity/compare.d.ts +59 -0
- package/dist/scripts/render-parity/compare.js +0 -0
- package/dist/scripts/render-parity/compare.test.d.ts +1 -0
- package/dist/scripts/render-parity/compare.test.js +98 -0
- package/dist/scripts/render-parity/normalize-html.d.ts +13 -0
- package/dist/scripts/render-parity/normalize-html.js +223 -0
- package/dist/scripts/render-parity/normalize-html.test.d.ts +1 -0
- package/dist/scripts/render-parity/normalize-html.test.js +58 -0
- package/dist/scripts/render-parity/types.d.ts +41 -0
- package/dist/scripts/render-parity/types.js +1 -0
- package/dist/scripts/resolve-routes.js +1 -1
- package/dist/scripts/scan-wrappable.d.ts +196 -0
- package/dist/scripts/scan-wrappable.js +1221 -0
- package/dist/scripts/scan-wrappable.test.d.ts +1 -0
- package/dist/scripts/scan-wrappable.test.js +502 -0
- package/dist/scripts/sync-schemas.test.d.ts +1 -0
- package/dist/scripts/sync-schemas.test.js +124 -0
- package/dist/scripts/validate-route-metadata.js +11 -0
- package/dist/scripts/validate-route-metadata.test.d.ts +1 -0
- package/dist/scripts/validate-route-metadata.test.js +116 -0
- package/dist/scripts/validate-schemas.test.d.ts +1 -0
- package/dist/scripts/validate-schemas.test.js +263 -0
- package/package.json +85 -20
- package/scripts/add-ext.mjs +47 -0
- package/scripts/bump-version.mjs +58 -0
- package/scripts/bundle-cli.mjs +123 -0
- package/scripts/install-cli.mjs +36 -0
- package/scripts/lib/channels.mjs +70 -0
- package/scripts/lib/dev-release-state.mjs +44 -0
- package/scripts/lib/dev-release-state.test.ts +36 -0
- package/scripts/lib/postinstall-onboarding.test.ts +82 -0
- package/scripts/lib/release-core.mjs +225 -0
- package/scripts/lib/release-core.test.ts +147 -0
- package/scripts/migration-qa-gate.test.ts +155 -0
- package/scripts/publish-npm.mjs +180 -0
- package/templates/react-router/package.json +7 -10
- package/templates/react-router/src/pages/Index.tsx +1 -1
- package/dist/runtime/content/client/createContentClient.d.ts +0 -5
- package/dist/runtime/content/client/createContentClient.js +0 -300
- package/dist/runtime/content/client/createSingletonClient.d.ts +0 -5
- package/dist/runtime/content/client/createSingletonClient.js +0 -108
- package/dist/runtime/content/client/internal.d.ts +0 -44
- package/dist/runtime/content/client/internal.js +0 -236
- package/dist/runtime/content/client/types.d.ts +0 -136
- package/dist/runtime/content/client/useQueryLocale.d.ts +0 -2
- package/dist/runtime/content/client/useQueryLocale.js +0 -6
- /package/dist/{runtime/content/client/types.js → scripts/apply-wrap-templates.test.d.ts} +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { detectStrapi } from './detect.js';
|
|
2
|
+
import { renderStrapiListSource } from './list-source.js';
|
|
3
|
+
import { readStrapiSchemas } from './schema-map.js';
|
|
4
|
+
/** Assemble the Strapi connector for a wrapped frontend, or null if no Strapi. */
|
|
5
|
+
export function strapiConnector(frontendDir) {
|
|
6
|
+
const detection = detectStrapi(frontendDir);
|
|
7
|
+
if (!detection) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
detection,
|
|
12
|
+
readSchemas: () => (detection.appDir ? readStrapiSchemas(detection.appDir) : []),
|
|
13
|
+
renderListSource: (schemas, serverImport) => schemas.length > 0 ? renderStrapiListSource(schemas, serverImport) : null,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CmsCollectionSchema } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Emit a GENERIC Strapi-list read helper as source text. The scaffold pushes a
|
|
4
|
+
* CMS model + example content into Fimo; this helper lets a site's LIST views
|
|
5
|
+
* render that example content from Fimo (detail views + dynamic zones stay on
|
|
6
|
+
* the CMS). It is intentionally best-effort and site-neutral — the agent drops
|
|
7
|
+
* it in and wires the site's list fetchers to it (the non-deterministic last
|
|
8
|
+
* mile the machine can't own).
|
|
9
|
+
*
|
|
10
|
+
* The reshape is grounded in the Fimo runtime contract (`fetchEntries` →
|
|
11
|
+
* `{ id, data }`, media fields `{ url, ... }`, richtext fields a ProseMirror
|
|
12
|
+
* doc): richtext docs are flattened to plain text (so list helpers like a
|
|
13
|
+
* read-time estimate that expect a string don't choke), media and scalars pass
|
|
14
|
+
* through, and an empty Fimo result returns null so the caller falls back to
|
|
15
|
+
* the live CMS.
|
|
16
|
+
*/
|
|
17
|
+
export declare function renderStrapiListSource(schemas: CmsCollectionSchema[], serverImport: string): string;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emit a GENERIC Strapi-list read helper as source text. The scaffold pushes a
|
|
3
|
+
* CMS model + example content into Fimo; this helper lets a site's LIST views
|
|
4
|
+
* render that example content from Fimo (detail views + dynamic zones stay on
|
|
5
|
+
* the CMS). It is intentionally best-effort and site-neutral — the agent drops
|
|
6
|
+
* it in and wires the site's list fetchers to it (the non-deterministic last
|
|
7
|
+
* mile the machine can't own).
|
|
8
|
+
*
|
|
9
|
+
* The reshape is grounded in the Fimo runtime contract (`fetchEntries` →
|
|
10
|
+
* `{ id, data }`, media fields `{ url, ... }`, richtext fields a ProseMirror
|
|
11
|
+
* doc): richtext docs are flattened to plain text (so list helpers like a
|
|
12
|
+
* read-time estimate that expect a string don't choke), media and scalars pass
|
|
13
|
+
* through, and an empty Fimo result returns null so the caller falls back to
|
|
14
|
+
* the live CMS.
|
|
15
|
+
*/
|
|
16
|
+
export function renderStrapiListSource(schemas, serverImport) {
|
|
17
|
+
const entries = schemas
|
|
18
|
+
.filter((schema) => !schema.isSingleton)
|
|
19
|
+
.map((schema) => ` '${schema.sourceUid}': '${schema.name}',`)
|
|
20
|
+
.join('\n');
|
|
21
|
+
return `// AUTO-GENERATED by \`fimo migrate --scaffold-cms\`. Renders the CMS collections
|
|
22
|
+
// scaffolded into Fimo (EXAMPLE content) for LIST views, so pages show the fake
|
|
23
|
+
// content from Fimo while detail views and dynamic zones stay on your CMS.
|
|
24
|
+
//
|
|
25
|
+
// Wire your CMS client's list methods to this (see the migration prompt), e.g.
|
|
26
|
+
// inside fetchMany/fetchAll: const fimo = await fimoCmsList(uid, { limit });
|
|
27
|
+
// if (fimo) return fimo;
|
|
28
|
+
// Best-effort and generic by design — adjust per component as needed.
|
|
29
|
+
import { fetchEntries } from '${serverImport}';
|
|
30
|
+
|
|
31
|
+
// Strapi content-type UID -> Fimo type name (from the scaffold).
|
|
32
|
+
const FIMO_TYPES: Record<string, string> = {
|
|
33
|
+
${entries}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function docToText(node: unknown): string {
|
|
37
|
+
if (!node || typeof node !== 'object') {
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
const record = node as { text?: unknown; content?: unknown };
|
|
41
|
+
if (typeof record.text === 'string') {
|
|
42
|
+
return record.text;
|
|
43
|
+
}
|
|
44
|
+
if (Array.isArray(record.content)) {
|
|
45
|
+
return record.content.map(docToText).join(' ');
|
|
46
|
+
}
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function reshapeValue(value: unknown): unknown {
|
|
51
|
+
// Richtext is a ProseMirror doc -> flatten to plain text so string consumers
|
|
52
|
+
// (excerpts, read-time) are safe. Media { url, ... } and scalars pass through.
|
|
53
|
+
if (value && typeof value === 'object' && (value as { type?: unknown }).type === 'doc') {
|
|
54
|
+
return docToText(value);
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function reshape(data: Record<string, unknown>): Record<string, unknown> {
|
|
60
|
+
const out: Record<string, unknown> = {};
|
|
61
|
+
for (const [key, value] of Object.entries(data)) {
|
|
62
|
+
out[key] = reshapeValue(value);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A Strapi-v5-shaped list envelope built from the scaffolded Fimo content, or
|
|
69
|
+
* null when nothing was scaffolded for this UID (fall back to the live CMS).
|
|
70
|
+
*/
|
|
71
|
+
export async function fimoCmsList(
|
|
72
|
+
uid: string,
|
|
73
|
+
options: { limit?: number } = {}
|
|
74
|
+
): Promise<{ data: Record<string, unknown>[]; meta: { pagination: Record<string, number> } } | null> {
|
|
75
|
+
const typeName = FIMO_TYPES[uid];
|
|
76
|
+
if (!typeName) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const entries = await fetchEntries(typeName, options.limit ? { limit: options.limit } : {});
|
|
80
|
+
if (entries.length === 0) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const data = entries.map((entry) => ({ id: entry.id, documentId: entry.id, ...reshape(entry.data) }));
|
|
84
|
+
return {
|
|
85
|
+
data,
|
|
86
|
+
meta: { pagination: { page: 1, pageSize: data.length, pageCount: 1, total: data.length } },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { renderStrapiListSource } from './list-source.js';
|
|
3
|
+
const schema = (name, sourceUid, isSingleton = false) => ({
|
|
4
|
+
name,
|
|
5
|
+
label: name,
|
|
6
|
+
isSingleton,
|
|
7
|
+
localized: false,
|
|
8
|
+
fields: {},
|
|
9
|
+
sourceUid,
|
|
10
|
+
sourcePath: `/${name.toLowerCase()}`,
|
|
11
|
+
});
|
|
12
|
+
describe('renderStrapiListSource', () => {
|
|
13
|
+
it('emits a helper importing fetchEntries with a uid→type map for collections only', () => {
|
|
14
|
+
const src = renderStrapiListSource([
|
|
15
|
+
schema('BlogPost', 'api::blog-post.blog-post'),
|
|
16
|
+
schema('PostTag', 'api::post-tag.post-tag'),
|
|
17
|
+
schema('Global', 'api::global.global', true),
|
|
18
|
+
], 'fimo/next/server');
|
|
19
|
+
expect(src).toContain("import { fetchEntries } from 'fimo/next/server';");
|
|
20
|
+
expect(src).toContain("'api::blog-post.blog-post': 'BlogPost',");
|
|
21
|
+
expect(src).toContain("'api::post-tag.post-tag': 'PostTag',");
|
|
22
|
+
// Singletons have no list view — excluded from the map.
|
|
23
|
+
expect(src).not.toContain('api::global.global');
|
|
24
|
+
expect(src).toContain('export async function fimoCmsList(');
|
|
25
|
+
// Richtext flattening + fallback-to-CMS contract are present.
|
|
26
|
+
expect(src).toContain('docToText');
|
|
27
|
+
expect(src).toContain('return null');
|
|
28
|
+
});
|
|
29
|
+
it('threads the framework server import through', () => {
|
|
30
|
+
const src = renderStrapiListSource([schema('Cms', 'api::cms.cms')], 'fimo/astro/server');
|
|
31
|
+
expect(src).toContain("import { fetchEntries } from 'fimo/astro/server';");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CmsCollectionSchema } from '../types.js';
|
|
2
|
+
/** `blog-post` / `blog_posts` / `Blog Post` → `BlogPost`. */
|
|
3
|
+
export declare function pascalCase(name: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Read every local Strapi content-type schema into the Fimo schema shape.
|
|
6
|
+
* Two passes: first name every type (so relation targets resolve to Fimo
|
|
7
|
+
* names), then map fields.
|
|
8
|
+
*/
|
|
9
|
+
export declare function readStrapiSchemas(strapiAppDir: string): CmsCollectionSchema[];
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const TO_MANY = new Set(['oneToMany', 'manyToMany']);
|
|
4
|
+
/** `blog-post` / `blog_posts` / `Blog Post` → `BlogPost`. */
|
|
5
|
+
export function pascalCase(name) {
|
|
6
|
+
return name
|
|
7
|
+
.replace(/[^a-zA-Z0-9]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))
|
|
8
|
+
.replace(/^(.)/, (c) => c.toUpperCase());
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Map one Strapi attribute to a Fimo field. Returns `null` for fields Fimo does
|
|
12
|
+
* not model as content (the slug uid, passwords). Relations to imported types
|
|
13
|
+
* become references; relations to non-imported targets (users, plugins) and
|
|
14
|
+
* structural fields (components, dynamic zones) fall back to `json` so example
|
|
15
|
+
* content round-trips and never dangles.
|
|
16
|
+
*/
|
|
17
|
+
function mapAttribute(field, attr, uidToName) {
|
|
18
|
+
if (field === 'slug') {
|
|
19
|
+
// Fimo reserves `slug` as entry metadata (set inside the row data).
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
switch (attr.type) {
|
|
23
|
+
case 'password':
|
|
24
|
+
return null;
|
|
25
|
+
case 'string':
|
|
26
|
+
case 'text':
|
|
27
|
+
case 'email':
|
|
28
|
+
case 'uid':
|
|
29
|
+
case 'enumeration':
|
|
30
|
+
return { type: 'string' };
|
|
31
|
+
case 'richtext':
|
|
32
|
+
case 'blocks':
|
|
33
|
+
return { type: 'richtext' };
|
|
34
|
+
case 'integer':
|
|
35
|
+
case 'biginteger':
|
|
36
|
+
case 'decimal':
|
|
37
|
+
case 'float':
|
|
38
|
+
return { type: 'number' };
|
|
39
|
+
case 'boolean':
|
|
40
|
+
return { type: 'boolean' };
|
|
41
|
+
case 'date':
|
|
42
|
+
case 'datetime':
|
|
43
|
+
case 'time':
|
|
44
|
+
case 'timestamp':
|
|
45
|
+
return { type: 'date' };
|
|
46
|
+
case 'media':
|
|
47
|
+
return { type: 'media' };
|
|
48
|
+
case 'json':
|
|
49
|
+
case 'component':
|
|
50
|
+
case 'dynamiczone':
|
|
51
|
+
return { type: 'json' };
|
|
52
|
+
case 'relation': {
|
|
53
|
+
const targetName = attr.target ? uidToName.get(attr.target) : undefined;
|
|
54
|
+
if (!targetName) {
|
|
55
|
+
// Relation to a non-imported target (users-permissions, other plugins).
|
|
56
|
+
return { type: 'json' };
|
|
57
|
+
}
|
|
58
|
+
const many = attr.relation ? TO_MANY.has(attr.relation) : false;
|
|
59
|
+
return many ? { type: 'reference', target: targetName, many: true } : { type: 'reference', target: targetName };
|
|
60
|
+
}
|
|
61
|
+
default:
|
|
62
|
+
// Unknown/custom attribute — never crash; keep the value verbatim.
|
|
63
|
+
return { type: 'json' };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function mapSchema(raw, uidToName) {
|
|
67
|
+
const info = raw.schema.info ?? {};
|
|
68
|
+
const name = uidToName.get(raw.uid);
|
|
69
|
+
const fields = {};
|
|
70
|
+
for (const [field, attr] of Object.entries(raw.schema.attributes ?? {})) {
|
|
71
|
+
const mapped = mapAttribute(field, attr, uidToName);
|
|
72
|
+
if (mapped) {
|
|
73
|
+
fields[field] = mapped;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
name,
|
|
78
|
+
label: info.displayName || name,
|
|
79
|
+
isSingleton: raw.schema.kind === 'singleType',
|
|
80
|
+
localized: raw.schema.pluginOptions?.i18n?.localized === true,
|
|
81
|
+
fields,
|
|
82
|
+
sourceUid: raw.uid,
|
|
83
|
+
sourcePath: `/${info.pluralName ?? raw.contentTypeName}`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** Walk `<strapiAppDir>/src/api/<api>/content-types/<ct>/schema.json`. */
|
|
87
|
+
function collectRawSchemas(strapiAppDir) {
|
|
88
|
+
const apiDir = join(strapiAppDir, 'src', 'api');
|
|
89
|
+
if (!existsSync(apiDir)) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
const raws = [];
|
|
93
|
+
for (const apiEntry of readdirSync(apiDir, { withFileTypes: true })) {
|
|
94
|
+
if (!apiEntry.isDirectory()) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const ctRoot = join(apiDir, apiEntry.name, 'content-types');
|
|
98
|
+
if (!existsSync(ctRoot)) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
for (const ctEntry of readdirSync(ctRoot, { withFileTypes: true })) {
|
|
102
|
+
if (!ctEntry.isDirectory()) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const schemaPath = join(ctRoot, ctEntry.name, 'schema.json');
|
|
106
|
+
if (!existsSync(schemaPath)) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const schema = JSON.parse(readFileSync(schemaPath, 'utf8'));
|
|
111
|
+
raws.push({
|
|
112
|
+
uid: `api::${apiEntry.name}.${ctEntry.name}`,
|
|
113
|
+
apiName: apiEntry.name,
|
|
114
|
+
contentTypeName: ctEntry.name,
|
|
115
|
+
schema,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Malformed schema.json — skip, never abort the whole import.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return raws;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Read every local Strapi content-type schema into the Fimo schema shape.
|
|
127
|
+
* Two passes: first name every type (so relation targets resolve to Fimo
|
|
128
|
+
* names), then map fields.
|
|
129
|
+
*/
|
|
130
|
+
export function readStrapiSchemas(strapiAppDir) {
|
|
131
|
+
const raws = collectRawSchemas(strapiAppDir);
|
|
132
|
+
const uidToName = new Map();
|
|
133
|
+
for (const raw of raws) {
|
|
134
|
+
const singular = raw.schema.info?.singularName ?? raw.contentTypeName;
|
|
135
|
+
uidToName.set(raw.uid, pascalCase(singular));
|
|
136
|
+
}
|
|
137
|
+
return raws.map((raw) => mapSchema(raw, uidToName)).sort((a, b) => a.name.localeCompare(b.name));
|
|
138
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { readStrapiSchemas } from './schema-map.js';
|
|
6
|
+
let dir;
|
|
7
|
+
function writeSchema(apiName, ctName, schema) {
|
|
8
|
+
const ctDir = join(dir, 'src', 'api', apiName, 'content-types', ctName);
|
|
9
|
+
mkdirSync(ctDir, { recursive: true });
|
|
10
|
+
writeFileSync(join(ctDir, 'schema.json'), JSON.stringify(schema));
|
|
11
|
+
}
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
if (dir) {
|
|
14
|
+
rmSync(dir, { recursive: true, force: true });
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
describe('readStrapiSchemas', () => {
|
|
18
|
+
it('maps Strapi attribute types to Fimo fields and resolves relations', () => {
|
|
19
|
+
dir = mkdtempSync(join(tmpdir(), 'strapi-schema-'));
|
|
20
|
+
writeSchema('blog-post', 'blog-post', {
|
|
21
|
+
kind: 'collectionType',
|
|
22
|
+
info: { singularName: 'blog-post', pluralName: 'blog-posts', displayName: 'Blog Post' },
|
|
23
|
+
pluginOptions: {},
|
|
24
|
+
attributes: {
|
|
25
|
+
title: { type: 'string', required: true },
|
|
26
|
+
slug: { type: 'uid', targetField: 'title' },
|
|
27
|
+
content: { type: 'richtext' },
|
|
28
|
+
readTime: { type: 'integer' },
|
|
29
|
+
featured: { type: 'boolean' },
|
|
30
|
+
publishedAt: { type: 'datetime' },
|
|
31
|
+
level: { type: 'enumeration', enum: ['a', 'b'] },
|
|
32
|
+
cover: { type: 'media' },
|
|
33
|
+
hero: { type: 'component', component: 'blocks.hero', repeatable: false },
|
|
34
|
+
sections: { type: 'dynamiczone', components: [] },
|
|
35
|
+
category: { type: 'relation', relation: 'manyToOne', target: 'api::post-category.post-category' },
|
|
36
|
+
tags: { type: 'relation', relation: 'manyToMany', target: 'api::post-tag.post-tag' },
|
|
37
|
+
author: { type: 'relation', relation: 'manyToOne', target: 'plugin::users-permissions.user' },
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
writeSchema('post-category', 'post-category', {
|
|
41
|
+
kind: 'collectionType',
|
|
42
|
+
info: { singularName: 'post-category', pluralName: 'post-categories', displayName: 'Post Category' },
|
|
43
|
+
attributes: { name: { type: 'string' }, slug: { type: 'uid' } },
|
|
44
|
+
});
|
|
45
|
+
writeSchema('post-tag', 'post-tag', {
|
|
46
|
+
kind: 'collectionType',
|
|
47
|
+
info: { singularName: 'post-tag', pluralName: 'post-tags', displayName: 'Post Tag' },
|
|
48
|
+
attributes: { name: { type: 'string' } },
|
|
49
|
+
});
|
|
50
|
+
writeSchema('global', 'global', {
|
|
51
|
+
kind: 'singleType',
|
|
52
|
+
info: { singularName: 'global', pluralName: 'globals', displayName: 'Global' },
|
|
53
|
+
pluginOptions: { i18n: { localized: true } },
|
|
54
|
+
attributes: { siteName: { type: 'string' } },
|
|
55
|
+
});
|
|
56
|
+
const schemas = readStrapiSchemas(dir);
|
|
57
|
+
expect(schemas.map((s) => s.name)).toEqual(['BlogPost', 'Global', 'PostCategory', 'PostTag']);
|
|
58
|
+
const blogPost = schemas.find((s) => s.name === 'BlogPost');
|
|
59
|
+
expect(blogPost.isSingleton).toBe(false);
|
|
60
|
+
expect(blogPost.localized).toBe(false);
|
|
61
|
+
expect(blogPost.sourceUid).toBe('api::blog-post.blog-post');
|
|
62
|
+
expect(blogPost.fields).toMatchObject({
|
|
63
|
+
title: { type: 'string' },
|
|
64
|
+
content: { type: 'richtext' },
|
|
65
|
+
readTime: { type: 'number' },
|
|
66
|
+
featured: { type: 'boolean' },
|
|
67
|
+
publishedAt: { type: 'date' },
|
|
68
|
+
level: { type: 'string' },
|
|
69
|
+
cover: { type: 'media' },
|
|
70
|
+
hero: { type: 'json' },
|
|
71
|
+
sections: { type: 'json' },
|
|
72
|
+
category: { type: 'reference', target: 'PostCategory' },
|
|
73
|
+
tags: { type: 'reference', target: 'PostTag', many: true },
|
|
74
|
+
author: { type: 'json' },
|
|
75
|
+
});
|
|
76
|
+
// slug is reserved entry metadata — never a schema field.
|
|
77
|
+
expect('slug' in blogPost.fields).toBe(false);
|
|
78
|
+
const global = schemas.find((s) => s.name === 'Global');
|
|
79
|
+
expect(global.isSingleton).toBe(true);
|
|
80
|
+
expect(global.localized).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
it('returns an empty list when the app has no content types', () => {
|
|
83
|
+
dir = mkdtempSync(join(tmpdir(), 'strapi-schema-empty-'));
|
|
84
|
+
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
85
|
+
expect(readStrapiSchemas(dir)).toEqual([]);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CMS-connector contract. A connector recognizes an external headless CMS
|
|
3
|
+
* behind a wrapped frontend and reads its content-type definitions into the
|
|
4
|
+
* neutral Fimo schema shape — WITHOUT connecting to the CMS at runtime.
|
|
5
|
+
*
|
|
6
|
+
* The connector seam mirrors the framework-adapter contract: a CMS's name and
|
|
7
|
+
* quirks live ONLY under `scripts/cms/<cms>/`. Everything above (the registry,
|
|
8
|
+
* the example-content generator, the orchestrator wiring) is CMS-neutral. A
|
|
9
|
+
* CMS name appearing in neutral code is a review finding.
|
|
10
|
+
*/
|
|
11
|
+
/** Scalar Fimo field types (see `FieldTypeSchema` in api-contracts). */
|
|
12
|
+
export type FimoScalarType = 'string' | 'richtext' | 'media' | 'number' | 'boolean' | 'date' | 'json';
|
|
13
|
+
export interface FimoScalarField {
|
|
14
|
+
readonly type: FimoScalarType;
|
|
15
|
+
}
|
|
16
|
+
export interface FimoReferenceField {
|
|
17
|
+
readonly type: 'reference';
|
|
18
|
+
/** Single-target reference (Fimo type name). */
|
|
19
|
+
readonly target?: string;
|
|
20
|
+
/** Poly-target reference (Fimo type names). */
|
|
21
|
+
readonly targets?: string[];
|
|
22
|
+
/** True for to-many relations. */
|
|
23
|
+
readonly many?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export type FimoFieldDef = FimoScalarField | FimoReferenceField;
|
|
26
|
+
/**
|
|
27
|
+
* One CMS content type mapped to the Fimo schema shape. `fields` is the exact
|
|
28
|
+
* pushable `contentType.fields` payload; `name`/`label`/`isSingleton` complete
|
|
29
|
+
* the `api.createSchema` call.
|
|
30
|
+
*/
|
|
31
|
+
export interface CmsCollectionSchema {
|
|
32
|
+
/** Fimo type name (PascalCase — deterministic from the CMS singular name). */
|
|
33
|
+
readonly name: string;
|
|
34
|
+
/** Human display label. */
|
|
35
|
+
readonly label: string;
|
|
36
|
+
/** Strapi singleType → true; collectionType → false. */
|
|
37
|
+
readonly isSingleton: boolean;
|
|
38
|
+
/** The CMS marks this type as localized → seed per configured locale. */
|
|
39
|
+
readonly localized: boolean;
|
|
40
|
+
readonly fields: Record<string, FimoFieldDef>;
|
|
41
|
+
/** Source CMS uid for provenance (e.g. `api::blog-post.blog-post`). */
|
|
42
|
+
readonly sourceUid: string;
|
|
43
|
+
/** Source REST path (e.g. `/blog-posts`) — informational for later rewire. */
|
|
44
|
+
readonly sourcePath: string;
|
|
45
|
+
}
|
|
46
|
+
/** What a connector reports about a detected CMS. */
|
|
47
|
+
export interface CmsDetection {
|
|
48
|
+
/** Connector name, capitalized for the data-source prompt (e.g. `Strapi`). */
|
|
49
|
+
readonly cms: string;
|
|
50
|
+
/** Host label recorded in the data-source decision. */
|
|
51
|
+
readonly host: string;
|
|
52
|
+
/** Absolute path to the CMS app root when co-located (monorepo), else null. */
|
|
53
|
+
readonly appDir: string | null;
|
|
54
|
+
/** Repo-relative files/signals that evidenced the detection. */
|
|
55
|
+
readonly evidence: string[];
|
|
56
|
+
/** Locales configured in the CMS, when discoverable (may be empty). */
|
|
57
|
+
readonly locales: string[];
|
|
58
|
+
}
|
|
59
|
+
export interface CmsConnector {
|
|
60
|
+
readonly detection: CmsDetection;
|
|
61
|
+
/**
|
|
62
|
+
* Read every content-type definition into the Fimo schema shape. Returns an
|
|
63
|
+
* empty array when the schemas are not locally available (decoupled CMS with
|
|
64
|
+
* no co-located source) — detection can still surface the source for a
|
|
65
|
+
* keep-live decision even when schema import is impossible.
|
|
66
|
+
*/
|
|
67
|
+
readSchemas(): CmsCollectionSchema[];
|
|
68
|
+
/**
|
|
69
|
+
* Emit a generic list-read helper (as source text) the agent wires into the
|
|
70
|
+
* site's LIST views so they render the scaffolded example content from Fimo.
|
|
71
|
+
* Returns null when the CMS has no such helper. `serverImport` is the
|
|
72
|
+
* framework's Fimo server subpath (e.g. `fimo/next/server`) the helper imports
|
|
73
|
+
* `fetchEntries` from.
|
|
74
|
+
*/
|
|
75
|
+
renderListSource?(schemas: CmsCollectionSchema[], serverImport: string): string | null;
|
|
76
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CMS-connector contract. A connector recognizes an external headless CMS
|
|
3
|
+
* behind a wrapped frontend and reads its content-type definitions into the
|
|
4
|
+
* neutral Fimo schema shape — WITHOUT connecting to the CMS at runtime.
|
|
5
|
+
*
|
|
6
|
+
* The connector seam mirrors the framework-adapter contract: a CMS's name and
|
|
7
|
+
* quirks live ONLY under `scripts/cms/<cms>/`. Everything above (the registry,
|
|
8
|
+
* the example-content generator, the orchestrator wiring) is CMS-neutral. A
|
|
9
|
+
* CMS name appearing in neutral code is a review finding.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type DetectedFramework = 'next' | 'react-router' | 'remix' | 'astro' | 'nuxt' | 'sveltekit' | 'vue' | 'vite';
|
|
2
|
+
export interface FrameworkDetection {
|
|
3
|
+
framework: DetectedFramework | null;
|
|
4
|
+
/** For Next.js: the app-router directory (`app` or `src/app`) when present. */
|
|
5
|
+
appDir?: string;
|
|
6
|
+
/** For Next.js: the pages-router directory (`pages` or `src/pages`) when present. */
|
|
7
|
+
pagesDir?: string;
|
|
8
|
+
/** A recognized framework that wrap migration does not support yet. */
|
|
9
|
+
unsupported?: string;
|
|
10
|
+
/** For bare Vite: the project depends on react, so .jsx/.tsx is React JSX. */
|
|
11
|
+
react?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Detect the framework of an existing site from its package.json and layout.
|
|
15
|
+
* Used by `fimo init` (adopt-existing-site) and the wrap migration to decide
|
|
16
|
+
* how routes are discovered and which env flavor to write.
|
|
17
|
+
*/
|
|
18
|
+
export declare function detectFramework(cwd: string): FrameworkDetection;
|
|
19
|
+
/** A frontend app found inside a workspace, adoptable by `fimo init`. */
|
|
20
|
+
export interface AdoptableApp {
|
|
21
|
+
/** Absolute directory of the app. */
|
|
22
|
+
dir: string;
|
|
23
|
+
/** Directory relative to the scan root (for display + as the init dirArg). */
|
|
24
|
+
relDir: string;
|
|
25
|
+
detection: FrameworkDetection;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Find frontend apps to adopt anywhere inside a directory tree. Most real
|
|
29
|
+
* sites are monorepos where the framework app is NOT at the root and NOT
|
|
30
|
+
* necessarily under `apps/` (`frontend/`, `sites/web`, `packages/marketing`,
|
|
31
|
+
* …) — `detectFramework(root)` returns null and `fimo init` would wrongly
|
|
32
|
+
* scaffold a new project. This walks the tree up to a bounded depth, running
|
|
33
|
+
* `detectFramework` on every directory that has a package.json and pruning
|
|
34
|
+
* `node_modules`/build output, so it finds the app wherever it lives without
|
|
35
|
+
* relying on a naming convention or a workspace manifest. The root itself is
|
|
36
|
+
* excluded (the caller checks it directly); descent stops at the first
|
|
37
|
+
* framework match in a subtree (an app's own subdirs are not separate apps).
|
|
38
|
+
*/
|
|
39
|
+
export declare function findAdoptableApps(rootDir: string): AdoptableApp[];
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Detect the framework of an existing site from its package.json and layout.
|
|
5
|
+
* Used by `fimo init` (adopt-existing-site) and the wrap migration to decide
|
|
6
|
+
* how routes are discovered and which env flavor to write.
|
|
7
|
+
*/
|
|
8
|
+
export function detectFramework(cwd) {
|
|
9
|
+
const pkgPath = join(cwd, 'package.json');
|
|
10
|
+
if (!existsSync(pkgPath)) {
|
|
11
|
+
return { framework: null };
|
|
12
|
+
}
|
|
13
|
+
let pkg;
|
|
14
|
+
try {
|
|
15
|
+
pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return { framework: null };
|
|
19
|
+
}
|
|
20
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
21
|
+
if (deps.next) {
|
|
22
|
+
const appDir = ['app', 'src/app'].find((dir) => existsSync(join(cwd, dir)));
|
|
23
|
+
const pagesDir = ['pages', 'src/pages'].find((dir) => existsSync(join(cwd, dir)));
|
|
24
|
+
return { framework: 'next', appDir, pagesDir };
|
|
25
|
+
}
|
|
26
|
+
if (deps['react-router'] || deps['@react-router/dev']) {
|
|
27
|
+
return { framework: 'react-router' };
|
|
28
|
+
}
|
|
29
|
+
if (deps['@remix-run/react']) {
|
|
30
|
+
const pagesDir = ['app/routes'].find((dir) => existsSync(join(cwd, dir)));
|
|
31
|
+
return { framework: 'remix', pagesDir };
|
|
32
|
+
}
|
|
33
|
+
if (deps.astro) {
|
|
34
|
+
const pagesDir = ['src/pages'].find((dir) => existsSync(join(cwd, dir)));
|
|
35
|
+
return { framework: 'astro', pagesDir };
|
|
36
|
+
}
|
|
37
|
+
if (deps.nuxt) {
|
|
38
|
+
// 'src/pages' covers the common custom srcDir (nuxt.config `srcDir: './src'`).
|
|
39
|
+
const pagesDir = ['pages', 'app/pages', 'src/pages'].find((dir) => existsSync(join(cwd, dir)));
|
|
40
|
+
return { framework: 'nuxt', pagesDir };
|
|
41
|
+
}
|
|
42
|
+
if (deps['@sveltejs/kit']) {
|
|
43
|
+
const pagesDir = ['src/routes'].find((dir) => existsSync(join(cwd, dir)));
|
|
44
|
+
return { framework: 'sveltekit', pagesDir };
|
|
45
|
+
}
|
|
46
|
+
if (deps.vue && deps.vite) {
|
|
47
|
+
return { framework: 'vue' };
|
|
48
|
+
}
|
|
49
|
+
if (deps.vite) {
|
|
50
|
+
// A react dependency guarantees the JSX runtime: the --apply codemod and
|
|
51
|
+
// react-router route discovery are safe. Bare vite (Solid, Preact, lit)
|
|
52
|
+
// stays hands-off.
|
|
53
|
+
return { framework: 'vite', react: Boolean(deps.react) };
|
|
54
|
+
}
|
|
55
|
+
// Known frameworks we can NAME but not wrap yet — lets `fimo init` route
|
|
56
|
+
// honestly instead of silently falling through to create-new.
|
|
57
|
+
const unsupportedByDep = [
|
|
58
|
+
['gatsby', 'Gatsby'],
|
|
59
|
+
['@angular/core', 'Angular'],
|
|
60
|
+
['@docusaurus/core', 'Docusaurus'],
|
|
61
|
+
['@11ty/eleventy', 'Eleventy'],
|
|
62
|
+
];
|
|
63
|
+
for (const [dep, name] of unsupportedByDep) {
|
|
64
|
+
if (deps[dep]) {
|
|
65
|
+
return { framework: null, unsupported: name };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { framework: null };
|
|
69
|
+
}
|
|
70
|
+
const WORKSPACE_SKIP = new Set([
|
|
71
|
+
'node_modules',
|
|
72
|
+
'.git',
|
|
73
|
+
'dist',
|
|
74
|
+
'build',
|
|
75
|
+
'out',
|
|
76
|
+
'.next',
|
|
77
|
+
'.nuxt',
|
|
78
|
+
'.svelte-kit',
|
|
79
|
+
'.turbo',
|
|
80
|
+
'.vercel',
|
|
81
|
+
'coverage',
|
|
82
|
+
]);
|
|
83
|
+
/** How deep below the root the scan looks for a framework app (`apps/ui` is
|
|
84
|
+
* depth 2, `packages/frontend/web` is depth 3). */
|
|
85
|
+
const MAX_SCAN_DEPTH = 3;
|
|
86
|
+
/**
|
|
87
|
+
* Find frontend apps to adopt anywhere inside a directory tree. Most real
|
|
88
|
+
* sites are monorepos where the framework app is NOT at the root and NOT
|
|
89
|
+
* necessarily under `apps/` (`frontend/`, `sites/web`, `packages/marketing`,
|
|
90
|
+
* …) — `detectFramework(root)` returns null and `fimo init` would wrongly
|
|
91
|
+
* scaffold a new project. This walks the tree up to a bounded depth, running
|
|
92
|
+
* `detectFramework` on every directory that has a package.json and pruning
|
|
93
|
+
* `node_modules`/build output, so it finds the app wherever it lives without
|
|
94
|
+
* relying on a naming convention or a workspace manifest. The root itself is
|
|
95
|
+
* excluded (the caller checks it directly); descent stops at the first
|
|
96
|
+
* framework match in a subtree (an app's own subdirs are not separate apps).
|
|
97
|
+
*/
|
|
98
|
+
export function findAdoptableApps(rootDir) {
|
|
99
|
+
const apps = [];
|
|
100
|
+
const walk = (dir, depth) => {
|
|
101
|
+
if (depth > MAX_SCAN_DEPTH) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (dir !== rootDir && existsSync(join(dir, 'package.json'))) {
|
|
105
|
+
const detection = detectFramework(dir);
|
|
106
|
+
if (detection.framework) {
|
|
107
|
+
apps.push({ dir, relDir: relative(rootDir, dir), detection });
|
|
108
|
+
return; // don't descend into an app's own subtree
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
let entries;
|
|
112
|
+
try {
|
|
113
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
if (entry.isDirectory() && !WORKSPACE_SKIP.has(entry.name) && !entry.name.startsWith('.')) {
|
|
120
|
+
walk(join(dir, entry.name), depth + 1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
walk(rootDir, 0);
|
|
125
|
+
// Shallower + apps/-conventional first (the most likely primary site), then
|
|
126
|
+
// alphabetical — the order the interactive picker presents them in.
|
|
127
|
+
return apps.sort((a, b) => {
|
|
128
|
+
const depthA = a.relDir.split('/').length;
|
|
129
|
+
const depthB = b.relDir.split('/').length;
|
|
130
|
+
const rank = (relDir) => (relDir.startsWith('apps/') ? 0 : 1);
|
|
131
|
+
return rank(a.relDir) - rank(b.relDir) || depthA - depthB || a.relDir.localeCompare(b.relDir);
|
|
132
|
+
});
|
|
133
|
+
}
|