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,134 @@
|
|
|
1
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const PAGE_FILES = new Set(['page.tsx', 'page.jsx', 'page.ts', 'page.js', 'page.mdx']);
|
|
4
|
+
const titleCase = (seg) => seg
|
|
5
|
+
.replace(/[_\-]+/g, ' ')
|
|
6
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
7
|
+
.split(/\s+/)
|
|
8
|
+
.filter(Boolean)
|
|
9
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
|
10
|
+
.join(' ');
|
|
11
|
+
function segmentToPath(segment) {
|
|
12
|
+
// Route groups `(marketing)` don't contribute URL segments. Parallel-route
|
|
13
|
+
// slots `@modal` and private folders `_lib` are not standalone pages.
|
|
14
|
+
if (segment.startsWith('(')) {
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
if (segment.startsWith('@') || segment.startsWith('_')) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const catchAll = segment.match(/^\[\.\.\.(.+)\]$/) ?? segment.match(/^\[\[\.\.\.(.+)\]\]$/);
|
|
21
|
+
if (catchAll) {
|
|
22
|
+
return `:${catchAll[1]}*`;
|
|
23
|
+
}
|
|
24
|
+
const param = segment.match(/^\[(.+)\]$/);
|
|
25
|
+
if (param) {
|
|
26
|
+
return `:${param[1]}`;
|
|
27
|
+
}
|
|
28
|
+
return segment;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Discover page routes of a Next.js app-router directory (`app/` or
|
|
32
|
+
* `src/app/`). Returns display routes for `fimo-config.json#routes` so the
|
|
33
|
+
* Studio page switcher can enumerate a wrapped site's pages.
|
|
34
|
+
*/
|
|
35
|
+
export function discoverNextRoutes(appDir) {
|
|
36
|
+
const routes = [];
|
|
37
|
+
const walk = (dir, segments) => {
|
|
38
|
+
let entries;
|
|
39
|
+
try {
|
|
40
|
+
entries = readdirSync(dir);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (entries.some((entry) => PAGE_FILES.has(entry))) {
|
|
46
|
+
const parts = segments.filter((s) => s.length > 0);
|
|
47
|
+
const routePath = `/${parts.join('/')}`.replace(/\/+/g, '/');
|
|
48
|
+
const lastStatic = [...parts].reverse().find((p) => !p.startsWith(':'));
|
|
49
|
+
routes.push({
|
|
50
|
+
id: routePath === '/' ? 'home' : parts.map((p) => p.replace(/^:/, '')).join('-'),
|
|
51
|
+
path: routePath,
|
|
52
|
+
label: routePath === '/' ? 'Home' : titleCase(lastStatic ?? parts[parts.length - 1] ?? routePath),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
const full = join(dir, entry);
|
|
57
|
+
let isDir = false;
|
|
58
|
+
try {
|
|
59
|
+
isDir = statSync(full).isDirectory();
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!isDir) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const mapped = segmentToPath(entry);
|
|
68
|
+
if (mapped === null) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
walk(full, [...segments, mapped]);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
walk(appDir, []);
|
|
75
|
+
return routes.sort((a, b) => a.path.localeCompare(b.path));
|
|
76
|
+
}
|
|
77
|
+
const PAGES_EXTENSIONS = /\.(tsx|jsx|ts|js)$/;
|
|
78
|
+
const PAGES_SKIP = new Set(['api']);
|
|
79
|
+
/**
|
|
80
|
+
* Pages-router discovery (`pages/` or `src/pages/`): every module is a route
|
|
81
|
+
* except `_app`/`_document`/`_error` and `api/`. `[param]` segments map to
|
|
82
|
+
* `:param` (the parity capture skips dynamic routes, same as app router).
|
|
83
|
+
*/
|
|
84
|
+
export function discoverNextPagesRoutes(pagesDir) {
|
|
85
|
+
const routes = [];
|
|
86
|
+
const toSegment = (name) => name
|
|
87
|
+
.replace(/^\[\.\.\.(.+)\]$/, ':$1')
|
|
88
|
+
.replace(/^\[\[\.\.\.(.+)\]\]$/, ':$1')
|
|
89
|
+
.replace(/^\[(.+)\]$/, ':$1');
|
|
90
|
+
const walk = (dir, segments) => {
|
|
91
|
+
let entries;
|
|
92
|
+
try {
|
|
93
|
+
entries = readdirSync(dir);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
const full = join(dir, entry);
|
|
100
|
+
let isDir = false;
|
|
101
|
+
try {
|
|
102
|
+
isDir = statSync(full).isDirectory();
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (isDir) {
|
|
108
|
+
// api/ is only special at the pages root.
|
|
109
|
+
if (!(segments.length === 0 && PAGES_SKIP.has(entry))) {
|
|
110
|
+
walk(full, [...segments, toSegment(entry)]);
|
|
111
|
+
}
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (!PAGES_EXTENSIONS.test(entry)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const base = entry.replace(PAGES_EXTENSIONS, '');
|
|
118
|
+
if (base.startsWith('_')) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const parts = base === 'index' ? segments : [...segments, toSegment(base)];
|
|
122
|
+
const routePath = `/${parts.join('/')}`.replace(/\/+/g, '/');
|
|
123
|
+
const lastStatic = [...parts].reverse().find((part) => !part.startsWith(':'));
|
|
124
|
+
routes.push({
|
|
125
|
+
id: routePath === '/' ? 'home' : parts.map((part) => part.replace(/^:/, '')).join('-'),
|
|
126
|
+
path: routePath,
|
|
127
|
+
label: routePath === '/' ? 'Home' : titleCase(lastStatic ?? parts[parts.length - 1] ?? routePath),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
walk(pagesDir, []);
|
|
132
|
+
routes.sort((a, b) => a.path.localeCompare(b.path));
|
|
133
|
+
return routes;
|
|
134
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
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 { discoverNextRoutes } from './routes.js';
|
|
6
|
+
let dir;
|
|
7
|
+
const app = (pages) => {
|
|
8
|
+
dir = mkdtempSync(join(tmpdir(), 'next-routes-'));
|
|
9
|
+
for (const page of pages) {
|
|
10
|
+
const full = join(dir, page);
|
|
11
|
+
mkdirSync(join(full, '..'), { recursive: true });
|
|
12
|
+
writeFileSync(full, 'export default function Page() { return null }');
|
|
13
|
+
}
|
|
14
|
+
return dir;
|
|
15
|
+
};
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
if (dir) {
|
|
18
|
+
rmSync(dir, { recursive: true, force: true });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
describe('discoverNextRoutes', () => {
|
|
22
|
+
it('discovers static routes', () => {
|
|
23
|
+
const routes = discoverNextRoutes(app(['page.tsx', 'about/page.tsx', 'pricing/page.tsx']));
|
|
24
|
+
expect(routes.map((r) => r.path)).toEqual(['/', '/about', '/pricing']);
|
|
25
|
+
expect(routes[0]).toEqual({ id: 'home', path: '/', label: 'Home' });
|
|
26
|
+
expect(routes[1]).toEqual({ id: 'about', path: '/about', label: 'About' });
|
|
27
|
+
});
|
|
28
|
+
it('maps dynamic and catch-all segments', () => {
|
|
29
|
+
const routes = discoverNextRoutes(app(['blog/[slug]/page.tsx', 'docs/[...path]/page.tsx']));
|
|
30
|
+
expect(routes.map((r) => r.path)).toEqual(['/blog/:slug', '/docs/:path*']);
|
|
31
|
+
expect(routes[0].label).toBe('Blog');
|
|
32
|
+
});
|
|
33
|
+
it('collapses route groups and skips private and parallel segments', () => {
|
|
34
|
+
const routes = discoverNextRoutes(app(['(marketing)/features/page.tsx', '_internal/secret/page.tsx', '@modal/photo/page.tsx']));
|
|
35
|
+
expect(routes.map((r) => r.path)).toEqual(['/features']);
|
|
36
|
+
});
|
|
37
|
+
it('returns [] for a missing directory', () => {
|
|
38
|
+
expect(discoverNextRoutes(join(tmpdir(), 'does-not-exist'))).toEqual([]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('discoverNextPagesRoutes', () => {
|
|
42
|
+
it('maps pages-router modules to routes, skipping api and _ files', async () => {
|
|
43
|
+
const { mkdtempSync, mkdirSync, writeFileSync, rmSync } = await import('node:fs');
|
|
44
|
+
const { tmpdir } = await import('node:os');
|
|
45
|
+
const { join } = await import('node:path');
|
|
46
|
+
const dir = mkdtempSync(join(tmpdir(), 'pages-'));
|
|
47
|
+
try {
|
|
48
|
+
mkdirSync(join(dir, 'blog'), { recursive: true });
|
|
49
|
+
mkdirSync(join(dir, 'api'), { recursive: true });
|
|
50
|
+
writeFileSync(join(dir, 'index.tsx'), '');
|
|
51
|
+
writeFileSync(join(dir, 'about.tsx'), '');
|
|
52
|
+
writeFileSync(join(dir, '_app.tsx'), '');
|
|
53
|
+
writeFileSync(join(dir, '_document.tsx'), '');
|
|
54
|
+
writeFileSync(join(dir, 'api/hello.ts'), '');
|
|
55
|
+
writeFileSync(join(dir, 'blog/index.tsx'), '');
|
|
56
|
+
writeFileSync(join(dir, 'blog/[slug].tsx'), '');
|
|
57
|
+
const { discoverNextPagesRoutes } = await import('./routes.js');
|
|
58
|
+
expect(discoverNextPagesRoutes(dir).map((route) => route.path)).toEqual(['/', '/about', '/blog', '/blog/:slug']);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
rmSync(dir, { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FrameworkDetection } from '../../detect-framework.js';
|
|
2
|
+
/**
|
|
3
|
+
* Next.js-side wrap wiring: everything the wrap migration must touch that is
|
|
4
|
+
* specific to this framework lives HERE, not in the generic flow. Mirror this
|
|
5
|
+
* module when adding a framework (see the fimo-framework-adapter skill).
|
|
6
|
+
*/
|
|
7
|
+
export declare function ensureNextWiring(cwd: string): {
|
|
8
|
+
changes: string[];
|
|
9
|
+
warnings: string[];
|
|
10
|
+
};
|
|
11
|
+
export interface RootWiringResult {
|
|
12
|
+
changes: string[];
|
|
13
|
+
warnings: string[];
|
|
14
|
+
/** True when the root wiring is in place (pre-existing or just applied). */
|
|
15
|
+
wired: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Wire the app-router root layout deterministically: imports, async default
|
|
19
|
+
* export, loader calls, `<FimoNextScripts />` in head, and `{children}`
|
|
20
|
+
* wrapped in `<FimoNextProviders>`. Only the provably safe shape is touched —
|
|
21
|
+
* one default-exported function whose JSX returns `<html>…<body>` with a
|
|
22
|
+
* `{children}` expression somewhere inside body (create-next-app and its
|
|
23
|
+
* variants). Anything else bails with the reason, and the agent prompt keeps
|
|
24
|
+
* the manual recipe.
|
|
25
|
+
*/
|
|
26
|
+
export declare function ensureNextLayoutWiring(cwd: string, detection: Pick<FrameworkDetection, 'appDir'>): RootWiringResult;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseSync } from 'oxc-parser';
|
|
4
|
+
import { ensureFimoImport } from '../../apply-wrap.js';
|
|
5
|
+
/**
|
|
6
|
+
* Next.js-side wrap wiring: everything the wrap migration must touch that is
|
|
7
|
+
* specific to this framework lives HERE, not in the generic flow. Mirror this
|
|
8
|
+
* module when adding a framework (see the fimo-framework-adapter skill).
|
|
9
|
+
*/
|
|
10
|
+
export function ensureNextWiring(cwd) {
|
|
11
|
+
const changes = [];
|
|
12
|
+
const warnings = [];
|
|
13
|
+
const configFile = ['next.config.ts', 'next.config.mjs', 'next.config.js', 'next.config.cjs']
|
|
14
|
+
.map((candidate) => path.join(cwd, candidate))
|
|
15
|
+
.find((candidate) => fs.existsSync(candidate));
|
|
16
|
+
if (configFile) {
|
|
17
|
+
const source = fs.readFileSync(configFile, 'utf8');
|
|
18
|
+
if (!source.includes('allowedDevOrigins')) {
|
|
19
|
+
// The three mainstream config shapes: a named object binding, a direct
|
|
20
|
+
// `export default {` literal, and CJS `module.exports = {`.
|
|
21
|
+
let patched = source.replace(/(const\s+nextConfig[^=]*=\s*\{)/, `$1\n allowedDevOrigins: ['*.preview.fimo.ai'],`);
|
|
22
|
+
if (patched === source) {
|
|
23
|
+
patched = source.replace(/((?:export\s+default|module\.exports\s*=)\s*\{)/, `$1\n allowedDevOrigins: ['*.preview.fimo.ai'],\n`);
|
|
24
|
+
}
|
|
25
|
+
if (patched !== source) {
|
|
26
|
+
fs.writeFileSync(configFile, patched);
|
|
27
|
+
changes.push(`added allowedDevOrigins to ${path.basename(configFile)}`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
warnings.push(`Could not patch ${path.basename(configFile)} automatically — add: allowedDevOrigins: ['*.preview.fimo.ai']`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
warnings.push("No next.config found — create one with: allowedDevOrigins: ['*.preview.fimo.ai']");
|
|
36
|
+
}
|
|
37
|
+
return { changes, warnings };
|
|
38
|
+
}
|
|
39
|
+
function findFirst(node, predicate) {
|
|
40
|
+
if (!node || typeof node !== 'object') {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (predicate(node)) {
|
|
44
|
+
return node;
|
|
45
|
+
}
|
|
46
|
+
for (const value of Object.values(node)) {
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
for (const entry of value) {
|
|
49
|
+
const found = findFirst(entry, predicate);
|
|
50
|
+
if (found) {
|
|
51
|
+
return found;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (value && typeof value === 'object') {
|
|
56
|
+
const found = findFirst(value, predicate);
|
|
57
|
+
if (found) {
|
|
58
|
+
return found;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const jsxName = (element) => element?.openingElement?.name?.name;
|
|
65
|
+
/**
|
|
66
|
+
* Wire the app-router root layout deterministically: imports, async default
|
|
67
|
+
* export, loader calls, `<FimoNextScripts />` in head, and `{children}`
|
|
68
|
+
* wrapped in `<FimoNextProviders>`. Only the provably safe shape is touched —
|
|
69
|
+
* one default-exported function whose JSX returns `<html>…<body>` with a
|
|
70
|
+
* `{children}` expression somewhere inside body (create-next-app and its
|
|
71
|
+
* variants). Anything else bails with the reason, and the agent prompt keeps
|
|
72
|
+
* the manual recipe.
|
|
73
|
+
*/
|
|
74
|
+
export function ensureNextLayoutWiring(cwd, detection) {
|
|
75
|
+
if (!detection.appDir) {
|
|
76
|
+
return {
|
|
77
|
+
changes: [],
|
|
78
|
+
warnings: ['Root wiring not applied: pages-router project (no app directory) — wire pages/_app manually.'],
|
|
79
|
+
wired: false,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const layoutFile = ['layout.tsx', 'layout.jsx', 'layout.js']
|
|
83
|
+
.map((candidate) => path.join(cwd, detection.appDir, candidate))
|
|
84
|
+
.find((candidate) => fs.existsSync(candidate));
|
|
85
|
+
const rel = layoutFile ? path.relative(cwd, layoutFile) : `${detection.appDir}/layout.tsx`;
|
|
86
|
+
const bail = (reason) => ({
|
|
87
|
+
changes: [],
|
|
88
|
+
warnings: [`Root wiring not applied: ${reason} — follow the layout recipe in the agent prompt (${rel}).`],
|
|
89
|
+
wired: false,
|
|
90
|
+
});
|
|
91
|
+
if (!layoutFile) {
|
|
92
|
+
return bail('no root layout file found');
|
|
93
|
+
}
|
|
94
|
+
const code = fs.readFileSync(layoutFile, 'utf8');
|
|
95
|
+
if (code.includes('FimoNextProviders')) {
|
|
96
|
+
return { changes: [], warnings: [], wired: true };
|
|
97
|
+
}
|
|
98
|
+
let program;
|
|
99
|
+
try {
|
|
100
|
+
program = parseSync(layoutFile, code).program;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return bail('root layout could not be parsed');
|
|
104
|
+
}
|
|
105
|
+
const exportDefault = (program.body ?? []).find((node) => node.type === 'ExportDefaultDeclaration');
|
|
106
|
+
if (!exportDefault || exportDefault.declaration?.type !== 'FunctionDeclaration') {
|
|
107
|
+
return bail('the default export is not a plain function declaration');
|
|
108
|
+
}
|
|
109
|
+
const layoutFn = exportDefault.declaration;
|
|
110
|
+
const body = layoutFn.body;
|
|
111
|
+
if (!body || (body.type !== 'FunctionBody' && body.type !== 'BlockStatement')) {
|
|
112
|
+
return bail('the layout function has no block body');
|
|
113
|
+
}
|
|
114
|
+
const html = findFirst(body, (node) => node?.type === 'JSXElement' && jsxName(node) === 'html');
|
|
115
|
+
if (!html) {
|
|
116
|
+
return bail('the layout does not return an <html> element');
|
|
117
|
+
}
|
|
118
|
+
const htmlBody = (html.children ?? []).find((child) => child?.type === 'JSXElement' && jsxName(child) === 'body');
|
|
119
|
+
if (!htmlBody) {
|
|
120
|
+
return bail('no <body> element found under <html>');
|
|
121
|
+
}
|
|
122
|
+
const childrenExpr = findFirst(htmlBody, (node) => node?.type === 'JSXExpressionContainer' &&
|
|
123
|
+
node.expression?.type === 'Identifier' &&
|
|
124
|
+
node.expression?.name === 'children');
|
|
125
|
+
if (!childrenExpr) {
|
|
126
|
+
return bail('no {children} expression found inside <body>');
|
|
127
|
+
}
|
|
128
|
+
const edits = [];
|
|
129
|
+
if (!layoutFn.async) {
|
|
130
|
+
edits.push({ start: layoutFn.start, end: layoutFn.start, replacement: 'async ' });
|
|
131
|
+
}
|
|
132
|
+
edits.push({
|
|
133
|
+
start: body.start + 1,
|
|
134
|
+
end: body.start + 1,
|
|
135
|
+
replacement: '\n const config = loadFimoConfig();\n const labels = await loadFimoLabels();\n',
|
|
136
|
+
});
|
|
137
|
+
edits.push({
|
|
138
|
+
start: childrenExpr.start,
|
|
139
|
+
end: childrenExpr.end,
|
|
140
|
+
replacement: `<FimoNextProviders config={config} labels={labels}>${code.slice(childrenExpr.start, childrenExpr.end)}</FimoNextProviders>`,
|
|
141
|
+
});
|
|
142
|
+
const head = (html.children ?? []).find((child) => child?.type === 'JSXElement' && jsxName(child) === 'head');
|
|
143
|
+
if (head) {
|
|
144
|
+
const at = head.openingElement.end;
|
|
145
|
+
edits.push({ start: at, end: at, replacement: '<FimoNextScripts />' });
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
const at = html.openingElement.end;
|
|
149
|
+
edits.push({
|
|
150
|
+
start: at,
|
|
151
|
+
end: at,
|
|
152
|
+
replacement: '\n <head>\n <FimoNextScripts />\n </head>',
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
let next = code;
|
|
156
|
+
for (const edit of edits.sort((a, b) => b.start - a.start)) {
|
|
157
|
+
next = next.slice(0, edit.start) + edit.replacement + next.slice(edit.end);
|
|
158
|
+
}
|
|
159
|
+
next = ensureFimoImport(next, ['FimoNextProviders', 'FimoNextScripts'], 'fimo/next');
|
|
160
|
+
next = ensureFimoImport(next, ['loadFimoConfig', 'loadFimoLabels'], 'fimo/next/server');
|
|
161
|
+
fs.writeFileSync(layoutFile, next);
|
|
162
|
+
return { changes: [`wired Fimo providers into ${rel}`], warnings: [], wired: true };
|
|
163
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
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 { ensureNextLayoutWiring, ensureNextWiring } from './wiring.js';
|
|
6
|
+
let dir;
|
|
7
|
+
const project = (files) => {
|
|
8
|
+
dir = mkdtempSync(join(tmpdir(), 'next-wiring-'));
|
|
9
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
10
|
+
const full = join(dir, rel);
|
|
11
|
+
mkdirSync(join(full, '..'), { recursive: true });
|
|
12
|
+
writeFileSync(full, content);
|
|
13
|
+
}
|
|
14
|
+
return dir;
|
|
15
|
+
};
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
if (dir) {
|
|
18
|
+
rmSync(dir, { recursive: true, force: true });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const CREATE_NEXT_APP_LAYOUT = `import type { Metadata } from 'next';
|
|
22
|
+
import './globals.css';
|
|
23
|
+
|
|
24
|
+
export const metadata: Metadata = { title: 'Harbor Docs' };
|
|
25
|
+
|
|
26
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
27
|
+
return (
|
|
28
|
+
<html lang="en">
|
|
29
|
+
<body className="antialiased">{children}</body>
|
|
30
|
+
</html>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
describe('ensureNextLayoutWiring', () => {
|
|
35
|
+
it('wires the create-next-app default layout end to end', () => {
|
|
36
|
+
const root = project({ 'app/layout.tsx': CREATE_NEXT_APP_LAYOUT });
|
|
37
|
+
const result = ensureNextLayoutWiring(root, { appDir: 'app' });
|
|
38
|
+
const code = readFileSync(join(root, 'app/layout.tsx'), 'utf8');
|
|
39
|
+
expect(result.wired).toBe(true);
|
|
40
|
+
expect(result.changes).toEqual(['wired Fimo providers into app/layout.tsx']);
|
|
41
|
+
expect(code).toContain('import { FimoNextProviders, FimoNextScripts } from "fimo/next";');
|
|
42
|
+
expect(code).toContain('import { loadFimoConfig, loadFimoLabels } from "fimo/next/server";');
|
|
43
|
+
expect(code).toContain('export default async function RootLayout');
|
|
44
|
+
expect(code).toContain('const config = loadFimoConfig();');
|
|
45
|
+
expect(code).toContain('const labels = await loadFimoLabels();');
|
|
46
|
+
expect(code).toContain('<body className="antialiased"><FimoNextProviders config={config} labels={labels}>{children}</FimoNextProviders></body>');
|
|
47
|
+
expect(code).toContain('<head>');
|
|
48
|
+
expect(code).toContain('<FimoNextScripts />');
|
|
49
|
+
// Head lands inside <html>, before <body>.
|
|
50
|
+
expect(code.indexOf('<FimoNextScripts />')).toBeGreaterThan(code.indexOf('<html'));
|
|
51
|
+
expect(code.indexOf('<FimoNextScripts />')).toBeLessThan(code.indexOf('<body'));
|
|
52
|
+
});
|
|
53
|
+
it('reuses an existing <head> element and keeps an async layout async', () => {
|
|
54
|
+
const root = project({
|
|
55
|
+
'src/app/layout.jsx': `export default async function Layout({ children }) {
|
|
56
|
+
return (
|
|
57
|
+
<html>
|
|
58
|
+
<head>
|
|
59
|
+
<link rel="icon" href="/favicon.ico" />
|
|
60
|
+
</head>
|
|
61
|
+
<body>
|
|
62
|
+
<div id="shell">{children}</div>
|
|
63
|
+
</body>
|
|
64
|
+
</html>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
`,
|
|
68
|
+
});
|
|
69
|
+
const result = ensureNextLayoutWiring(root, { appDir: 'src/app' });
|
|
70
|
+
const code = readFileSync(join(root, 'src/app/layout.jsx'), 'utf8');
|
|
71
|
+
expect(result.wired).toBe(true);
|
|
72
|
+
expect(code).not.toContain('async async');
|
|
73
|
+
expect(code).toContain('<head><FimoNextScripts />');
|
|
74
|
+
// children was NOT a direct body child — the nested container still wraps.
|
|
75
|
+
expect(code).toContain('<FimoNextProviders config={config} labels={labels}>{children}</FimoNextProviders>');
|
|
76
|
+
expect(code.match(/<head>/g)).toHaveLength(1);
|
|
77
|
+
});
|
|
78
|
+
it('is a no-op when the layout is already wired', () => {
|
|
79
|
+
const wired = CREATE_NEXT_APP_LAYOUT.replace('{children}', '<FimoNextProviders>{children}</FimoNextProviders>');
|
|
80
|
+
const root = project({ 'app/layout.tsx': wired });
|
|
81
|
+
const result = ensureNextLayoutWiring(root, { appDir: 'app' });
|
|
82
|
+
expect(result.wired).toBe(true);
|
|
83
|
+
expect(result.changes).toEqual([]);
|
|
84
|
+
expect(readFileSync(join(root, 'app/layout.tsx'), 'utf8')).toBe(wired);
|
|
85
|
+
});
|
|
86
|
+
it('bails on pages-router-only projects', () => {
|
|
87
|
+
const root = project({ 'pages/index.tsx': 'export default function Home() { return <p>hi</p>; }\n' });
|
|
88
|
+
const result = ensureNextLayoutWiring(root, { appDir: undefined });
|
|
89
|
+
expect(result.wired).toBe(false);
|
|
90
|
+
expect(result.warnings[0]).toContain('pages-router');
|
|
91
|
+
});
|
|
92
|
+
it('bails when the default export is not a plain function', () => {
|
|
93
|
+
const source = `const Layout = ({ children }) => <html><body>{children}</body></html>;
|
|
94
|
+
export default Layout;
|
|
95
|
+
`;
|
|
96
|
+
const root = project({ 'app/layout.tsx': source });
|
|
97
|
+
const result = ensureNextLayoutWiring(root, { appDir: 'app' });
|
|
98
|
+
expect(result.wired).toBe(false);
|
|
99
|
+
expect(result.warnings[0]).toContain('not a plain function declaration');
|
|
100
|
+
expect(readFileSync(join(root, 'app/layout.tsx'), 'utf8')).toBe(source);
|
|
101
|
+
});
|
|
102
|
+
it('bails when no {children} expression exists inside <body>', () => {
|
|
103
|
+
const root = project({
|
|
104
|
+
'app/layout.tsx': `export default function Layout(props) {
|
|
105
|
+
return (
|
|
106
|
+
<html>
|
|
107
|
+
<body>{props.children}</body>
|
|
108
|
+
</html>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
`,
|
|
112
|
+
});
|
|
113
|
+
const result = ensureNextLayoutWiring(root, { appDir: 'app' });
|
|
114
|
+
expect(result.wired).toBe(false);
|
|
115
|
+
expect(result.warnings[0]).toContain('no {children} expression');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('ensureNextWiring config patch', () => {
|
|
119
|
+
it('patches the named nextConfig binding', () => {
|
|
120
|
+
const root = project({
|
|
121
|
+
'next.config.ts': `const nextConfig = {\n reactStrictMode: true,\n};\nexport default nextConfig;\n`,
|
|
122
|
+
});
|
|
123
|
+
const result = ensureNextWiring(root);
|
|
124
|
+
expect(result.changes).toEqual(['added allowedDevOrigins to next.config.ts']);
|
|
125
|
+
expect(readFileSync(join(root, 'next.config.ts'), 'utf8')).toContain("allowedDevOrigins: ['*.preview.fimo.ai'],");
|
|
126
|
+
});
|
|
127
|
+
it('patches a direct export default object literal (.mjs)', () => {
|
|
128
|
+
const root = project({
|
|
129
|
+
'next.config.mjs': `/** @type {import('next').NextConfig} */\nexport default {};\n`,
|
|
130
|
+
});
|
|
131
|
+
const result = ensureNextWiring(root);
|
|
132
|
+
expect(result.changes).toEqual(['added allowedDevOrigins to next.config.mjs']);
|
|
133
|
+
const code = readFileSync(join(root, 'next.config.mjs'), 'utf8');
|
|
134
|
+
expect(code).toContain("allowedDevOrigins: ['*.preview.fimo.ai'],");
|
|
135
|
+
expect(code).toContain('export default {');
|
|
136
|
+
});
|
|
137
|
+
it('patches module.exports form and warns on unrecognized shapes', () => {
|
|
138
|
+
const cjs = project({ 'next.config.cjs': `module.exports = {\n poweredByHeader: false,\n};\n` });
|
|
139
|
+
expect(ensureNextWiring(cjs).changes).toHaveLength(1);
|
|
140
|
+
rmSync(cjs, { recursive: true, force: true });
|
|
141
|
+
const weird = project({ 'next.config.js': `module.exports = buildConfig();\n` });
|
|
142
|
+
const result = ensureNextWiring(weird);
|
|
143
|
+
expect(result.changes).toEqual([]);
|
|
144
|
+
expect(result.warnings[0]).toContain('Could not patch next.config.js');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const titleCase = (seg) => seg
|
|
4
|
+
.replace(/[_\-]+/g, ' ')
|
|
5
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
6
|
+
.split(/\s+/)
|
|
7
|
+
.filter(Boolean)
|
|
8
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
|
9
|
+
.join(' ');
|
|
10
|
+
function pushRoute(routes, parts) {
|
|
11
|
+
const routePath = `/${parts.join('/')}`.replace(/\/+/g, '/');
|
|
12
|
+
const lastStatic = [...parts].reverse().find((part) => !part.startsWith(':'));
|
|
13
|
+
routes.push({
|
|
14
|
+
id: routePath === '/' ? 'home' : parts.map((part) => part.replace(/^:/, '')).join('-'),
|
|
15
|
+
path: routePath,
|
|
16
|
+
label: routePath === '/' ? 'Home' : titleCase(lastStatic ?? parts[parts.length - 1] ?? routePath),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const toSegment = (name) => name.replace(/^\[\.\.\.(.+)\]$/, ':$1').replace(/^\[(.+)\]$/, ':$1');
|
|
20
|
+
/** Nuxt file-based routes under pages/ (index.vue maps to the dir path). */
|
|
21
|
+
export function discoverNuxtRoutes(pagesDir) {
|
|
22
|
+
const routes = [];
|
|
23
|
+
const walk = (dir, segments) => {
|
|
24
|
+
let entries;
|
|
25
|
+
try {
|
|
26
|
+
entries = readdirSync(dir);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
const full = join(dir, entry);
|
|
33
|
+
let isDir = false;
|
|
34
|
+
try {
|
|
35
|
+
isDir = statSync(full).isDirectory();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (isDir) {
|
|
41
|
+
walk(full, [...segments, toSegment(entry)]);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (!entry.endsWith('.vue')) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const base = entry.replace(/\.vue$/, '');
|
|
48
|
+
pushRoute(routes, base === 'index' ? segments : [...segments, toSegment(base)]);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
walk(pagesDir, []);
|
|
52
|
+
routes.sort((a, b) => a.path.localeCompare(b.path));
|
|
53
|
+
return routes;
|
|
54
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TemplateWrapStyle } from '../../apply-wrap-templates.js';
|
|
2
|
+
/**
|
|
3
|
+
* Nuxt template wraps: the useFimoT composable is client-safe (fimo/nuxt
|
|
4
|
+
* never pulls server loaders into the bundle) and transfers the dictionary
|
|
5
|
+
* through the Nuxt payload, so a single `<script setup>` line wires a file.
|
|
6
|
+
* Options-API scripts have no safe injection point (top-level await needs
|
|
7
|
+
* setup) — those files stay agent work.
|
|
8
|
+
*/
|
|
9
|
+
export declare const nuxtTemplateWrapStyle: TemplateWrapStyle;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nuxt template wraps: the useFimoT composable is client-safe (fimo/nuxt
|
|
3
|
+
* never pulls server loaders into the bundle) and transfers the dictionary
|
|
4
|
+
* through the Nuxt payload, so a single `<script setup>` line wires a file.
|
|
5
|
+
* Options-API scripts have no safe injection point (top-level await needs
|
|
6
|
+
* setup) — those files stay agent work.
|
|
7
|
+
*/
|
|
8
|
+
export const nuxtTemplateWrapStyle = {
|
|
9
|
+
ext: '.vue',
|
|
10
|
+
head: "import { useFimoT } from 'fimo/nuxt'\nconst t = await useFimoT()",
|
|
11
|
+
headMarker: /useFimoT/,
|
|
12
|
+
open: '{{ t(',
|
|
13
|
+
close: ') }}',
|
|
14
|
+
injectHead: (code, head) => {
|
|
15
|
+
// An SFC may carry BOTH <script> and <script setup>: target the setup
|
|
16
|
+
// block specifically (top-level await is only legal there).
|
|
17
|
+
const setup = code.match(/<script[^>]*\bsetup\b[^>]*>\r?\n?/);
|
|
18
|
+
if (setup && setup.index !== undefined) {
|
|
19
|
+
const at = setup.index + setup[0].length;
|
|
20
|
+
return `${code.slice(0, at)}${head}\n${code.slice(at)}`;
|
|
21
|
+
}
|
|
22
|
+
if (/<script\b/.test(code)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return `<script setup>\n${head}\n</script>\n\n${code}`;
|
|
26
|
+
},
|
|
27
|
+
};
|