fimo 0.16.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 +5 -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/package.json +1 -1
- 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/package.json +1 -1
- 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 +2 -2
- 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/package.json +1 -1
- 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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// dynamic routes are expanded by fetching schema entries from the content API.
|
|
4
4
|
import { existsSync, readFileSync } from 'node:fs';
|
|
5
5
|
import { resolve } from 'node:path';
|
|
6
|
-
import {
|
|
6
|
+
import { materializeRoutePath, resolveRouteContracts, routeLocaleTargets } from '../routes/contract.js';
|
|
7
7
|
import { resolveSeoForPath } from '../seo/resolver.js';
|
|
8
8
|
import { getConfigServer } from '../shared/fimo-config.server.js';
|
|
9
9
|
import { resolveLocalRuntimeEnv } from '../shared/local-runtime-env.js';
|
|
@@ -82,11 +82,12 @@ function matchesAny(routeKey, patterns) {
|
|
|
82
82
|
* robots.txt, or any per-route logic.
|
|
83
83
|
*
|
|
84
84
|
* - Static paths in config: emitted as-is.
|
|
85
|
-
* - Dynamic paths (with `:slug`-style params): expanded by fetching
|
|
86
|
-
* entries from `VITE_API_URL`. One returned entry per
|
|
87
|
-
* -
|
|
88
|
-
*
|
|
89
|
-
*
|
|
85
|
+
* - Dynamic paths (with `:slug`-style entry params): expanded by fetching
|
|
86
|
+
* schema entries from `VITE_API_URL`. One returned entry per expansion.
|
|
87
|
+
* - Locales come from the route contract, not from the project locale list: a
|
|
88
|
+
* route is emitted once per locale only when its path declares a `:locale`
|
|
89
|
+
* segment. The default locale is always unprefixed. A route without a
|
|
90
|
+
* `:locale` segment yields exactly one default-locale path.
|
|
90
91
|
* - Pure-string return shape (`p.path`) is suitable for RR's `prerender`.
|
|
91
92
|
*
|
|
92
93
|
* If no API URL is set, dynamic routes are skipped with a console warning.
|
|
@@ -110,14 +111,20 @@ function matchesAny(routeKey, patterns) {
|
|
|
110
111
|
export async function getFimoPaths(opts = {}) {
|
|
111
112
|
const rootDir = opts.cwd ?? process.cwd();
|
|
112
113
|
const config = getConfigServer(rootDir);
|
|
113
|
-
const locales = getFimoLocales(config);
|
|
114
|
-
const routesEntries = Object.entries(config.routes ?? {});
|
|
115
114
|
// Pre-filter route keys by include/exclude before doing any network I/O.
|
|
116
|
-
const
|
|
117
|
-
if (
|
|
115
|
+
const contracts = resolveRouteContracts(config.routes).filter((contract) => {
|
|
116
|
+
if (!contract.config.path) {
|
|
118
117
|
return false;
|
|
119
118
|
}
|
|
120
|
-
|
|
119
|
+
// A placeholder no descriptor resolves cannot be expanded into a URL;
|
|
120
|
+
// `fimo validate` is what reports it.
|
|
121
|
+
if (contract.unresolvedParams.length > 0) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (opts.include && opts.include.length > 0 && !matchesAny(contract.id, opts.include)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
if (opts.exclude && opts.exclude.length > 0 && matchesAny(contract.id, opts.exclude)) {
|
|
121
128
|
return false;
|
|
122
129
|
}
|
|
123
130
|
return true;
|
|
@@ -125,20 +132,18 @@ export async function getFimoPaths(opts = {}) {
|
|
|
125
132
|
const results = [];
|
|
126
133
|
const schemaCache = new Map();
|
|
127
134
|
let baseUrl = null; // lazy-resolved on first dynamic route
|
|
128
|
-
for (const
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (!isDynamic) {
|
|
135
|
-
for (const locale of locales) {
|
|
135
|
+
for (const contract of contracts) {
|
|
136
|
+
const targets = routeLocaleTargets(contract, config);
|
|
137
|
+
// Static — no entry param, so no content to fetch. A locale param alone
|
|
138
|
+
// never reaches the CMS.
|
|
139
|
+
if (contract.entryParams.length === 0) {
|
|
140
|
+
for (const target of targets) {
|
|
136
141
|
results.push({
|
|
137
|
-
routeKey,
|
|
138
|
-
pathPattern,
|
|
139
|
-
path:
|
|
140
|
-
locale,
|
|
141
|
-
seo: resolveSeoForPath(
|
|
142
|
+
routeKey: contract.id,
|
|
143
|
+
pathPattern: contract.path,
|
|
144
|
+
path: materializeRoutePath(contract, target),
|
|
145
|
+
locale: target.locale,
|
|
146
|
+
seo: resolveSeoForPath(contract.canonicalPath, config),
|
|
142
147
|
});
|
|
143
148
|
}
|
|
144
149
|
continue;
|
|
@@ -153,40 +158,36 @@ export async function getFimoPaths(opts = {}) {
|
|
|
153
158
|
if (!baseUrl) {
|
|
154
159
|
continue;
|
|
155
160
|
}
|
|
156
|
-
const
|
|
157
|
-
const schemas = [...new Set(paramEntries.map(([, p]) => p.schema))];
|
|
161
|
+
const schemas = [...new Set(contract.entryParams.map((param) => param.schema))];
|
|
158
162
|
for (const schema of schemas) {
|
|
159
|
-
for (const
|
|
160
|
-
const cacheKey = `${locale}:${schema}`;
|
|
163
|
+
for (const target of targets) {
|
|
164
|
+
const cacheKey = `${target.locale}:${schema}`;
|
|
161
165
|
if (!schemaCache.has(cacheKey)) {
|
|
162
|
-
schemaCache.set(cacheKey, await fetchEntries(baseUrl, schema, rootDir, locale));
|
|
166
|
+
schemaCache.set(cacheKey, await fetchEntries(baseUrl, schema, rootDir, target.locale));
|
|
163
167
|
}
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
|
-
for (const
|
|
167
|
-
const entries = schemaCache.get(`${locale}:${
|
|
170
|
+
for (const target of targets) {
|
|
171
|
+
const entries = schemaCache.get(`${target.locale}:${contract.entryParams[0].schema}`) ?? [];
|
|
168
172
|
for (const entry of entries) {
|
|
169
|
-
let expandedPath = pathPattern;
|
|
170
173
|
const params = {};
|
|
171
174
|
let valid = true;
|
|
172
|
-
for (const
|
|
173
|
-
const value = entry[
|
|
175
|
+
for (const param of contract.entryParams) {
|
|
176
|
+
const value = entry[param.queryField];
|
|
174
177
|
if (value == null || value === '') {
|
|
175
178
|
valid = false;
|
|
176
179
|
break;
|
|
177
180
|
}
|
|
178
|
-
|
|
179
|
-
params[paramName] = stringValue;
|
|
180
|
-
expandedPath = expandedPath.replace(`:${paramName}`, encodeURIComponent(stringValue));
|
|
181
|
+
params[param.name] = String(value);
|
|
181
182
|
}
|
|
182
183
|
if (valid) {
|
|
183
184
|
results.push({
|
|
184
|
-
routeKey,
|
|
185
|
-
pathPattern,
|
|
186
|
-
path:
|
|
187
|
-
locale,
|
|
185
|
+
routeKey: contract.id,
|
|
186
|
+
pathPattern: contract.path,
|
|
187
|
+
path: materializeRoutePath(contract, { ...target, params }),
|
|
188
|
+
locale: target.locale,
|
|
188
189
|
params,
|
|
189
|
-
seo: resolveSeoForPath(
|
|
190
|
+
seo: resolveSeoForPath(materializeRoutePath(contract, { locale: target.locale, prefixed: false, params }), config),
|
|
190
191
|
});
|
|
191
192
|
}
|
|
192
193
|
}
|
|
@@ -7,7 +7,13 @@ export interface PreviewLocationChange {
|
|
|
7
7
|
/** Safe workspace focus value; query/hash state stays navigation-only. */
|
|
8
8
|
path: string;
|
|
9
9
|
/** Exact app location used to keep the parent's iframe URL in sync. */
|
|
10
|
-
navigationPath
|
|
10
|
+
navigationPath?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The locale the app settled on for this location. The app owns locale
|
|
13
|
+
* detection, so Studio stores this value rather than deriving one from
|
|
14
|
+
* `path`, and the bridge re-sends it when only the locale changed.
|
|
15
|
+
*/
|
|
16
|
+
locale: string;
|
|
11
17
|
}
|
|
12
18
|
export interface PreviewBrowserContext {
|
|
13
19
|
readonly window: Pick<Window, 'location' | 'sessionStorage'>;
|
|
@@ -19,5 +25,16 @@ export declare function resolvePreviewParentOrigin(referrer: string | undefined,
|
|
|
19
25
|
export declare function getPreviewParentOrigin(browser?: PreviewBrowserContext): string | null;
|
|
20
26
|
/** Keep sandbox credentials and the iframe cache-buster out of app navigation state. */
|
|
21
27
|
export declare function getPreviewLocationPath(location: PreviewLocationLike): string;
|
|
22
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Build the `nav/locationChange` payload. `navigationPath` carries the app's
|
|
30
|
+
* own query and hash state, so it is withheld until a parent origin is
|
|
31
|
+
* established and the message can be addressed to it instead of broadcast.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getPreviewLocationChange(location: PreviewLocationLike, locale: string, parentOrigin: string | null): PreviewLocationChange;
|
|
34
|
+
/**
|
|
35
|
+
* The locale the rendered document declares. Every Fimo framework shell writes
|
|
36
|
+
* `<html lang>` once it has settled the locale for the page, which makes it the
|
|
37
|
+
* app's own answer rather than a guess made from the URL.
|
|
38
|
+
*/
|
|
39
|
+
export declare function getDocumentLocale(browserDocument: Pick<Document, 'documentElement'> | undefined, fallbackLocale: string): string;
|
|
23
40
|
//# sourceMappingURL=location.d.ts.map
|
|
@@ -85,9 +85,23 @@ export function getPreviewLocationPath(location) {
|
|
|
85
85
|
const query = search.toString();
|
|
86
86
|
return `${location.pathname}${query ? `?${query}` : ''}${location.hash ?? ''}`;
|
|
87
87
|
}
|
|
88
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Build the `nav/locationChange` payload. `navigationPath` carries the app's
|
|
90
|
+
* own query and hash state, so it is withheld until a parent origin is
|
|
91
|
+
* established and the message can be addressed to it instead of broadcast.
|
|
92
|
+
*/
|
|
93
|
+
export function getPreviewLocationChange(location, locale, parentOrigin) {
|
|
89
94
|
return {
|
|
90
95
|
path: location.pathname,
|
|
91
|
-
navigationPath: getPreviewLocationPath(location),
|
|
96
|
+
...(parentOrigin ? { navigationPath: getPreviewLocationPath(location) } : {}),
|
|
97
|
+
locale,
|
|
92
98
|
};
|
|
93
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* The locale the rendered document declares. Every Fimo framework shell writes
|
|
102
|
+
* `<html lang>` once it has settled the locale for the page, which makes it the
|
|
103
|
+
* app's own answer rather than a guess made from the URL.
|
|
104
|
+
*/
|
|
105
|
+
export function getDocumentLocale(browserDocument, fallbackLocale) {
|
|
106
|
+
return browserDocument?.documentElement?.lang?.trim() || fallbackLocale;
|
|
107
|
+
}
|
|
@@ -2,6 +2,7 @@ export { Text } from '../react/primitives/editable/Text.js';
|
|
|
2
2
|
export { Image, StaticImage } from '../vite/primitives/Images.js';
|
|
3
3
|
export { Video } from '../react/primitives/editable/Video.js';
|
|
4
4
|
export { Date } from '../react/primitives/editable/Date.js';
|
|
5
|
+
export { DateTime } from '../react/primitives/editable/DateTime.js';
|
|
5
6
|
export { Boolean } from '../react/primitives/editable/Boolean.js';
|
|
6
7
|
export { Json } from '../react/primitives/editable/Json.js';
|
|
7
8
|
export { RichText } from '../react/primitives/editable/RichText.js';
|
|
@@ -5,6 +5,7 @@ export { Text } from '../react/primitives/editable/Text.js';
|
|
|
5
5
|
export { Image, StaticImage } from '../vite/primitives/Images.js';
|
|
6
6
|
export { Video } from '../react/primitives/editable/Video.js';
|
|
7
7
|
export { Date } from '../react/primitives/editable/Date.js';
|
|
8
|
+
export { DateTime } from '../react/primitives/editable/DateTime.js';
|
|
8
9
|
export { Boolean } from '../react/primitives/editable/Boolean.js';
|
|
9
10
|
export { Json } from '../react/primitives/editable/Json.js';
|
|
10
11
|
export { RichText } from '../react/primitives/editable/RichText.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DateProps } from '../contracts.js';
|
|
2
|
+
/** Renders a `datetime` field: the `dateTime` attribute keeps the whole instant. */
|
|
3
|
+
export declare function DateTime({ value, ...props }: DateProps): import("react/jsx-runtime").JSX.Element | null;
|
|
4
|
+
//# sourceMappingURL=DateTime.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FIMO_SOURCE } from '../../../primitives/lib/index.js';
|
|
3
|
+
import { useFimoLiveValue } from '../../useFimoLiveValue.js';
|
|
4
|
+
import { DateRenderer } from '../renderers/DateRenderer.js';
|
|
5
|
+
/** Renders a `datetime` field: the `dateTime` attribute keeps the whole instant. */
|
|
6
|
+
export function DateTime({ value, ...props }) {
|
|
7
|
+
const { value: liveValue, ref } = useFimoLiveValue(value ?? null);
|
|
8
|
+
if (!liveValue) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const source = typeof value === 'object' && FIMO_SOURCE in value ? (value[FIMO_SOURCE] ?? undefined) : undefined;
|
|
12
|
+
return _jsx(DateRenderer, { ...props, value: liveValue, source: source, precision: "instant", elementRef: ref });
|
|
13
|
+
}
|
|
@@ -3,9 +3,15 @@ import type { FimoDate, FimoString } from '../../../primitives/lib/index.js';
|
|
|
3
3
|
export interface DateRendererProps {
|
|
4
4
|
value: FimoDate | FimoString | Date | string | number;
|
|
5
5
|
source: string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* `day` emits the calendar day in `dateTime`; `instant` keeps the full ISO
|
|
8
|
+
* instant, which is the whole point of a `datetime` field. Defaults to `day`
|
|
9
|
+
* so `<Date>` renders exactly what it always has.
|
|
10
|
+
*/
|
|
11
|
+
precision?: 'day' | 'instant';
|
|
6
12
|
children?: (date: Date) => ReactNode;
|
|
7
13
|
className?: string;
|
|
8
14
|
elementRef?: Ref<HTMLTimeElement>;
|
|
9
15
|
}
|
|
10
|
-
export declare function DateRenderer({ value, source, children, className, elementRef }: DateRendererProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function DateRenderer({ value, source, precision, children, className, elementRef }: DateRendererProps): import("react/jsx-runtime").JSX.Element;
|
|
11
17
|
//# sourceMappingURL=DateRenderer.d.ts.map
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
export function DateRenderer({ value, source, children, className, elementRef }) {
|
|
2
|
+
export function DateRenderer({ value, source, precision = 'day', children, className, elementRef }) {
|
|
3
3
|
const rawValue = value instanceof String ? value.toString() : value;
|
|
4
4
|
const dateValue = new globalThis.Date(rawValue);
|
|
5
|
-
const
|
|
5
|
+
const iso = dateValue.toISOString();
|
|
6
|
+
const dateTime = precision === 'instant' ? iso : iso.split('T')[0];
|
|
6
7
|
const content = children
|
|
7
8
|
? children(dateValue)
|
|
8
|
-
:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
: precision === 'instant'
|
|
10
|
+
? dateValue.toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' })
|
|
11
|
+
: dateValue.toLocaleDateString(undefined, {
|
|
12
|
+
year: 'numeric',
|
|
13
|
+
month: 'short',
|
|
14
|
+
day: 'numeric',
|
|
15
|
+
});
|
|
16
|
+
return (_jsx("time", { ref: elementRef, dateTime: dateTime, className: className, "data-fimo-source": source, children: content }));
|
|
14
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FimoConfig } from '
|
|
1
|
+
import type { FimoConfig } from '../../shared/fimo-config.types.js';
|
|
2
2
|
export interface FimoReactRouterRoute {
|
|
3
3
|
id?: string;
|
|
4
4
|
path?: string;
|
|
@@ -25,6 +25,11 @@ export interface FimoRoutesOptions {
|
|
|
25
25
|
config?: Pick<FimoConfig, 'i18n'>;
|
|
26
26
|
rootDir?: string;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated New React Router projects should declare optional `:locale?`
|
|
30
|
+
* segments directly. This compatibility adapter may be removed in a future
|
|
31
|
+
* breaking release.
|
|
32
|
+
*/
|
|
28
33
|
export declare function fimoRoutes<T extends FimoRouteShape>(routes: readonly T[], options?: FimoRoutesOptions): T[];
|
|
29
34
|
export {};
|
|
30
35
|
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { getFimoDefaultLocale, getFimoLocales } from '
|
|
2
|
-
import {
|
|
1
|
+
import { getFimoDefaultLocale, getFimoLocales } from '../../i18n/locale.js';
|
|
2
|
+
import { hasLocaleSegment } from '../../routes/contract.js';
|
|
3
|
+
import { getConfigServer } from '../../shared/fimo-config.server.js';
|
|
4
|
+
/**
|
|
5
|
+
* @deprecated New React Router projects should declare optional `:locale?`
|
|
6
|
+
* segments directly. This compatibility adapter may be removed in a future
|
|
7
|
+
* breaking release.
|
|
8
|
+
*/
|
|
3
9
|
export function fimoRoutes(routes, options = {}) {
|
|
4
10
|
const config = options.config ?? getConfigServer(options.rootDir ?? process.cwd());
|
|
5
11
|
const strategy = config.i18n?.routing?.strategy ?? 'path-prefix';
|
|
@@ -7,21 +13,30 @@ export function fimoRoutes(routes, options = {}) {
|
|
|
7
13
|
return [...routes];
|
|
8
14
|
}
|
|
9
15
|
const defaultLocale = getFimoDefaultLocale(config);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const prefixedLocales =
|
|
13
|
-
|
|
16
|
+
// The default locale renders at the canonical unprefixed path, so it never
|
|
17
|
+
// gets an alias.
|
|
18
|
+
const prefixedLocales = getFimoLocales(config).filter((locale) => locale !== defaultLocale);
|
|
19
|
+
// A route that declares its own `:locale` segment already carries the locale
|
|
20
|
+
// in its path; prefixing it again would produce `/es/:locale/...`.
|
|
21
|
+
const aliasable = routes.filter((route) => !declaresLocaleSegment(route));
|
|
22
|
+
if (prefixedLocales.length === 0 || aliasable.length === 0) {
|
|
14
23
|
return [...routes];
|
|
15
24
|
}
|
|
16
25
|
return [
|
|
17
26
|
...routes,
|
|
18
|
-
...prefixedLocales.flatMap((locale) =>
|
|
27
|
+
...prefixedLocales.flatMap((locale) => aliasable.map((route, index) => localizeRouteEntry(route, {
|
|
19
28
|
locale,
|
|
20
29
|
prefixThisEntry: true,
|
|
21
30
|
routeKey: String(index),
|
|
22
31
|
}))),
|
|
23
32
|
];
|
|
24
33
|
}
|
|
34
|
+
function declaresLocaleSegment(route) {
|
|
35
|
+
if (route.path && hasLocaleSegment(route.path)) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return (route.children ?? []).some(declaresLocaleSegment);
|
|
39
|
+
}
|
|
25
40
|
function localizeRouteEntry(route, options) {
|
|
26
41
|
const clone = {
|
|
27
42
|
...route,
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { FimoConfig, FimoRouteConfig } from '../shared/fimo-config.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The reserved parameter name for the locale segment. A `:locale` placeholder
|
|
4
|
+
* is always the locale, whether or not the route declares a descriptor for it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const FIMO_LOCALE_PARAM = "locale";
|
|
7
|
+
export interface FimoEntryParam {
|
|
8
|
+
type: 'entry';
|
|
9
|
+
name: string;
|
|
10
|
+
schema: string;
|
|
11
|
+
queryField: string;
|
|
12
|
+
displayField?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface FimoLocaleParam {
|
|
15
|
+
type: 'locale';
|
|
16
|
+
name: string;
|
|
17
|
+
/**
|
|
18
|
+
* True (the default) means the default locale renders at the unprefixed
|
|
19
|
+
* path and only other locales carry a `/<locale>` prefix. False means every
|
|
20
|
+
* locale, the default included, renders behind the prefix.
|
|
21
|
+
*/
|
|
22
|
+
optional: boolean;
|
|
23
|
+
}
|
|
24
|
+
export type FimoRouteParam = FimoEntryParam | FimoLocaleParam;
|
|
25
|
+
export interface FimoRouteIssue {
|
|
26
|
+
/** The parameter the issue is about, when it is about one. */
|
|
27
|
+
param?: string;
|
|
28
|
+
message: string;
|
|
29
|
+
/** A concrete edit that resolves the issue. */
|
|
30
|
+
fix: string;
|
|
31
|
+
}
|
|
32
|
+
export interface FimoRouteContract {
|
|
33
|
+
id: string;
|
|
34
|
+
/** The declared route entry, kept so callers can reach `label` / `seo`. */
|
|
35
|
+
config: FimoRouteConfig;
|
|
36
|
+
/** The declared path, locale placeholder included (`/:locale/blog/:slug`). */
|
|
37
|
+
path: string;
|
|
38
|
+
/** The declared path without the locale placeholder (`/blog/:slug`). */
|
|
39
|
+
canonicalPath: string;
|
|
40
|
+
params: FimoRouteParam[];
|
|
41
|
+
entryParams: FimoEntryParam[];
|
|
42
|
+
localeParam: FimoLocaleParam | null;
|
|
43
|
+
/** Placeholders in the path that no usable descriptor resolves. */
|
|
44
|
+
unresolvedParams: string[];
|
|
45
|
+
issues: FimoRouteIssue[];
|
|
46
|
+
}
|
|
47
|
+
export interface FimoRouteMatch {
|
|
48
|
+
contract: FimoRouteContract;
|
|
49
|
+
/** Every resolved path parameter, the locale segment included. */
|
|
50
|
+
params: Record<string, string>;
|
|
51
|
+
/** Entry parameters only — the subset a content lookup may use. */
|
|
52
|
+
entryParams: Record<string, string>;
|
|
53
|
+
/** The locale this URL renders. The project default when the path omits it. */
|
|
54
|
+
locale: string;
|
|
55
|
+
/** True when the locale came from a path segment rather than the default. */
|
|
56
|
+
localeFromPath: boolean;
|
|
57
|
+
/** Static segments matched. Higher wins when several routes match. */
|
|
58
|
+
score: number;
|
|
59
|
+
}
|
|
60
|
+
/** One URL shape a route produces for one locale. */
|
|
61
|
+
export interface FimoRouteLocaleTarget {
|
|
62
|
+
locale: string;
|
|
63
|
+
/** Whether this locale renders behind a `/<locale>` prefix. */
|
|
64
|
+
prefixed: boolean;
|
|
65
|
+
}
|
|
66
|
+
export declare function normalizeRoutePath(path: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* True when the path declares a locale segment, in either spelling: the config
|
|
69
|
+
* writes `:locale`, a React Router registration writes `:locale?` when the
|
|
70
|
+
* segment may be omitted.
|
|
71
|
+
*/
|
|
72
|
+
export declare function hasLocaleSegment(path: string): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Normalize one declared route into the contract every consumer reads.
|
|
75
|
+
*
|
|
76
|
+
* Parameter types resolve in this order:
|
|
77
|
+
* 1. an explicit `type`;
|
|
78
|
+
* 2. the reserved name `locale`, which infers the locale type;
|
|
79
|
+
* 3. otherwise the entry type, which is what every route written before
|
|
80
|
+
* locale-aware routing existed relies on.
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveRouteContract(id: string, route: FimoRouteConfig): FimoRouteContract;
|
|
83
|
+
export declare function resolveRouteContracts(routes: Record<string, FimoRouteConfig> | undefined): FimoRouteContract[];
|
|
84
|
+
/**
|
|
85
|
+
* Match a concrete pathname against the project's routes.
|
|
86
|
+
*
|
|
87
|
+
* The truth table, for a route declared `/:locale/x/:slug` in a project whose
|
|
88
|
+
* default locale is `en`:
|
|
89
|
+
*
|
|
90
|
+
* | URL | locale param | resolved locale | entry params |
|
|
91
|
+
* | --------------- | ------------ | --------------- | ------------------- |
|
|
92
|
+
* | `/x/hello` | optional | `en` (default) | `{ slug: 'hello' }` |
|
|
93
|
+
* | `/fr/x/bonjour` | optional | `fr` | `{ slug: 'bonjour'}`|
|
|
94
|
+
* | `/en/x/hello` | optional | no match | — |
|
|
95
|
+
* | `/x/hello` | required | no match | — |
|
|
96
|
+
* | `/en/x/hello` | required | `en` | `{ slug: 'hello' }` |
|
|
97
|
+
*
|
|
98
|
+
* A route with no locale segment always resolves the default locale.
|
|
99
|
+
*/
|
|
100
|
+
export declare function matchFimoRoute(pathnameOrUrl: string, config: Pick<FimoConfig, 'routes' | 'i18n'>): FimoRouteMatch | null;
|
|
101
|
+
/**
|
|
102
|
+
* The URL shapes a route produces, one per locale it can render.
|
|
103
|
+
*
|
|
104
|
+
* A route without a locale segment produces a single default-locale URL: under
|
|
105
|
+
* path-prefix routing it has no way to express another locale in its path.
|
|
106
|
+
*/
|
|
107
|
+
export declare function routeLocaleTargets(contract: FimoRouteContract, config: Pick<FimoConfig, 'i18n'>): FimoRouteLocaleTarget[];
|
|
108
|
+
/**
|
|
109
|
+
* Build the concrete URL for one locale target. Entry param values are
|
|
110
|
+
* URL-encoded; the locale segment is a prefix, never a substitution, so the
|
|
111
|
+
* canonical default-locale URL carries no locale at all.
|
|
112
|
+
*/
|
|
113
|
+
export declare function materializeRoutePath(contract: FimoRouteContract, target: FimoRouteLocaleTarget & {
|
|
114
|
+
params?: Record<string, string>;
|
|
115
|
+
}): string;
|
|
116
|
+
//# sourceMappingURL=contract.d.ts.map
|