fimo 0.7.0-experimental.3 → 0.7.0-experimental.5
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 +2 -3
- package/assets/content-templates/hooks-template.eta +27 -73
- package/assets/content-templates/types-template.eta +1 -4
- package/assets/skills/fimo/SKILL.md +3 -3
- package/assets/skills/fimo/references/assets.md +2 -2
- package/assets/skills/fimo/references/content.md +43 -47
- package/assets/skills/fimo/references/design.md +1 -1
- 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/SKILL.md +1 -1
- package/assets/skills/fimo-cli/references/assets.md +24 -7
- package/assets/skills/fimo-cli/references/content.md +16 -19
- package/assets/skills/fimo-cli/references/domains.md +1 -1
- package/assets/skills/fimo-cli/references/locales.md +0 -6
- package/assets/skills/fimo-cli/references/media.md +142 -132
- 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 +118296 -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/strapi/client.d.ts +26 -0
- package/dist/runtime/strapi/client.d.ts.map +1 -0
- package/dist/runtime/strapi/client.js +88 -0
- package/dist/runtime/strapi/client.test.d.ts +2 -0
- package/dist/runtime/strapi/client.test.d.ts.map +1 -0
- package/dist/runtime/strapi/client.test.js +64 -0
- package/dist/runtime/strapi/index.d.ts +5 -0
- package/dist/runtime/strapi/index.d.ts.map +1 -0
- package/dist/runtime/strapi/index.js +6 -0
- package/dist/runtime/strapi/reshape.d.ts +28 -0
- package/dist/runtime/strapi/reshape.d.ts.map +1 -0
- package/dist/runtime/strapi/reshape.js +79 -0
- package/dist/runtime/strapi/reshape.test.d.ts +2 -0
- package/dist/runtime/strapi/reshape.test.d.ts.map +1 -0
- package/dist/runtime/strapi/reshape.test.js +33 -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 +108 -0
- package/dist/scripts/cms/example-content.test.d.ts +1 -0
- package/dist/scripts/cms/example-content.test.js +66 -0
- package/dist/scripts/cms/strapi/detect.d.ts +2 -0
- package/dist/scripts/cms/strapi/detect.js +186 -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 +23 -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 +149 -0
- package/dist/scripts/cms/strapi/schema-map.test.d.ts +1 -0
- package/dist/scripts/cms/strapi/schema-map.test.js +91 -0
- package/dist/scripts/cms/strapi/wire-client.d.ts +15 -0
- package/dist/scripts/cms/strapi/wire-client.js +107 -0
- package/dist/scripts/cms/strapi/wire-client.test.d.ts +1 -0
- package/dist/scripts/cms/strapi/wire-client.test.js +51 -0
- package/dist/scripts/cms/strapi/wiring.d.ts +17 -0
- package/dist/scripts/cms/strapi/wiring.js +36 -0
- package/dist/scripts/cms/strapi/wiring.test.d.ts +1 -0
- package/dist/scripts/cms/strapi/wiring.test.js +27 -0
- package/dist/scripts/cms/types.d.ts +94 -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 +89 -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,56 @@
|
|
|
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 { detectStrapi } from './detect.js';
|
|
6
|
+
let root;
|
|
7
|
+
function pkg(dir, deps) {
|
|
8
|
+
mkdirSync(dir, { recursive: true });
|
|
9
|
+
writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: 't', dependencies: deps }));
|
|
10
|
+
}
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
if (root) {
|
|
13
|
+
rmSync(root, { recursive: true, force: true });
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
describe('detectStrapi', () => {
|
|
17
|
+
it('detects Strapi in a monorepo via a types dep and locates the CMS app', () => {
|
|
18
|
+
root = mkdtempSync(join(tmpdir(), 'strapi-detect-'));
|
|
19
|
+
writeFileSync(join(root, 'pnpm-workspace.yaml'), 'packages:\n - apps/*\n');
|
|
20
|
+
const frontend = join(root, 'apps', 'ui');
|
|
21
|
+
pkg(frontend, { next: '^16.0.0', '@repo/strapi-types': 'workspace:*' });
|
|
22
|
+
const cms = join(root, 'apps', 'strapi');
|
|
23
|
+
pkg(cms, { '@strapi/strapi': '5.48.1' });
|
|
24
|
+
mkdirSync(join(cms, 'src', 'api'), { recursive: true });
|
|
25
|
+
const detection = detectStrapi(frontend);
|
|
26
|
+
expect(detection).not.toBeNull();
|
|
27
|
+
expect(detection.cms).toBe('Strapi');
|
|
28
|
+
expect(detection.host).toBe('Strapi');
|
|
29
|
+
expect(detection.appDir).toBe(cms);
|
|
30
|
+
expect(detection.evidence.some((e) => e.includes('@repo/strapi-types'))).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
it('detects Strapi via a strapi-named client module with no dependency', () => {
|
|
33
|
+
root = mkdtempSync(join(tmpdir(), 'strapi-detect-lib-'));
|
|
34
|
+
const frontend = join(root, 'app');
|
|
35
|
+
pkg(frontend, { next: '^16.0.0' });
|
|
36
|
+
mkdirSync(join(frontend, 'src', 'lib', 'strapi-api'), { recursive: true });
|
|
37
|
+
const detection = detectStrapi(frontend);
|
|
38
|
+
expect(detection).not.toBeNull();
|
|
39
|
+
expect(detection.evidence.some((e) => e.includes('strapi-api'))).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
it('detects a decoupled Strapi (SDK dep, no co-located app) with a null appDir', () => {
|
|
42
|
+
root = mkdtempSync(join(tmpdir(), 'strapi-detect-decoupled-'));
|
|
43
|
+
const frontend = join(root, 'frontend');
|
|
44
|
+
pkg(frontend, { next: '^16.0.0', '@strapi/client': '^1.0.0' });
|
|
45
|
+
const detection = detectStrapi(frontend);
|
|
46
|
+
expect(detection).not.toBeNull();
|
|
47
|
+
expect(detection.appDir).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
it('returns null when there is no Strapi signal', () => {
|
|
50
|
+
root = mkdtempSync(join(tmpdir(), 'strapi-detect-none-'));
|
|
51
|
+
const frontend = join(root, 'app');
|
|
52
|
+
pkg(frontend, { next: '^16.0.0', '@sanity/client': '^3.0.0' });
|
|
53
|
+
mkdirSync(join(frontend, 'src', 'components'), { recursive: true });
|
|
54
|
+
expect(detectStrapi(frontend)).toBeNull();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { detectStrapi } from './detect.js';
|
|
2
|
+
import { renderStrapiListSource } from './list-source.js';
|
|
3
|
+
import { readStrapiSchemas } from './schema-map.js';
|
|
4
|
+
import { wireStrapiClient } from './wire-client.js';
|
|
5
|
+
/** Assemble the Strapi connector for a wrapped frontend, or null if no Strapi. */
|
|
6
|
+
export function strapiConnector(frontendDir) {
|
|
7
|
+
const detection = detectStrapi(frontendDir);
|
|
8
|
+
if (!detection) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
detection,
|
|
13
|
+
readSchemas: () => (detection.appDir ? readStrapiSchemas(detection.appDir) : []),
|
|
14
|
+
renderListSource: (schemas, serverImport) => schemas.length > 0 ? renderStrapiListSource(schemas, serverImport) : null,
|
|
15
|
+
wireClient: (cwd, schemas) => detection.clientDir
|
|
16
|
+
? wireStrapiClient(cwd, detection.clientDir, schemas)
|
|
17
|
+
: {
|
|
18
|
+
changes: [],
|
|
19
|
+
warnings: ['No Strapi client directory detected — wire fimoStrapiFetch manually.'],
|
|
20
|
+
wired: false,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -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,149 @@
|
|
|
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
|
+
const examples = {};
|
|
71
|
+
for (const [field, attr] of Object.entries(raw.schema.attributes ?? {})) {
|
|
72
|
+
const mapped = mapAttribute(field, attr, uidToName);
|
|
73
|
+
if (mapped) {
|
|
74
|
+
fields[field] = mapped;
|
|
75
|
+
// Dynamic zones (and repeatable components) render as ARRAYS a page
|
|
76
|
+
// builder maps over; a single component as an OBJECT. Seed the correct
|
|
77
|
+
// empty shape so pages render instead of crashing on `{}.map(...)`.
|
|
78
|
+
if (attr.type === 'dynamiczone' || (attr.type === 'component' && attr.repeatable)) {
|
|
79
|
+
examples[field] = [];
|
|
80
|
+
}
|
|
81
|
+
else if (attr.type === 'component') {
|
|
82
|
+
examples[field] = {};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
name,
|
|
88
|
+
label: info.displayName || name,
|
|
89
|
+
isSingleton: raw.schema.kind === 'singleType',
|
|
90
|
+
localized: raw.schema.pluginOptions?.i18n?.localized === true,
|
|
91
|
+
fields,
|
|
92
|
+
...(Object.keys(examples).length > 0 ? { examples } : {}),
|
|
93
|
+
sourceUid: raw.uid,
|
|
94
|
+
sourcePath: `/${info.pluralName ?? raw.contentTypeName}`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** Walk `<strapiAppDir>/src/api/<api>/content-types/<ct>/schema.json`. */
|
|
98
|
+
function collectRawSchemas(strapiAppDir) {
|
|
99
|
+
const apiDir = join(strapiAppDir, 'src', 'api');
|
|
100
|
+
if (!existsSync(apiDir)) {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
const raws = [];
|
|
104
|
+
for (const apiEntry of readdirSync(apiDir, { withFileTypes: true })) {
|
|
105
|
+
if (!apiEntry.isDirectory()) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const ctRoot = join(apiDir, apiEntry.name, 'content-types');
|
|
109
|
+
if (!existsSync(ctRoot)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
for (const ctEntry of readdirSync(ctRoot, { withFileTypes: true })) {
|
|
113
|
+
if (!ctEntry.isDirectory()) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const schemaPath = join(ctRoot, ctEntry.name, 'schema.json');
|
|
117
|
+
if (!existsSync(schemaPath)) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const schema = JSON.parse(readFileSync(schemaPath, 'utf8'));
|
|
122
|
+
raws.push({
|
|
123
|
+
uid: `api::${apiEntry.name}.${ctEntry.name}`,
|
|
124
|
+
apiName: apiEntry.name,
|
|
125
|
+
contentTypeName: ctEntry.name,
|
|
126
|
+
schema,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Malformed schema.json — skip, never abort the whole import.
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return raws;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Read every local Strapi content-type schema into the Fimo schema shape.
|
|
138
|
+
* Two passes: first name every type (so relation targets resolve to Fimo
|
|
139
|
+
* names), then map fields.
|
|
140
|
+
*/
|
|
141
|
+
export function readStrapiSchemas(strapiAppDir) {
|
|
142
|
+
const raws = collectRawSchemas(strapiAppDir);
|
|
143
|
+
const uidToName = new Map();
|
|
144
|
+
for (const raw of raws) {
|
|
145
|
+
const singular = raw.schema.info?.singularName ?? raw.contentTypeName;
|
|
146
|
+
uidToName.set(raw.uid, pascalCase(singular));
|
|
147
|
+
}
|
|
148
|
+
return raws.map((raw) => mapSchema(raw, uidToName)).sort((a, b) => a.name.localeCompare(b.name));
|
|
149
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
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
|
+
// Structural fields get the correct empty shape so pages render:
|
|
79
|
+
// dynamic zone → array, single component → object.
|
|
80
|
+
expect(blogPost.examples?.sections).toEqual([]);
|
|
81
|
+
expect(blogPost.examples?.hero).toEqual({});
|
|
82
|
+
const global = schemas.find((s) => s.name === 'Global');
|
|
83
|
+
expect(global.isSingleton).toBe(true);
|
|
84
|
+
expect(global.localized).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
it('returns an empty list when the app has no content types', () => {
|
|
87
|
+
dir = mkdtempSync(join(tmpdir(), 'strapi-schema-empty-'));
|
|
88
|
+
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
89
|
+
expect(readStrapiSchemas(dir)).toEqual([]);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CmsCollectionSchema } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Machine-wire a Strapi frontend to read from Fimo: emit the `fimo-strapi`
|
|
4
|
+
* module in the CMS client dir, then swap every bare `fetch(...)` in that dir
|
|
5
|
+
* to `fimoStrapiFetch(...)`. Because every Strapi client funnels through one
|
|
6
|
+
* `fetch`, this makes the WHOLE site read scaffolded content from Fimo — no
|
|
7
|
+
* per-method or per-call-site knowledge, no agent step. Unscaffolded types fall
|
|
8
|
+
* through to the live CMS inside the shim.
|
|
9
|
+
*/
|
|
10
|
+
export interface StrapiWireResult {
|
|
11
|
+
changes: string[];
|
|
12
|
+
warnings: string[];
|
|
13
|
+
wired: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function wireStrapiClient(cwd: string, clientDir: string, schemas: CmsCollectionSchema[]): StrapiWireResult;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseSync } from 'oxc-parser';
|
|
4
|
+
import { renderFimoStrapiModule } from './wiring.js';
|
|
5
|
+
function walk(node, visit) {
|
|
6
|
+
if (!node || typeof node !== 'object') {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (Array.isArray(node)) {
|
|
10
|
+
for (const child of node) {
|
|
11
|
+
walk(child, visit);
|
|
12
|
+
}
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const record = node;
|
|
16
|
+
if (typeof record.type === 'string') {
|
|
17
|
+
visit(record);
|
|
18
|
+
}
|
|
19
|
+
for (const [key, value] of Object.entries(record)) {
|
|
20
|
+
if (key !== 'type' && value && typeof value === 'object') {
|
|
21
|
+
walk(value, visit);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Callee spans of bare `fetch(...)` calls (not `x.fetch()` / `this.fetch()`). */
|
|
26
|
+
function bareFetchCallees(filePath, code) {
|
|
27
|
+
let program;
|
|
28
|
+
try {
|
|
29
|
+
program = parseSync(filePath, code).program;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const spans = [];
|
|
35
|
+
walk(program, (n) => {
|
|
36
|
+
if (n.type === 'CallExpression' && n.callee?.type === 'Identifier' && n.callee.name === 'fetch') {
|
|
37
|
+
spans.push({ start: n.callee.start, end: n.callee.end });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return spans;
|
|
41
|
+
}
|
|
42
|
+
function applyReplacements(code, edits) {
|
|
43
|
+
let next = code;
|
|
44
|
+
for (const edit of [...edits].sort((a, b) => b.start - a.start)) {
|
|
45
|
+
next = next.slice(0, edit.start) + edit.text + next.slice(edit.end);
|
|
46
|
+
}
|
|
47
|
+
return next;
|
|
48
|
+
}
|
|
49
|
+
function relativeImport(fromFile, toFile) {
|
|
50
|
+
const rel = path
|
|
51
|
+
.relative(path.dirname(fromFile), toFile)
|
|
52
|
+
.replace(/\.tsx?$/, '')
|
|
53
|
+
.split(path.sep)
|
|
54
|
+
.join('/');
|
|
55
|
+
return rel.startsWith('.') ? rel : `./${rel}`;
|
|
56
|
+
}
|
|
57
|
+
function sourceFiles(dir) {
|
|
58
|
+
const out = [];
|
|
59
|
+
const walkDir = (current) => {
|
|
60
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
61
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.')) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const full = path.join(current, entry.name);
|
|
65
|
+
if (entry.isDirectory()) {
|
|
66
|
+
walkDir(full);
|
|
67
|
+
}
|
|
68
|
+
else if (/\.(tsx?|jsx?|mjs)$/.test(entry.name)) {
|
|
69
|
+
out.push(full);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
if (fs.existsSync(dir)) {
|
|
74
|
+
walkDir(dir);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
export function wireStrapiClient(cwd, clientDir, schemas) {
|
|
79
|
+
const changes = [];
|
|
80
|
+
const warnings = [];
|
|
81
|
+
if (!fs.existsSync(clientDir)) {
|
|
82
|
+
return { changes, warnings: [`CMS client directory not found: ${path.relative(cwd, clientDir)}`], wired: false };
|
|
83
|
+
}
|
|
84
|
+
const modulePath = path.join(clientDir, 'fimo-strapi.ts');
|
|
85
|
+
fs.writeFileSync(modulePath, renderFimoStrapiModule(schemas));
|
|
86
|
+
changes.push(`generated ${path.relative(cwd, modulePath)}`);
|
|
87
|
+
let swapped = 0;
|
|
88
|
+
for (const file of sourceFiles(clientDir)) {
|
|
89
|
+
if (path.resolve(file) === path.resolve(modulePath)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const code = fs.readFileSync(file, 'utf8');
|
|
93
|
+
const spans = bareFetchCallees(file, code);
|
|
94
|
+
if (spans.length === 0) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
let next = applyReplacements(code, spans.map((span) => ({ ...span, text: 'fimoStrapiFetch' })));
|
|
98
|
+
next = `import { fimoStrapiFetch } from '${relativeImport(file, modulePath)}';\n${next}`;
|
|
99
|
+
fs.writeFileSync(file, next);
|
|
100
|
+
swapped += spans.length;
|
|
101
|
+
changes.push(`rewired ${path.relative(cwd, file)}: ${spans.length} fetch call(s) → fimoStrapiFetch`);
|
|
102
|
+
}
|
|
103
|
+
if (swapped === 0) {
|
|
104
|
+
warnings.push(`No bare fetch() call found under ${path.relative(cwd, clientDir)} — wire fimoStrapiFetch into the client manually.`);
|
|
105
|
+
}
|
|
106
|
+
return { changes, warnings, wired: swapped > 0 };
|
|
107
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, readFileSync, 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 { wireStrapiClient } from './wire-client.js';
|
|
6
|
+
let dir;
|
|
7
|
+
const schemas = [
|
|
8
|
+
{
|
|
9
|
+
name: 'BlogPost',
|
|
10
|
+
label: 'Blog Post',
|
|
11
|
+
isSingleton: false,
|
|
12
|
+
localized: false,
|
|
13
|
+
fields: {},
|
|
14
|
+
sourceUid: 'api::blog-post.blog-post',
|
|
15
|
+
sourcePath: '/blog-posts',
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
if (dir) {
|
|
20
|
+
rmSync(dir, { recursive: true, force: true });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
describe('wireStrapiClient', () => {
|
|
24
|
+
it('generates the fimo-strapi module and swaps bare fetch calls', () => {
|
|
25
|
+
dir = mkdtempSync(join(tmpdir(), 'wire-strapi-'));
|
|
26
|
+
const clientDir = join(dir, 'src', 'lib', 'strapi-api');
|
|
27
|
+
mkdirSync(clientDir, { recursive: true });
|
|
28
|
+
writeFileSync(join(clientDir, 'base.ts'), 'export async function req(url: string, init: RequestInit) {\n const response = await fetch(url, init);\n return response;\n}\n');
|
|
29
|
+
writeFileSync(join(clientDir, 'other.ts'), 'export const x = 1;\n');
|
|
30
|
+
const result = wireStrapiClient(dir, clientDir, schemas);
|
|
31
|
+
expect(result.wired).toBe(true);
|
|
32
|
+
const generated = readFileSync(join(clientDir, 'fimo-strapi.ts'), 'utf8');
|
|
33
|
+
expect(generated).toContain('createFimoStrapiFetch');
|
|
34
|
+
expect(generated).toContain("'blog-posts': 'BlogPost',");
|
|
35
|
+
const base = readFileSync(join(clientDir, 'base.ts'), 'utf8');
|
|
36
|
+
expect(base).toContain('await fimoStrapiFetch(url, init)');
|
|
37
|
+
expect(base).not.toContain('await fetch(');
|
|
38
|
+
expect(base).toContain("import { fimoStrapiFetch } from './fimo-strapi';");
|
|
39
|
+
// A file with no fetch stays untouched.
|
|
40
|
+
expect(readFileSync(join(clientDir, 'other.ts'), 'utf8')).not.toContain('fimoStrapiFetch');
|
|
41
|
+
});
|
|
42
|
+
it('warns when no bare fetch is found', () => {
|
|
43
|
+
dir = mkdtempSync(join(tmpdir(), 'wire-strapi-none-'));
|
|
44
|
+
const clientDir = join(dir, 'client');
|
|
45
|
+
mkdirSync(clientDir, { recursive: true });
|
|
46
|
+
writeFileSync(join(clientDir, 'base.ts'), 'export const x = 1;\n');
|
|
47
|
+
const result = wireStrapiClient(dir, clientDir, schemas);
|
|
48
|
+
expect(result.wired).toBe(false);
|
|
49
|
+
expect(result.warnings[0]).toContain('No bare fetch()');
|
|
50
|
+
});
|
|
51
|
+
});
|