fimo 0.17.0 → 0.18.0
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/assets/astro/components/DateTime.astro +22 -0
- package/assets/astro/components/index.d.ts +1 -0
- package/assets/astro/components/index.js +1 -0
- package/assets/content-templates/shared/types.eta +2 -0
- package/assets/skills/fimo-cli/SKILL.md +3 -2
- package/assets/skills/fimo-cli/references/content.md +2 -2
- package/assets/skills/fimo-cli/references/deploy.md +2 -0
- package/assets/skills/fimo-cli/references/init.md +11 -4
- package/assets/skills/fimo-cli/references/locales.md +38 -14
- package/assets/skills/fimo-cli/references/pages.md +70 -0
- package/assets/skills/fimo-cli/references/webhooks.md +1 -1
- package/assets/skills/fimo-code/SKILL.md +11 -0
- package/assets/skills/fimo-code/assets/react-router-locales/LocaleLayout.tsx +12 -0
- package/assets/skills/fimo-code/assets/react-router-locales/routes.ts +7 -6
- package/assets/skills/fimo-code/references/best-practices/astro.md +47 -0
- package/assets/skills/fimo-code/references/best-practices/next.md +42 -0
- package/assets/skills/fimo-code/references/best-practices/sveltekit.md +17 -0
- package/assets/skills/fimo-code/references/content/richtext.md +5 -0
- package/assets/skills/fimo-code/references/content/schema.md +4 -1
- package/assets/skills/fimo-code/references/frameworks/astro.md +5 -3
- package/assets/skills/fimo-code/references/frameworks/hosting.md +5 -2
- package/assets/skills/fimo-code/references/frameworks/next/app-router.md +6 -0
- package/assets/skills/fimo-code/references/frameworks/next/locales.md +12 -2
- package/assets/skills/fimo-code/references/frameworks/next.md +7 -0
- package/assets/skills/fimo-code/references/frameworks/react-router.md +15 -7
- package/assets/skills/fimo-code/references/frameworks/sveltekit.md +15 -8
- package/assets/skills/fimo-code/references/labels.md +9 -1
- package/assets/skills/fimo-code/references/locales.md +8 -2
- package/assets/skills/fimo-code/references/migrations/react-router-route-ownership.md +93 -0
- package/assets/skills/fimo-code/references/ui.md +21 -2
- package/assets/skills/fimo-migration/SKILL.md +276 -51
- package/assets/skills/fimo-migration/references/content.md +119 -35
- package/assets/skills/fimo-migration/references/decision.md +72 -0
- package/assets/skills/fimo-migration/references/forms.md +38 -6
- package/assets/skills/fimo-migration/references/frameworks/nextjs.md +179 -23
- package/assets/skills/fimo-migration/references/import-contract.md +71 -19
- package/assets/skills/fimo-migration/references/manifest.md +11 -161
- package/assets/skills/fimo-migration/references/media.md +33 -1
- package/assets/skills/fimo-migration/references/platforms.md +26 -2
- package/assets/skills/fimo-migration/references/qa.md +167 -13
- package/assets/skills/fimo-migration/references/workers/content-execution.md +39 -0
- package/assets/skills/fimo-migration/references/workers/content-planner.md +64 -0
- package/assets/skills/fimo-migration/references/workers/framework.md +37 -0
- package/assets/skills/fimo-migration/references/workers/validator.md +25 -0
- package/assets/skills/fimo-migration/references/workers.md +82 -0
- package/assets/skills/fimo-migration/references/workflow.md +140 -192
- package/assets/skills/fimo-migration/scripts/apply-decision.mjs +534 -0
- package/assets/skills/fimo-migration/scripts/build-canonical-manifest.mjs +7 -4
- package/assets/skills/fimo-migration/scripts/write-migration-report.mjs +191 -4
- package/assets/skills/fimo-studio/references/cms.md +25 -7
- package/assets/skills/fimo-studio/references/webhooks.md +1 -1
- package/assets/sveltekit/components/DateTime.svelte +35 -0
- package/assets/sveltekit/components/FimoProvider.svelte +2 -2
- package/assets/sveltekit/components/index.d.ts +1 -0
- package/assets/sveltekit/components/index.js +1 -0
- package/dist/build/next/config.d.ts +4 -1
- package/dist/build/next/config.js +21 -5
- package/dist/build/sveltekit/vite.d.ts +3 -4
- package/dist/build/sveltekit/vite.js +3 -4
- package/dist/build/vite/plugins/runtime-env.d.ts +2 -1
- package/dist/build/vite/plugins/runtime-env.js +14 -10
- package/dist/cli/bundle.json +2 -2
- package/dist/cli/index.js +76 -78
- package/dist/runtime/app/FimoProviders.js +25 -14
- package/dist/runtime/app/prefetch.d.ts +3 -1
- package/dist/runtime/app/prefetch.js +11 -55
- package/dist/runtime/astro/preview.js +7 -9
- package/dist/runtime/content/client/internal.js +1 -0
- package/dist/runtime/i18n/locale.d.ts +4 -0
- package/dist/runtime/i18n/locale.js +14 -14
- package/dist/runtime/next/client-runtime.js +5 -1
- package/dist/runtime/next/client.d.ts +3 -2
- package/dist/runtime/next/client.js +2 -1
- package/dist/runtime/next/labels.d.ts +2 -2
- package/dist/runtime/next/labels.js +2 -2
- package/dist/runtime/next/preview.d.ts +13 -0
- package/dist/runtime/next/preview.js +34 -0
- package/dist/runtime/next/primitives/server.d.ts +2 -0
- package/dist/runtime/next/primitives/server.js +8 -0
- package/dist/runtime/next/provider.d.ts +5 -0
- package/dist/runtime/next/provider.js +17 -3
- package/dist/runtime/next/server-runtime.js +4 -1
- package/dist/runtime/next/server.d.ts +1 -1
- package/dist/runtime/next/server.js +1 -1
- package/dist/runtime/paths/get-fimo-paths.d.ts +6 -5
- package/dist/runtime/paths/get-fimo-paths.js +43 -42
- package/dist/runtime/preview/location.d.ts +19 -2
- package/dist/runtime/preview/location.js +16 -2
- package/dist/runtime/primitives/index.d.ts +1 -0
- package/dist/runtime/primitives/index.js +1 -0
- package/dist/runtime/react/primitives/editable/DateTime.d.ts +4 -0
- package/dist/runtime/react/primitives/editable/DateTime.js +13 -0
- package/dist/runtime/react/primitives/renderers/DateRenderer.d.ts +7 -1
- package/dist/runtime/react/primitives/renderers/DateRenderer.js +11 -8
- package/dist/runtime/react-router/{routes.d.ts → legacy/routes.d.ts} +6 -1
- package/dist/runtime/react-router/{routes.js → legacy/routes.js} +22 -7
- package/dist/runtime/routes/contract.d.ts +116 -0
- package/dist/runtime/routes/contract.js +377 -0
- package/dist/runtime/routes/index.d.ts +3 -0
- package/dist/runtime/routes/index.js +3 -0
- package/dist/runtime/routing/AppRoutesProvider.js +4 -3
- package/dist/runtime/seo/resolver.d.ts +5 -0
- package/dist/runtime/seo/resolver.js +10 -42
- package/dist/runtime/shared/fimo-config.types.d.ts +34 -8
- package/dist/runtime/shared/preview-location.d.ts +1 -22
- package/dist/runtime/shared/preview-location.js +5 -93
- package/dist/runtime/sveltekit/preview.d.ts +12 -3
- package/dist/runtime/sveltekit/preview.js +13 -8
- package/dist/scripts/sync-forms.js +1 -1
- package/dist/scripts/sync-schemas.js +1 -4
- package/dist/scripts/validate-fimo-config.js +5 -1
- package/dist/scripts/validate-page-registry.d.ts +2 -0
- package/dist/scripts/validate-page-registry.js +167 -0
- package/dist/scripts/validate-route-metadata.js +132 -41
- package/package.json +7 -3
- package/templates/astro/.fimo/config.json +13 -5
- package/templates/astro/AGENTS.md +5 -1
- package/templates/astro/astro.config.mjs +7 -0
- package/templates/astro/pnpm-workspace.yaml +0 -3
- package/templates/astro/src/components/ProjectOverview.astro +9 -6
- package/templates/astro/src/layouts/Base.astro +1 -1
- package/templates/astro/src/locales.ts +4 -0
- package/templates/astro/src/pages/[locale]/index.astro +19 -0
- package/templates/astro/src/pages/[locale]/projects/[slug].astro +35 -0
- package/templates/astro/src/pages/[locale]/projects/index.astro +19 -0
- package/templates/astro/src/pages/index.astro +5 -2
- package/templates/astro/src/pages/projects/[slug].astro +4 -2
- package/templates/astro/src/pages/projects/index.astro +5 -2
- package/templates/nextjs/.fimo/config.json +2 -1
- package/templates/nextjs/AGENTS.md +5 -0
- package/templates/nextjs/src/app/(default)/layout.tsx +10 -0
- package/templates/nextjs/src/app/[locale]/layout.tsx +22 -0
- package/templates/nextjs/src/app/[locale]/page.tsx +1 -0
- package/templates/nextjs/src/components/Document.tsx +26 -0
- package/templates/nextjs/src/components/FimoPreviewBridge.tsx +13 -0
- package/templates/nextjs/src/locales.ts +13 -0
- package/templates/react-router/.fimo/config.json +3 -6
- package/templates/react-router/AGENTS.md +4 -0
- package/templates/react-router/package.json +1 -1
- package/templates/react-router/pnpm-workspace.yaml +1 -3
- package/templates/react-router/react-router.config.ts +4 -10
- package/templates/react-router/src/pages/LocaleLayout.tsx +12 -0
- package/templates/react-router/src/root.tsx +2 -4
- package/templates/react-router/src/routes.ts +5 -3
- package/templates/sveltekit/.fimo/config.json +2 -1
- package/templates/sveltekit/AGENTS.md +5 -0
- package/templates/sveltekit/src/app.html +1 -1
- package/templates/sveltekit/src/hooks.server.ts +11 -0
- package/templates/sveltekit/src/locales.ts +8 -0
- package/templates/sveltekit/src/params/locale.ts +5 -0
- package/templates/sveltekit/src/routes/+layout.server.ts +6 -1
- package/templates/sveltekit/src/routes/+layout.svelte +5 -0
- package/assets/skills/fimo-migration/references/inventory/coordinator.md +0 -40
- package/assets/skills/fimo-migration/references/inventory/locales/next-intl.md +0 -39
- package/assets/skills/fimo-migration/references/inventory/locales.md +0 -41
- package/assets/skills/fimo-migration/scripts/inventory/locales/next-intl.mjs +0 -459
- package/templates/nextjs/src/app/layout.tsx +0 -17
- /package/templates/nextjs/src/app/{page.tsx → (default)/page.tsx} +0 -0
- /package/templates/sveltekit/src/routes/{+page.svelte → [[locale=locale]]/+page.svelte} +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getFimoSource } from 'fimo/astro';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
value: globalThis.Date | String | string | number | null | undefined;
|
|
6
|
+
locale?: string;
|
|
7
|
+
options?: Intl.DateTimeFormatOptions;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { value, locale, options, ...props } = Astro.props;
|
|
12
|
+
const rawValue = value instanceof String ? value.toString() : value;
|
|
13
|
+
const date = rawValue == null ? null : new globalThis.Date(rawValue as string | number | globalThis.Date);
|
|
14
|
+
const validDate = date && !Number.isNaN(date.getTime()) ? date : null;
|
|
15
|
+
// The whole instant, not just the day: a `datetime` field exists to name a
|
|
16
|
+
// moment, and truncating it here would throw the time away on every page.
|
|
17
|
+
const dateTime = validDate?.toISOString();
|
|
18
|
+
const content = validDate?.toLocaleString(locale, options ?? { dateStyle: 'medium', timeStyle: 'short' });
|
|
19
|
+
const source = getFimoSource(value);
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
{validDate && <time {...props} datetime={dateTime} data-fimo-source={source}>{content}</time>}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as Boolean } from './Boolean.astro';
|
|
2
2
|
export { default as Date } from './Date.astro';
|
|
3
|
+
export { default as DateTime } from './DateTime.astro';
|
|
3
4
|
export { default as Image } from './Image.astro';
|
|
4
5
|
export { default as Json } from './Json.astro';
|
|
5
6
|
export { default as RichText } from './RichText.astro';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as Boolean } from './Boolean.astro';
|
|
2
2
|
export { default as Date } from './Date.astro';
|
|
3
|
+
export { default as DateTime } from './DateTime.astro';
|
|
3
4
|
export { default as Image } from './Image.astro';
|
|
4
5
|
export { default as Json } from './Json.astro';
|
|
5
6
|
export { default as RichText } from './RichText.astro';
|
|
@@ -97,6 +97,7 @@ function getTypeForField(field) {
|
|
|
97
97
|
case 'boolean':
|
|
98
98
|
return 'FimoBoolean';
|
|
99
99
|
case 'date':
|
|
100
|
+
case 'datetime':
|
|
100
101
|
return 'string';
|
|
101
102
|
case 'json':
|
|
102
103
|
return 'Record<string, any>';
|
|
@@ -126,6 +127,7 @@ function getInputTypeForField(field) {
|
|
|
126
127
|
case 'boolean':
|
|
127
128
|
return 'boolean';
|
|
128
129
|
case 'date':
|
|
130
|
+
case 'datetime':
|
|
129
131
|
return 'string';
|
|
130
132
|
case 'json':
|
|
131
133
|
return 'Record<string, unknown>';
|
|
@@ -15,7 +15,7 @@ This skill describes **what the CLI does and the workflows it drives**. For the
|
|
|
15
15
|
|
|
16
16
|
Use `fimo <command> --help` for full flag details on any command.
|
|
17
17
|
|
|
18
|
-
>
|
|
18
|
+
> Verify a destination with `fimo webhooks test <id> --wait` and inspect its delivery history before depending on it in production.
|
|
19
19
|
|
|
20
20
|
## Running the CLI
|
|
21
21
|
|
|
@@ -75,7 +75,8 @@ Keep `fimo validate --json` at the front of the project's `build` script, which
|
|
|
75
75
|
Load the matching reference when the user's request fires its trigger. Recipes that involve writing code also need the `fimo-code` skill's package references — they're called out where relevant.
|
|
76
76
|
|
|
77
77
|
- "Create a new Fimo project / scaffold a site / start a new website / add the opt-in content starter / link an existing project locally" → `references/init.md`
|
|
78
|
-
- "Add a content type / blog / products /
|
|
78
|
+
- "Add a content type / blog / products / content schemas / CMS" → `references/content.md` (workflow) **+ run `fimo skills get fimo-code --ref content/schema` and load the detected framework reference**
|
|
79
|
+
- "Declare Pages routes / configure Pages sources or route params / control what Studio shows for a route" → `references/pages.md`
|
|
79
80
|
- "Add a contact / signup / feedback / newsletter form" → `references/forms.md` (workflow) **+ run `fimo skills get fimo-code --ref forms` for the JSON spec and generated client**
|
|
80
81
|
- "View form submissions / who filled out the contact form / inspect what users sent / how many signups" → `references/forms.md` § Reading submissions
|
|
81
82
|
- "Upload an image / hero / cover / existing media file" → `references/assets.md` (workflow: upload, browse, open, delete) **+ run `fimo skills get fimo-code --ref assets` for the `FimoMedia` shape and rendering**
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
> Package contract: load the version-locked `fimo-code` skill with `fimo skills get fimo-code`, then fetch its nested content and detected-framework references for schema JSON, entry values, generated clients, and imports.
|
|
4
4
|
|
|
5
|
-
This file covers the **CLI workflow** for content. Use it when the user wants to add a content type (blog, products,
|
|
5
|
+
This file covers the **CLI workflow** for content schemas, Singles, and entries. Use it when the user wants to add a content type (blog, products, reusable content) or manage content records. For route metadata shown in Studio Pages, load `references/pages.md`.
|
|
6
6
|
|
|
7
7
|
## Choose the write path first
|
|
8
8
|
|
|
9
9
|
- Exactly one homepage, settings document, About page, or campaign page → declare `"isSingleton": true` and write it with `fimo singles update <TypeName>`.
|
|
10
|
-
- Repeated posts, products, team members, or reusable
|
|
10
|
+
- Repeated posts, products, team members, or other reusable records → keep a collection and create rows with `fimo entries create <TypeName>`.
|
|
11
11
|
|
|
12
12
|
Never remove `isSingleton: true` merely because `fimo entries create` rejects the type. `entries` and `singles` are separate command namespaces; inspect `fimo singles --help` when the content is one-off.
|
|
13
13
|
|
|
@@ -118,6 +118,8 @@ curl -H 'Accept: text/markdown' https://your-site/ # content negotiation
|
|
|
118
118
|
curl https://your-site/?format=md # query param
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
+
This is a hosting capability: Fimo generates the `.md`, content-negotiation, and `?format=md` responses for supported built pages. Application code must not reimplement any of them — in particular, never add a route that fetches the app's own public URL to convert it to Markdown. For a dynamic page, render Markdown from the same server-side data the page itself uses when the framework supports that; otherwise report the capability as unresolved instead of building a self-fetching route.
|
|
122
|
+
|
|
121
123
|
## Recovering from Git drift
|
|
122
124
|
|
|
123
125
|
Most stale states are caught before deploy changes or commits files. Follow the emitted hint: clean a dirty checkout, or reconcile a diverged branch. A push can still lose a race if the remote advances after preflight:
|
|
@@ -72,9 +72,16 @@ fimo init my-svelte-site --framework sveltekit --org <organizationId>
|
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
Each shell includes its framework adapter, hosted-preview connector, and
|
|
75
|
-
`.fimo/config.json#runtime.framework
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
`.fimo/config.json#runtime.framework`, plus an application-owned optional locale
|
|
76
|
+
boundary that keeps the default locale unprefixed. The Astro shell also includes a minimal
|
|
77
|
+
content-backed example with static, on-demand, and dynamic routes; the other shells intentionally
|
|
78
|
+
contain no schema, labels, or designed website.
|
|
79
|
+
|
|
80
|
+
Set the primary locale from the request: when several are explicitly named, use the first as default;
|
|
81
|
+
if none is requested, keep `en` and tell the user. Update `.fimo/config.json#i18n`, the framework-native
|
|
82
|
+
locale boundary, matcher, and HTML language default. Do not keep `en` solely because the shell starts
|
|
83
|
+
there. Do not add `/en`, a language picker, global path-prefix configuration, or localized schemas unless
|
|
84
|
+
the site needs them.
|
|
78
85
|
After init, load the project rules and `fimo-code` to build the requested site.
|
|
79
86
|
If you replace a shell's code with a site that uses another framework or none,
|
|
80
87
|
fix `runtime.framework` in the same change
|
|
@@ -93,7 +100,7 @@ Pass `--project-id <id>` to set up a known Fimo project. If the project has no c
|
|
|
93
100
|
|
|
94
101
|
- The selected project shell: React Router by default, Astro with `--framework astro`, Next.js App Router with `--framework nextjs`, or SvelteKit with `--framework sveltekit`
|
|
95
102
|
- `.fimo/project.json` — links this directory to the remote project (read-only after creation)
|
|
96
|
-
- `.fimo/config.json` — project, route, locale, SEO, and agent policy
|
|
103
|
+
- `.fimo/config.json` — project, optional Studio Pages route metadata, locale, SEO, and agent policy
|
|
97
104
|
- `.fimo/agents/` — versioned agent bundles
|
|
98
105
|
- `.env` — framework-specific tenant content API URLs for local development
|
|
99
106
|
- `AGENTS.md` + `CLAUDE.md` — project rules (from the template) read by AGENTS.md-compatible tools and Claude Code. Both point the agent at `fimo skills get` to load the real skills on demand.
|
|
@@ -19,17 +19,13 @@ These commands update the selected Branch through the locale lifecycle API. The
|
|
|
19
19
|
|
|
20
20
|
If a mutation exits with code `2` and `data.code` is `LOCALE_APPLIED_SYNC_REQUIRED`, the remote operation succeeded (`data.applied` is `true`) but the local fast-forward did not. **Do not retry the locale command.** Run the commands in `data.sync.recovery`, resolve the checkout, and confirm with `fimo status` or `fimo locales list`.
|
|
21
21
|
|
|
22
|
-
Edit `.fimo/config.json` only for declarative
|
|
22
|
+
Edit `.fimo/config.json` only for declarative locale and route metadata:
|
|
23
23
|
|
|
24
24
|
```jsonc
|
|
25
25
|
{
|
|
26
26
|
"i18n": {
|
|
27
27
|
"defaultLocale": "en",
|
|
28
28
|
"locales": ["en"],
|
|
29
|
-
"routing": {
|
|
30
|
-
"strategy": "path-prefix", // only supported strategy: /es/about for Spanish; default locale can stay /about
|
|
31
|
-
"prefixDefaultLocale": false, // when false, omit /en for the default locale
|
|
32
|
-
},
|
|
33
29
|
},
|
|
34
30
|
}
|
|
35
31
|
```
|
|
@@ -37,7 +33,7 @@ Edit `.fimo/config.json` only for declarative routing behavior:
|
|
|
37
33
|
- `defaultLocale` is the locale used by `fimo validate`, `fimo labels set` without `--locale`, and content commands without `--locale`. Change it with `fimo locales set-default`.
|
|
38
34
|
- `locales` must include `defaultLocale`. Add and remove values with `fimo locales add` and `fimo locales remove`.
|
|
39
35
|
- Locale tags should be structurally valid, for example `en`, `es`, `pt-BR`, or `zh-Hans`.
|
|
40
|
-
- `routing.strategy`
|
|
36
|
+
- New projects do not need a global routing strategy. The route contract declares locale placement with `/:locale`; legacy `i18n.routing.strategy` values are read only for compatibility while migrating.
|
|
41
37
|
- If multiple locales are configured, create or update labels and localized content for every configured locale. Create schemas with `i18n: { "enabled": false }` only once.
|
|
42
38
|
|
|
43
39
|
`fimo locales remove` permanently deletes that locale's stored entries and labels.
|
|
@@ -55,38 +51,66 @@ Validation checks:
|
|
|
55
51
|
- malformed locale tags
|
|
56
52
|
- `defaultLocale` missing from `locales`
|
|
57
53
|
- unsupported routing strategies
|
|
58
|
-
-
|
|
59
|
-
-
|
|
54
|
+
- routes in `.fimo/config.json` that hardcode a locale prefix, for example `/es/pricing`
|
|
55
|
+
- two routes that answer the same URL once the locale segment is removed
|
|
56
|
+
- a `:locale` segment that is not first, or that declares `schema`, `queryField`, `displayField`
|
|
57
|
+
- an entry param declared `optional`, or missing `schema` / `queryField`
|
|
58
|
+
- top-level dynamic routes other than `:locale` that could capture locale prefixes
|
|
60
59
|
- missing default-locale DB labels
|
|
61
60
|
|
|
62
61
|
## Route Metadata Rule
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
Write the locale into a route's path with the reserved `:locale` segment, never as a literal prefix. The descriptor `type` is optional: `locale` infers `"locale"`, while every other parameter defaults to `"entry"`:
|
|
65
64
|
|
|
66
65
|
```json
|
|
67
66
|
{
|
|
68
67
|
"routes": {
|
|
68
|
+
"blog-post": {
|
|
69
|
+
"label": "Blog post",
|
|
70
|
+
"path": "/:locale/blog/:slug",
|
|
71
|
+
"params": {
|
|
72
|
+
"locale": { "type": "locale", "optional": true },
|
|
73
|
+
"slug": { "schema": "BlogPost", "queryField": "slug", "displayField": "title" }
|
|
74
|
+
}
|
|
75
|
+
},
|
|
69
76
|
"pricing": {
|
|
70
77
|
"label": "Pricing",
|
|
71
|
-
"path": "/pricing"
|
|
78
|
+
"path": "/:locale/pricing",
|
|
79
|
+
"params": { "locale": { "type": "locale", "optional": true } }
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
}
|
|
75
83
|
```
|
|
76
84
|
|
|
77
|
-
|
|
85
|
+
| Declared path | `params.locale` | URLs the route answers |
|
|
86
|
+
| --------------------- | ----------------------------------------- | --------------------------------------------------- |
|
|
87
|
+
| `/:locale/blog/:slug` | omitted | `/blog/hello` for `en`, `/fr/blog/bonjour` for `fr` |
|
|
88
|
+
| `/:locale/blog/:slug` | `{ "type": "locale", "optional": false }` | `/en/blog/hello` and `/fr/blog/bonjour` |
|
|
89
|
+
| `/:locale/pricing` | omitted | `/pricing` for `en`, `/fr/pricing` for `fr` |
|
|
90
|
+
|
|
91
|
+
- `locale` is reserved. It is read from the URL, so it never takes `schema` or `queryField` and never becomes a CMS query. Every other param is content-backed and needs both.
|
|
92
|
+
- `optional: true` means omit the configured default locale from the URL. With `i18n.defaultLocale: "en"`, `/pricing` is English and `/fr/pricing` is French.
|
|
93
|
+
- `optional: false` prefixes every locale, including the default (`/en/pricing` and `/fr/pricing`). Changing `i18n.defaultLocale` chooses the fallback locale; it does not by itself change this prefix policy.
|
|
94
|
+
- A route with no `:locale` segment stays application-owned. Studio shows it once and neither adds nor interprets a locale segment.
|
|
95
|
+
- Do not add `/es/pricing` as a second route. A locale segment does not make two routes distinct.
|
|
96
|
+
|
|
97
|
+
Register the same shape in the router. React Router spells the omittable segment `:locale?`:
|
|
78
98
|
|
|
79
99
|
```ts
|
|
80
|
-
|
|
100
|
+
route(':locale?/blog/:slug', './pages/BlogPost.tsx');
|
|
81
101
|
```
|
|
82
102
|
|
|
83
|
-
|
|
103
|
+
These canonical entries are also the optional metadata used by Studio Pages. See `references/pages.md` for content-backed `params` and declared `sources`.
|
|
104
|
+
|
|
105
|
+
For every framework, the application owns its URL scheme, resolves the locale, and passes it into Fimo. Studio stores the locale the app reports for the current location — including when only the locale changed — and reads content in it. Route metadata is optional unless the project wants Fimo Studio route selection/navigation.
|
|
106
|
+
|
|
107
|
+
Astro and Next express the same optional-locale semantic with paired native filesystem route trees: an unprefixed default-locale tree and a prefixed non-default tree. Neither framework supports an optional single-segment filesystem folder.
|
|
84
108
|
|
|
85
109
|
## Migrate An Existing Locale System
|
|
86
110
|
|
|
87
111
|
Inspect the existing locale provider, route table, switcher, redirects, and public URLs before editing configuration. Preserve working URL behavior during the migration.
|
|
88
112
|
|
|
89
|
-
Preserve the existing locale provider, router, switcher, redirects, and public URLs.
|
|
113
|
+
Preserve the existing locale provider, router, switcher, redirects, and public URLs. Next.js projects run `fimo skills get fimo-code --ref frameworks/next/locales`; React Router projects load the React Router setup reference and its focused route-ownership migration when upgrading; Astro projects run `fimo skills get fimo-code --ref frameworks/astro` and pass the page-resolved locale into server reads and `getLabels`.
|
|
90
114
|
|
|
91
115
|
## Labels And Content
|
|
92
116
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Pages route metadata
|
|
2
|
+
|
|
3
|
+
Pages can mirror the application's routes in Fimo Studio. It gives editors a route tree and, for each selected route, a page-shaped workspace for its Labels, Single, bound entries, and Collection results. The CLI writes this optional metadata to `.fimo/config.json` under `routes.<id>`; it does not replace the application router or move or duplicate content.
|
|
4
|
+
|
|
5
|
+
## Route shape
|
|
6
|
+
|
|
7
|
+
A route has three separate parts:
|
|
8
|
+
|
|
9
|
+
- `path` is the URL pattern, such as `/blog/:slug`.
|
|
10
|
+
- `params` optionally binds path placeholders to Collection entries so Studio can select a concrete route in the page header.
|
|
11
|
+
- `sources` optionally names the Labels, Single, and Collection results editors manage after selecting a route.
|
|
12
|
+
|
|
13
|
+
Each param key matches a `:placeholder` in `path`. `schema` selects the Collection, `queryField` is the entry field stored in the URL, and `displayField` is the field Studio shows to editors. Use `id` or the built-in `slug` for `queryField`; a custom URL field must set both `"required": true` and `"unique": true` in its schema. Params resolve in path order, so a later param query may use an earlier selection:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
fimo pages set <id> --path '/shop/:category/:product' --params '{"category":{"schema":"Category","queryField":"slug","displayField":"name"},"product":{"schema":"Product","queryField":"slug","displayField":"name","query":{"where":{"category":"$params.category.id"},"sort":"name"}}}'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`sources` is a named object under `routes.<id>.sources`. It is not an array or a route definition. Keep its keys stable and descriptive:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"copy": {
|
|
24
|
+
"type": "labels",
|
|
25
|
+
"match": ["blog.*"]
|
|
26
|
+
},
|
|
27
|
+
"settings": {
|
|
28
|
+
"type": "single",
|
|
29
|
+
"schema": "BlogSettings"
|
|
30
|
+
},
|
|
31
|
+
"relatedPosts": {
|
|
32
|
+
"type": "collection",
|
|
33
|
+
"schema": "BlogPost",
|
|
34
|
+
"query": {
|
|
35
|
+
"where": {
|
|
36
|
+
"category": "$params.slug.category"
|
|
37
|
+
},
|
|
38
|
+
"sort": "-publishedAt",
|
|
39
|
+
"limit": 4
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Pass that same object as JSON:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
fimo pages set <id> --sources '{"copy":{"type":"labels","match":["blog.*"]},"settings":{"type":"single","schema":"BlogSettings"},"relatedPosts":{"type":"collection","schema":"BlogPost","query":{"where":{"category":"$params.slug.category"},"sort":"-publishedAt","limit":4}}}'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The three source types select matching Labels, one-off Singles, or Collection results. A Collection source's optional `query` uses the normal content query shape.
|
|
52
|
+
|
|
53
|
+
## Params queries and source queries
|
|
54
|
+
|
|
55
|
+
Both use the normal content query shape and may read an earlier bound value with `$params.<param>.<field>`, but they run at different stages and narrow different things:
|
|
56
|
+
|
|
57
|
+
- A dependent param query runs while Studio resolves the header selectors. On `/shop/:category/:product`, the product query narrows which products can become the `:product` URL segment after a category is chosen.
|
|
58
|
+
- A Collection source query runs after Studio has resolved and selected the route. On `/blog/:slug`, `relatedPosts` uses the selected post's `$params.slug.category` to narrow the entries shown in that route's editor; it does not create more routes.
|
|
59
|
+
|
|
60
|
+
## Commands
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
fimo pages list
|
|
64
|
+
fimo pages list --expand
|
|
65
|
+
fimo pages set <id> --path /about --label About
|
|
66
|
+
fimo pages set <id> --clear-params
|
|
67
|
+
fimo pages set <id> --clear-sources
|
|
68
|
+
fimo pages rename <id> <new-id>
|
|
69
|
+
fimo pages delete <id>
|
|
70
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Outbound webhooks
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Test each destination and inspect its delivery history before depending on it in production.
|
|
4
4
|
|
|
5
5
|
Use `fimo webhooks` to send project events to external HTTPS endpoints. The CLI and Studio edit the same project-wide configurations and delivery history. The CLI has no provider presets: inspect the destination's API documentation, then pass its URL, headers, and JSON body explicitly.
|
|
6
6
|
|
|
@@ -14,6 +14,12 @@ Use this skill for code that consumes the version-locked `fimo` package in the c
|
|
|
14
14
|
|
|
15
15
|
Use the project-installed CLI for local inspection, code generation, and validation (`pnpm exec fimo`, `npm exec fimo`, `yarn fimo`, or `bunx --no-install fimo`, matching the lockfile). If that runner tries to install packages even though dependencies already exist, invoke the local `node_modules/.bin/fimo` executable instead. Load the `fimo-cli` skill for remote operational workflows such as schema pushes, label writes, entry mutations, and deploys. If the skill is not already available locally, fetch it with `npx fimo@latest skills get fimo-cli`.
|
|
16
16
|
|
|
17
|
+
Fimo API values may be class-backed tracked objects, not plain JSON. Keep them
|
|
18
|
+
as-is on the server. Before passing them to a `'use client'` prop or a
|
|
19
|
+
plain-JSON consumer such as Markdown, RSS, or JSON-LD, use the generated
|
|
20
|
+
serializer and hydrate them in the client when the generated client requires
|
|
21
|
+
it. Load the framework reference for the exact boundary.
|
|
22
|
+
|
|
17
23
|
## Orient before writing
|
|
18
24
|
|
|
19
25
|
1. Run the project-installed `fimo describe --json` in a linked project and read `data.framework.slug`, schemas, locales, and tree. If the project is not linked yet, inspect `package.json` and the app directory instead.
|
|
@@ -35,6 +41,10 @@ Framework routing:
|
|
|
35
41
|
- Nuxt, Remix, Vue, or another hosting-only setup → `references/frameworks/hosting.md`. Do not invent an SDK adapter that the installed package does not export.
|
|
36
42
|
- No framework (static generator, plain HTML, a build script) → `references/frameworks/hosting.md` § `runtime`: leave `runtime.framework` unset.
|
|
37
43
|
|
|
44
|
+
For public-site performance or navigation work, also load the matching
|
|
45
|
+
`references/best-practices/astro.md`, `references/best-practices/sveltekit.md`,
|
|
46
|
+
or `references/best-practices/next.md` reference.
|
|
47
|
+
|
|
38
48
|
When a project's framework version is uncertain, load
|
|
39
49
|
`references/frameworks/compatibility.md`. Use the project's installed version
|
|
40
50
|
and the documented adapter range; do not assume that a framework profile means
|
|
@@ -60,6 +70,7 @@ Each reference defines one package contract. Fetch it when the task reaches that
|
|
|
60
70
|
- Media values, `<Image>`, `<StaticImage>` → `references/assets.md`
|
|
61
71
|
- Static editable copy: `Label`, `t()`, providers, and server label readers → `references/labels.md`
|
|
62
72
|
- Locale ownership and framework handoff → `references/locales.md`
|
|
73
|
+
- Upgrading a React Router app that imports `fimo/react-router/routes` → `references/migrations/react-router-route-ownership.md`
|
|
63
74
|
- Astro connector and output/adapter contract → `references/frameworks/astro.md`
|
|
64
75
|
- Framework detection, production routing, and hosting adapters → `references/frameworks/hosting.md`
|
|
65
76
|
- Major visual work → `references/design.md`
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FimoProviders } from 'fimo/react-router';
|
|
2
|
+
import { Outlet, useParams } from 'react-router';
|
|
3
|
+
|
|
4
|
+
export default function LocaleLayout() {
|
|
5
|
+
const { locale } = useParams();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<FimoProviders locale={locale}>
|
|
9
|
+
<Outlet />
|
|
10
|
+
</FimoProviders>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { RouteConfig } from '@react-router/dev/routes';
|
|
2
|
-
import {
|
|
3
|
-
import { fimoRoutes } from 'fimo/react-router/routes';
|
|
2
|
+
import { layout, route } from '@react-router/dev/routes';
|
|
4
3
|
|
|
5
4
|
const routes = [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
layout('./pages/LocaleLayout.tsx', [
|
|
6
|
+
route(':locale?', './pages/Index.tsx', { id: 'home' }),
|
|
7
|
+
route(':locale?/pricing', './pages/Pricing.tsx', { id: 'pricing' }),
|
|
8
|
+
route(':locale?/blog/:slug', './pages/BlogPost.tsx', { id: 'blog-post' }),
|
|
9
|
+
]),
|
|
9
10
|
] satisfies RouteConfig;
|
|
10
11
|
|
|
11
|
-
export default
|
|
12
|
+
export default routes;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Astro website best practices
|
|
2
|
+
|
|
3
|
+
Load `references/frameworks/astro.md` first.
|
|
4
|
+
|
|
5
|
+
- Put the document shell, navigation, and shared metadata in a layout.
|
|
6
|
+
- Render the H1, primary copy, canonical, and crawlable links in the initial HTML.
|
|
7
|
+
- Pass tracked Fimo values intact to Fimo components; use `String(value)` only for attributes and URLs. Flattening removes Studio's edit target.
|
|
8
|
+
- Start independent Fimo reads with `Promise.all`; do not repeat page reads in children.
|
|
9
|
+
- Ship no client JavaScript for static content. Use `client:visible` or `client:idle` below the fold; reserve `client:load` for immediately required interaction.
|
|
10
|
+
- Use `server:defer` only for slow, secondary, or personalized UI, with a stable fallback. Keep primary content and metadata outside server islands.
|
|
11
|
+
- Prerender when rebuild freshness is acceptable; render on demand when edits must appear immediately.
|
|
12
|
+
- Give images dimensions; eagerly load only the likely LCP image.
|
|
13
|
+
- Return a real 404 for a missing slug. Build one canonical from the public origin and normalized route, never the preview host.
|
|
14
|
+
|
|
15
|
+
## Consider incremental static builds
|
|
16
|
+
|
|
17
|
+
For Astro 7.2+ sites with many `getStaticPaths()` pages:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
import { defineConfig } from 'astro/config';
|
|
21
|
+
import { fimo } from 'fimo/astro/config';
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
integrations: [fimo()],
|
|
25
|
+
experimental: { incrementalBuild: true },
|
|
26
|
+
build: { concurrency: 1 },
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```astro
|
|
31
|
+
export async function getStaticPaths() {
|
|
32
|
+
const { items } = await Story.list({ limit: 100 });
|
|
33
|
+
return items.map((story) => ({
|
|
34
|
+
params: { slug: String(story.slug) },
|
|
35
|
+
props: { story },
|
|
36
|
+
cacheKey: story.updatedAt,
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- The `cacheKey` must change for every rendered data dependency, including shared labels, settings, and related entries. A slug is not an invalidation key.
|
|
42
|
+
- Persist `node_modules/.astro/` between builds. Use `astro build --force` after middleware changes or to ignore the cache.
|
|
43
|
+
- Keep build concurrency at `1`. Pages with server islands also need a stable `ASTRO_KEY`.
|
|
44
|
+
|
|
45
|
+
Verify production HTML, valid and missing slugs, canonical output, shipped scripts, API waterfalls, and image sizing. For incremental builds, prove one unchanged and one invalidated path.
|
|
46
|
+
|
|
47
|
+
Sources: [incremental builds](https://docs.astro.build/en/reference/experimental-flags/incremental-build/) and [server islands](https://docs.astro.build/en/guides/server-islands/).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Next.js website best practices
|
|
2
|
+
|
|
3
|
+
Load `references/frameworks/next.md` and the matching router reference first. Instant Navigations apply only to Next 16.3+ App Router projects.
|
|
4
|
+
|
|
5
|
+
- Use Server Components by default and keep Client Components at interactive leaves.
|
|
6
|
+
- Keep root layouts cheap; put slow page-specific Fimo reads in the page or a nested component.
|
|
7
|
+
- Start independent reads together. Server Components should call generated Fimo modules directly, not application Route Handlers.
|
|
8
|
+
- Keep tracked Fimo values intact in Server Components. Use generated `serialize()`/`hydrate()` at JSON prop boundaries. Generated client hooks are already hydrated; pass their values directly to Fimo components.
|
|
9
|
+
- Render the H1, primary copy, canonical, and crawlable links in the server result. Do not repeat the read after hydration.
|
|
10
|
+
- Give images dimensions; prioritize only the likely LCP image.
|
|
11
|
+
|
|
12
|
+
## Instant Navigations
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import type { NextConfig } from 'next';
|
|
16
|
+
import { withFimo } from 'fimo/next/config';
|
|
17
|
+
|
|
18
|
+
const nextConfig: NextConfig = {
|
|
19
|
+
cacheComponents: true,
|
|
20
|
+
partialPrefetching: true,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default withFimo(nextConfig);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Choose per blocking Fimo read:
|
|
27
|
+
|
|
28
|
+
- **Stream:** use `Suspense` for fresh or slow data. Prefer this when Studio edits must appear immediately.
|
|
29
|
+
- **Cache:** use `use cache` only with a publication-aware lifetime and tag invalidation.
|
|
30
|
+
- **Block:** set `instant = false` as low as possible when partial UI is misleading.
|
|
31
|
+
|
|
32
|
+
Keep default `<Link>` prefetching. Use `prefetch={true}` only for a few high-intent links; large lists can overload the content API. Prefetch rendering must be side-effect free.
|
|
33
|
+
|
|
34
|
+
Use `instant = true` to find blocking code, then verify with `next build` and `next start`; development does not prefetch. Inspect the shell and streams with Navigation Inspector.
|
|
35
|
+
|
|
36
|
+
- Call `notFound()` for a missing slug.
|
|
37
|
+
- Build one canonical from `metadataBase` and the normalized route, never the preview host.
|
|
38
|
+
- Avoid duplicate content reads between `generateMetadata` and the page.
|
|
39
|
+
|
|
40
|
+
Verify valid and missing slugs, canonical and initial HTML, shipped JavaScript, duplicate reads, image sizing, instant shell rendering, streaming, freshness, and bounded prefetching.
|
|
41
|
+
|
|
42
|
+
Sources: [Instant Navigations](https://nextjs.org/blog/next-16-3-instant-navigations), [`partialPrefetching`](https://github.com/vercel/next.js/blob/canary/docs/01-app/03-api-reference/05-config/01-next-config-js/partialPrefetching.mdx), and [`instant`](https://github.com/vercel/next.js/blob/canary/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/instant.mdx).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# SvelteKit website best practices
|
|
2
|
+
|
|
3
|
+
Load `references/frameworks/sveltekit.md` first.
|
|
4
|
+
|
|
5
|
+
- Keep shared navigation, labels, and metadata in layouts; keep slow page data in the narrowest `+page.server.ts` or `+layout.server.ts`.
|
|
6
|
+
- Pass the request `event`. Serialize generated Fimo values at the load boundary and render them with Fimo components, which restore their Studio editing metadata. Do not flatten tracked values first.
|
|
7
|
+
- Start independent reads together. Use `await parent()` only for a real parent-data dependency.
|
|
8
|
+
- Await primary content. Stream only slow, non-essential promises; add a stable `{#await}` fallback, an error branch, and an early rejection handler when the promise does not use SvelteKit's `fetch`.
|
|
9
|
+
- Render the H1, primary copy, canonical, and crawlable links during SSR. Do not disable SSR or repeat the read after hydration.
|
|
10
|
+
- Keep browser JavaScript at interactive leaves.
|
|
11
|
+
- Use request-time loads when edits must appear immediately. Prerender or cache only when rebuild or invalidation behavior is explicit.
|
|
12
|
+
- Give images dimensions; eagerly load only the likely LCP image.
|
|
13
|
+
- Call `error(404, ...)` for a missing slug. Build one canonical from the configured public origin and normalized route, never the preview host.
|
|
14
|
+
|
|
15
|
+
Verify the production server response, valid and missing slugs, canonical output, streamed errors, shipped JavaScript, API waterfalls, and image sizing.
|
|
16
|
+
|
|
17
|
+
Source: [SvelteKit load and streaming](https://svelte.dev/docs/kit/load).
|
|
@@ -26,6 +26,11 @@ Marks: `bold`, `italic`, `underline`, `strike`, `code`, and `link`.
|
|
|
26
26
|
|
|
27
27
|
Use semantic nodes instead of flattening long content into paragraphs. Render the returned value with the framework's `<RichText>` primitive so source metadata survives.
|
|
28
28
|
|
|
29
|
+
Richtext and other API values may carry class-backed tracking metadata. Values
|
|
30
|
+
rendered by server Fimo primitives can stay intact. Serialize them before a
|
|
31
|
+
plain-JSON consumer (Markdown, RSS, JSON-LD) or a client prop, then use the
|
|
32
|
+
generated client hydration helper where required.
|
|
33
|
+
|
|
29
34
|
## Entry payload
|
|
30
35
|
|
|
31
36
|
CLI entry writes put every value, including the collection slug, under `data`:
|
|
@@ -56,12 +56,15 @@ Never declare `id`, `entryId`, `documentId`, `slug`, `locale`, `createdAt`, `upd
|
|
|
56
56
|
| `richtext` | Tiptap JSONContent | formatted content |
|
|
57
57
|
| `number` | number | quantities and ordering fields |
|
|
58
58
|
| `boolean` | boolean | flags |
|
|
59
|
-
| `date` | ISO date string |
|
|
59
|
+
| `date` | ISO date string | calendar days; loose about the form |
|
|
60
|
+
| `datetime` | ISO instant with offset | exact moments (`2026-08-13T09:30:00Z`) |
|
|
60
61
|
| `media` | media object or array | images, video, files |
|
|
61
62
|
| `json` | JSON | structured metadata |
|
|
62
63
|
| `reference` | `{ "id": "..." }` or array | links to another schema |
|
|
63
64
|
| `blocks` | ordered object array | nested/repeatable sections |
|
|
64
65
|
|
|
66
|
+
A `datetime` value must carry `Z` or `±HH:MM`; a bare day or a naive local time is rejected. Render it with `<DateTime>`, which keeps the whole instant in the `<time datetime>` attribute, and a `date` with `<Date>`. `unique` is not supported on `datetime`.
|
|
67
|
+
|
|
65
68
|
An enum string uses `options`; its optional `default` must be one of them:
|
|
66
69
|
|
|
67
70
|
```json
|
|
@@ -73,7 +73,7 @@ const [settings, stories, labels] = await Promise.all([
|
|
|
73
73
|
}
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
The native surface is `Text`, `RichText`, `Image`, `StaticImage`, `Video`, `Date`, `Boolean`, and `Json` (component table: `references/ui.md`). Pass tracked values whole so Studio can resolve their source; use `String(value)` only for attributes and URLs.
|
|
76
|
+
The native surface is `Text`, `RichText`, `Image`, `StaticImage`, `Video`, `Date`, `DateTime`, `Boolean`, and `Json` (component table: `references/ui.md`). Pass tracked values whole so Studio can resolve their source; use `String(value)` only for attributes and URLs.
|
|
77
77
|
|
|
78
78
|
## Forms
|
|
79
79
|
|
|
@@ -107,11 +107,13 @@ export async function getStaticPaths() {
|
|
|
107
107
|
|
|
108
108
|
## Locales
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
Configure Astro i18n from `.fimo/config.json#i18n` and use `Astro.currentLocale` for content reads, `getLabels({ locale })`, and `<html lang>`. The standard starter keeps the configured default locale unprefixed and prefixes the others. Existing applications may instead resolve locale through middleware, domains, cookies, or another application-owned strategy. Astro needs no Fimo locale provider. Ownership rules: `references/locales.md`.
|
|
111
|
+
|
|
112
|
+
The new-project shell pairs unprefixed default-locale pages with a parallel `[locale]` tree because Astro has no optional single-segment page filename. Add locales in `.fimo/config.json`; Astro's native i18n router validates the prefixed tree. Route metadata uses `/:locale` with `params.locale.optional: true`; Astro page files remain authoritative.
|
|
111
113
|
|
|
112
114
|
## Studio route catalog
|
|
113
115
|
|
|
114
|
-
`.fimo/config.json#routes` is an optional catalog of pages for Studio navigation, not router configuration. Never replace or infer Astro's route files, redirects, middleware, or 404 behavior from it. Keep
|
|
116
|
+
`.fimo/config.json#routes` is an optional catalog of pages for Studio navigation, not router configuration. Never replace or infer Astro's route files, redirects, middleware, or 404 behavior from it. Keep routes without locale ownership canonical and unlocalized. For a locale-aware route, declare the reserved `/:locale` segment and mark it optional in `params`; describe other dynamic segments with `params` so Studio can list entries:
|
|
115
117
|
|
|
116
118
|
```json
|
|
117
119
|
{
|
|
@@ -49,9 +49,12 @@ Locally, the app derives its env from the **git branch**: `fimo/vite`,
|
|
|
49
49
|
API URL from `.fimo/project.json`
|
|
50
50
|
(`projectId` + `tenantApiBaseUrl`, recorded once at `init`/`clone`/`link`) and
|
|
51
51
|
expose the Vite, Next and public (`PUBLIC_*`) aliases plus `FIMO_ENV` /
|
|
52
|
-
`NEXT_PUBLIC_FIMO_ENV` at startup.
|
|
52
|
+
`NEXT_PUBLIC_FIMO_ENV` at startup. The Vite connectors derive them during both
|
|
53
|
+
dev and production builds, and `withFimo` derives them in `next dev`, `next
|
|
54
|
+
build`, and `next start`, so a linked project builds and serves locally without
|
|
55
|
+
exporting them by hand. No env file is written on a switch, so a plain `git checkout` is a
|
|
53
56
|
complete env switch. Never hardcode a tenant URL in application source. One
|
|
54
|
-
rule, per variable: a value the user set explicitly (`.env`, shell, their own
|
|
57
|
+
rule, per variable: a non-empty value the user set explicitly (`.env`, shell, their own
|
|
55
58
|
`define`) is left alone, and Fimo fills the variables they did not set.
|
|
56
59
|
|
|
57
60
|
For a framework whose alias is not in that list yet, preserve or add its normal
|
|
@@ -76,6 +76,12 @@ This example demonstrates request-time rendering, not a recommended locale polic
|
|
|
76
76
|
|
|
77
77
|
## Client Components
|
|
78
78
|
|
|
79
|
+
When a Server Component passes Fimo content into a Client Component, it MUST
|
|
80
|
+
serialize with the generated server module and the Client Component MUST
|
|
81
|
+
hydrate with the matching generated `.client` module. Do not pass a raw Fimo
|
|
82
|
+
value merely because its enumerable fields look JSON-safe. No conversion is
|
|
83
|
+
needed while the value stays inside server rendering.
|
|
84
|
+
|
|
79
85
|
Client Components use the generated `.client` module and `fimo/next/client`:
|
|
80
86
|
|
|
81
87
|
```tsx
|
|
@@ -33,6 +33,16 @@ When labels are not needed at that boundary, use `<FimoProvider locale={locale}>
|
|
|
33
33
|
- Pages Router i18n: reuse `context.locale` in data functions and `router.locale`/page props on the client.
|
|
34
34
|
- Default-only app: omit locale arguments and let Fimo use `.fimo/config.json#i18n.defaultLocale` (fallback `en`).
|
|
35
35
|
|
|
36
|
+
## New-project boundary
|
|
37
|
+
|
|
38
|
+
Choose the primary locale from the user's request before building pages. Put the first explicitly requested locale in `.fimo/config.json#i18n.defaultLocale` and keep all requested locales in `i18n.locales`. That edit is the whole locale setup: `src/locales.ts` reads the file, so the default locale passed to `FimoProvider`, the `<html lang>` of each tree, and the set of prefixed URLs follow it. If the user gives no locale signal, keep `en` and state that choice. Never add `NEXT_PUBLIC_FIMO_DEFAULT_LOCALE` or `FIMO_DEFAULT_LOCALE`; the runtime ignores them by design.
|
|
39
|
+
|
|
40
|
+
The starter keeps default routes in an unprefixed `app/(default)` route group and a native `app/[locale]` tree for the other locales. Only a root layout may render `<html>`, so each tree owns its root layout and renders the shared `src/components/Document.tsx` with its locale: the unprefixed tree with `defaultLocale`, the prefixed tree with `params.locale`. The prefixed layout 404s a locale that is not in `prefixedLocales`, which is why the default locale has exactly one URL. For `/:locale/pricing`, `params.locale.optional: true` means omit the configured default locale: with `i18n.defaultLocale: "en"`, `/pricing` is English and `/fr/pricing` is French. With `optional: false`, both `/en/pricing` and `/fr/pricing` are prefixed. The default locale value chooses the fallback locale; `optional` independently controls URL prefixes. A route without `:locale` remains application-owned.
|
|
41
|
+
|
|
42
|
+
Next.js has no optional single-segment route folder, so mirror localized routes in both existing groups as the site grows. Astro uses the same paired native filesystem-tree shape for this semantic. Do not replace the trees with middleware, rewrites, an optional catch-all that captures the application route tree, or a Fimo route helper. The config route catalog describes the URL shapes for Studio; Next's `app` directory remains authoritative.
|
|
43
|
+
|
|
44
|
+
Keep the starter's client-only `src/components/FimoPreviewBridge.tsx` inside `Document`, so both trees mount it. It reads Next's native pathname/router, reports the settled locale to Studio, and lets Studio navigate through Next; do not move Next imports into Fimo's shared runtime.
|
|
45
|
+
|
|
36
46
|
With `next-i18next`, keep its routing and translation provider for the strings it owns. Pass the same resolved locale to Fimo; do not replace the library or duplicate its route structure.
|
|
37
47
|
|
|
38
48
|
Before creating non-default Fimo labels or content variants, confirm that those locales are enabled for the linked Fimo project. Load `fimo-cli` with its `locales` reference and run `fimo locales add <locale> --yes --json`; `.fimo/config.json` documents the application's locale policy, but `fimo validate` does not add project locales.
|
|
@@ -54,6 +64,6 @@ Fimo owns:
|
|
|
54
64
|
- client content/label context after receiving the locale;
|
|
55
65
|
- editable source metadata.
|
|
56
66
|
|
|
57
|
-
|
|
67
|
+
The new-project shell includes a small `.fimo/config.json#routes` catalog for Studio. It describes the page URLs but does not configure the App Router. Keep the `app` directory authoritative, and do not create a fake `src/routes.ts` or a universal Fimo route helper.
|
|
58
68
|
|
|
59
|
-
Do not introduce
|
|
69
|
+
Do not introduce new locale middleware, cookie routing, or domain routing unless the product request requires that URL strategy. Do not add a second locale provider when the existing one can supply the resolved locale to Fimo.
|