fimo 0.11.0-staging.4 → 0.11.0-staging.6
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/agent-templates/content-translator/GOAL.md +3 -3
- package/assets/agent-templates/seo-audit/config.yaml +1 -1
- package/assets/skills/fimo/SKILL.md +1 -0
- package/assets/skills/fimo-cli/SKILL.md +8 -6
- package/assets/skills/fimo-cli/references/agents.md +71 -23
- package/assets/skills/fimo-cli/references/branches.md +2 -2
- package/assets/skills/fimo-cli/references/credits.md +1 -1
- package/assets/skills/fimo-cli/references/deploy.md +1 -1
- package/assets/skills/fimo-cli/references/describe.md +1 -1
- package/assets/skills/fimo-cli/references/history.md +1 -1
- package/assets/skills/fimo-cli/references/init.md +4 -2
- package/assets/skills/fimo-cli/references/integrations/firecrawl.md +12 -3
- package/assets/skills/fimo-cli/references/integrations.md +15 -3
- package/assets/skills/fimo-cli/references/invite.md +1 -1
- package/assets/skills/fimo-cli/references/labels.md +1 -1
- package/assets/skills/fimo-cli/references/locales.md +4 -4
- package/assets/skills/fimo-cli/references/mcp.md +85 -0
- package/assets/skills/fimo-cli/references/project-vars-and-secrets.md +1 -1
- package/assets/skills/fimo-cli/references/projects.md +7 -7
- package/assets/skills/fimo-code/references/labels.md +2 -2
- package/assets/skills/fimo-code/references/locales.md +5 -5
- package/assets/skills/fimo-code/references/setup-plain-vite.md +4 -4
- package/assets/skills/fimo-code/references/setup-react-router.md +5 -5
- package/assets/skills/fimo-code/references/ui.md +1 -1
- package/assets/skills/fimo-migration/references/workflow.md +1 -1
- package/assets/skills/fimo-migration/scripts/build-canonical-manifest.mjs +32 -3
- package/assets/skills/fimo-studio/references/custom-domains.md +1 -1
- package/dist/build/vite/index.d.ts +3 -3
- package/dist/build/vite/plugins/fimo-config.d.ts +2 -2
- package/dist/build/vite/plugins/fimo-config.js +5 -5
- package/dist/build/vite/plugins/robots.d.ts +1 -1
- package/dist/build/vite/plugins/robots.js +3 -3
- package/dist/build/vite/plugins/server-config.js +1 -1
- package/dist/build/vite/plugins/sitemap.js +2 -2
- package/dist/build/vite/plugins/translations.js +14 -10
- package/dist/cli/bundle.json +1 -1
- package/dist/cli/index.js +142 -131
- package/dist/runtime/app/prefetch.d.ts +1 -1
- package/dist/runtime/app/prefetch.js +2 -2
- package/dist/runtime/paths/get-fimo-paths.d.ts +1 -1
- package/dist/runtime/paths/get-fimo-paths.js +2 -2
- package/dist/runtime/paths/types.d.ts +1 -1
- package/dist/runtime/project-layout.d.ts +14 -0
- package/dist/runtime/project-layout.js +57 -0
- package/dist/runtime/seo/resolver.d.ts +1 -1
- package/dist/runtime/seo/resolver.js +1 -1
- package/dist/runtime/shared/fimo-config.server.d.ts +2 -0
- package/dist/runtime/shared/fimo-config.server.js +17 -4
- package/dist/runtime/templates.d.ts +4 -4
- package/dist/runtime/templates.js +11 -6
- package/dist/scripts/validate-route-metadata.js +8 -6
- package/package.json +7 -1
- package/templates/react-router/AGENTS.md +1 -1
- package/templates/react-router/package.json +1 -1
- /package/templates/react-router/{fimo-config.json → .fimo/config.json} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare function resolveFimoLocaleForUrl(url: string): import("./index.js").FimoLocaleResolution;
|
|
2
2
|
/**
|
|
3
3
|
* Fetch route data for SEO meta resolution.
|
|
4
|
-
* Only fetches data for detail pages (routes with `params` in fimo
|
|
4
|
+
* Only fetches data for detail pages (routes with `params` in .fimo/config.json).
|
|
5
5
|
*/
|
|
6
6
|
export declare function resolveMetaEntries(url: string): Promise<Record<string, unknown> | undefined>;
|
|
7
7
|
//# sourceMappingURL=prefetch.d.ts.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import fimoConfig from 'virtual:fimo-config';
|
|
3
3
|
import { resolveFimoLocale } from '../i18n/locale.js';
|
|
4
4
|
/**
|
|
5
|
-
* Match a concrete URL against the routes in fimo
|
|
5
|
+
* Match a concrete URL against the routes in .fimo/config.json.
|
|
6
6
|
* Returns the matching route config or null.
|
|
7
7
|
*/
|
|
8
8
|
function matchRoute(url) {
|
|
@@ -51,7 +51,7 @@ export function resolveFimoLocaleForUrl(url) {
|
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* Fetch route data for SEO meta resolution.
|
|
54
|
-
* Only fetches data for detail pages (routes with `params` in fimo
|
|
54
|
+
* Only fetches data for detail pages (routes with `params` in .fimo/config.json).
|
|
55
55
|
*/
|
|
56
56
|
export async function resolveMetaEntries(url) {
|
|
57
57
|
// @ts-ignore – import.meta.env is a Vite feature, replaced at build time for both client and SSR
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FimoPath, GetFimoPathsOptions } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
* Resolve the full list of paths from
|
|
3
|
+
* Resolve the full list of paths from `.fimo/config.json` and dynamic-route
|
|
4
4
|
* schema entries. Use as input to your framework's prerender list, sitemap,
|
|
5
5
|
* robots.txt, or any per-route logic.
|
|
6
6
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Resolve the list of paths a project should consider for prerender / sitemap /
|
|
2
|
-
// robots / per-route SEO generation. Static paths come from fimo
|
|
2
|
+
// robots / per-route SEO generation. Static paths come from .fimo/config.json;
|
|
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';
|
|
@@ -72,7 +72,7 @@ function matchesAny(routeKey, patterns) {
|
|
|
72
72
|
return patterns.some((p) => compilePattern(p).test(routeKey));
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
75
|
-
* Resolve the full list of paths from
|
|
75
|
+
* Resolve the full list of paths from `.fimo/config.json` and dynamic-route
|
|
76
76
|
* schema entries. Use as input to your framework's prerender list, sitemap,
|
|
77
77
|
* robots.txt, or any per-route logic.
|
|
78
78
|
*
|
|
@@ -4,7 +4,7 @@ import type { RouteSEO } from '../shared/fimo-config.types.js';
|
|
|
4
4
|
* sitemap / robots / per-route SEO generation. Returned by `getFimoPaths()`.
|
|
5
5
|
*/
|
|
6
6
|
export interface FimoPath {
|
|
7
|
-
/** Route key from
|
|
7
|
+
/** Route key from `.fimo/config.json#routes` (e.g. `'home'`, `'blog/post'`). */
|
|
8
8
|
routeKey: string;
|
|
9
9
|
/** The configured path pattern (e.g. `'/'`, `'/blog/:slug'`). */
|
|
10
10
|
pathPattern: string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ProjectLayout = 'current' | 'legacy';
|
|
2
|
+
export interface ProjectLayoutPaths {
|
|
3
|
+
readonly project: string;
|
|
4
|
+
readonly config: string;
|
|
5
|
+
readonly agents: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const CURRENT_PROJECT_LAYOUT: ProjectLayoutPaths;
|
|
8
|
+
export declare const LEGACY_PROJECT_LAYOUT: ProjectLayoutPaths;
|
|
9
|
+
export declare const LEGACY_AGENT_POLICY_PATH = "fimo.config.json";
|
|
10
|
+
export declare function detectProjectLayout(paths: Iterable<string>): ProjectLayout | null;
|
|
11
|
+
export declare function getProjectLayoutPaths(layout: ProjectLayout): ProjectLayoutPaths;
|
|
12
|
+
export declare function selectProjectConfigPath(paths: Iterable<string>, layout?: ProjectLayout | null): string;
|
|
13
|
+
export declare function selectProjectPolicyPath(paths: Iterable<string>, layout?: ProjectLayout | null): string;
|
|
14
|
+
//# sourceMappingURL=project-layout.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export const CURRENT_PROJECT_LAYOUT = {
|
|
2
|
+
project: '.fimo/project.json',
|
|
3
|
+
config: '.fimo/config.json',
|
|
4
|
+
agents: '.fimo/agents',
|
|
5
|
+
};
|
|
6
|
+
export const LEGACY_PROJECT_LAYOUT = {
|
|
7
|
+
project: '.fimo.settings.json',
|
|
8
|
+
config: 'fimo-config.json',
|
|
9
|
+
agents: 'agents',
|
|
10
|
+
};
|
|
11
|
+
export const LEGACY_AGENT_POLICY_PATH = 'fimo.config.json';
|
|
12
|
+
export function detectProjectLayout(paths) {
|
|
13
|
+
let hasCurrentProject = false;
|
|
14
|
+
let hasLegacyProject = false;
|
|
15
|
+
let hasCurrentMarker = false;
|
|
16
|
+
let hasLegacyMarker = false;
|
|
17
|
+
for (const input of paths) {
|
|
18
|
+
const file = input.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/$/, '');
|
|
19
|
+
hasCurrentProject || (hasCurrentProject = file === CURRENT_PROJECT_LAYOUT.project);
|
|
20
|
+
hasLegacyProject || (hasLegacyProject = file === LEGACY_PROJECT_LAYOUT.project);
|
|
21
|
+
hasCurrentMarker || (hasCurrentMarker = file === CURRENT_PROJECT_LAYOUT.config ||
|
|
22
|
+
file === CURRENT_PROJECT_LAYOUT.agents ||
|
|
23
|
+
file.startsWith(`${CURRENT_PROJECT_LAYOUT.agents}/`));
|
|
24
|
+
hasLegacyMarker || (hasLegacyMarker = file === LEGACY_PROJECT_LAYOUT.config || file === LEGACY_AGENT_POLICY_PATH);
|
|
25
|
+
}
|
|
26
|
+
if (hasCurrentProject) {
|
|
27
|
+
return 'current';
|
|
28
|
+
}
|
|
29
|
+
if (hasLegacyProject) {
|
|
30
|
+
return 'legacy';
|
|
31
|
+
}
|
|
32
|
+
if (hasCurrentMarker) {
|
|
33
|
+
return 'current';
|
|
34
|
+
}
|
|
35
|
+
return hasLegacyMarker ? 'legacy' : null;
|
|
36
|
+
}
|
|
37
|
+
export function getProjectLayoutPaths(layout) {
|
|
38
|
+
return layout === 'current' ? CURRENT_PROJECT_LAYOUT : LEGACY_PROJECT_LAYOUT;
|
|
39
|
+
}
|
|
40
|
+
export function selectProjectConfigPath(paths, layout) {
|
|
41
|
+
const files = new Set(Array.from(paths, (file) => file.replace(/\\/g, '/').replace(/^\.\//, '')));
|
|
42
|
+
const resolvedLayout = layout ?? detectProjectLayout(files) ?? 'current';
|
|
43
|
+
if (resolvedLayout === 'current') {
|
|
44
|
+
return CURRENT_PROJECT_LAYOUT.config;
|
|
45
|
+
}
|
|
46
|
+
return files.has(LEGACY_AGENT_POLICY_PATH) && !files.has(LEGACY_PROJECT_LAYOUT.config)
|
|
47
|
+
? LEGACY_AGENT_POLICY_PATH
|
|
48
|
+
: LEGACY_PROJECT_LAYOUT.config;
|
|
49
|
+
}
|
|
50
|
+
export function selectProjectPolicyPath(paths, layout) {
|
|
51
|
+
const files = new Set(Array.from(paths, (file) => file.replace(/\\/g, '/').replace(/^\.\//, '')));
|
|
52
|
+
const resolvedLayout = layout ?? detectProjectLayout(files) ?? 'current';
|
|
53
|
+
if (resolvedLayout === 'current') {
|
|
54
|
+
return CURRENT_PROJECT_LAYOUT.config;
|
|
55
|
+
}
|
|
56
|
+
return files.has(LEGACY_AGENT_POLICY_PATH) ? LEGACY_AGENT_POLICY_PATH : LEGACY_PROJECT_LAYOUT.config;
|
|
57
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { FimoConfig, FimoRouteConfig, FimoSEO } from '../shared/fimo-config
|
|
|
3
3
|
* Shared SEO resolver for the generated app.
|
|
4
4
|
*
|
|
5
5
|
* This module answers one question:
|
|
6
|
-
* "Given a pathname and the project's fimo
|
|
6
|
+
* "Given a pathname and the project's .fimo/config.json, what SEO values
|
|
7
7
|
* should apply to this route?"
|
|
8
8
|
*
|
|
9
9
|
* It centralizes the rule set that was previously duplicated in multiple places:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared SEO resolver for the generated app.
|
|
3
3
|
*
|
|
4
4
|
* This module answers one question:
|
|
5
|
-
* "Given a pathname and the project's fimo
|
|
5
|
+
* "Given a pathname and the project's .fimo/config.json, what SEO values
|
|
6
6
|
* should apply to this route?"
|
|
7
7
|
*
|
|
8
8
|
* It centralizes the rule set that was previously duplicated in multiple places:
|
|
@@ -5,4 +5,6 @@ import type { FimoConfig } from './fimo-config.types.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare function getConfigServer(rootDir: string): FimoConfig;
|
|
7
7
|
export declare function getConfigVersion(rootDir: string): number;
|
|
8
|
+
/** Resolve the current config path while keeping legacy projects readable. */
|
|
9
|
+
export declare function resolveFimoConfigPath(rootDir: string): string;
|
|
8
10
|
//# sourceMappingURL=fimo-config.server.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
|
-
|
|
3
|
+
import { CURRENT_PROJECT_LAYOUT, LEGACY_AGENT_POLICY_PATH, LEGACY_PROJECT_LAYOUT, selectProjectConfigPath, } from '../project-layout.js';
|
|
4
|
+
// Matches the template's `.fimo/config.json` (`version: "4"`) and the agents
|
|
4
5
|
// workspace default — the fallback when a config omits the field.
|
|
5
6
|
const LATEST_TEMPLATE_VERSION = 4;
|
|
6
7
|
const defaultConfig = {
|
|
@@ -12,7 +13,7 @@ const defaultConfig = {
|
|
|
12
13
|
*/
|
|
13
14
|
export function getConfigServer(rootDir) {
|
|
14
15
|
try {
|
|
15
|
-
const configPath =
|
|
16
|
+
const configPath = resolveFimoConfigPath(rootDir);
|
|
16
17
|
const content = readFileSync(configPath, 'utf-8');
|
|
17
18
|
const raw = JSON.parse(content);
|
|
18
19
|
return {
|
|
@@ -28,7 +29,7 @@ export function getConfigServer(rootDir) {
|
|
|
28
29
|
}
|
|
29
30
|
export function getConfigVersion(rootDir) {
|
|
30
31
|
try {
|
|
31
|
-
const configPath =
|
|
32
|
+
const configPath = resolveFimoConfigPath(rootDir);
|
|
32
33
|
const content = readFileSync(configPath, 'utf-8');
|
|
33
34
|
const raw = JSON.parse(content);
|
|
34
35
|
const parsed = Number.parseInt(String(raw.version ?? LATEST_TEMPLATE_VERSION), 10);
|
|
@@ -38,3 +39,15 @@ export function getConfigVersion(rootDir) {
|
|
|
38
39
|
return LATEST_TEMPLATE_VERSION;
|
|
39
40
|
}
|
|
40
41
|
}
|
|
42
|
+
/** Resolve the current config path while keeping legacy projects readable. */
|
|
43
|
+
export function resolveFimoConfigPath(rootDir) {
|
|
44
|
+
const candidates = [
|
|
45
|
+
CURRENT_PROJECT_LAYOUT.project,
|
|
46
|
+
LEGACY_PROJECT_LAYOUT.project,
|
|
47
|
+
CURRENT_PROJECT_LAYOUT.config,
|
|
48
|
+
LEGACY_PROJECT_LAYOUT.config,
|
|
49
|
+
LEGACY_AGENT_POLICY_PATH,
|
|
50
|
+
];
|
|
51
|
+
const existing = candidates.filter((candidate) => existsSync(resolve(rootDir, candidate)));
|
|
52
|
+
return resolve(rootDir, selectProjectConfigPath(existing));
|
|
53
|
+
}
|
|
@@ -45,17 +45,17 @@ export declare function readCliSemver(): string | null;
|
|
|
45
45
|
export declare function computeFimoPin(meta: ReleaseMeta, semver: string | null, options?: {
|
|
46
46
|
devPackageRoot?: string | null;
|
|
47
47
|
}): string | null;
|
|
48
|
-
export declare function computeRemoteFimoPin(meta: ReleaseMeta, semver: string | null, options
|
|
48
|
+
export declare function computeRemoteFimoPin(meta: ReleaseMeta, semver: string | null, options: {
|
|
49
49
|
devReleaseVersion?: string | null;
|
|
50
|
-
fallback
|
|
50
|
+
fallback: string | null;
|
|
51
51
|
}): string | null;
|
|
52
52
|
/** Filesystem-backed wrapper around computeFimoPin. */
|
|
53
53
|
export declare function resolveFimoPin(options?: {
|
|
54
54
|
devPackageRoot?: string | null;
|
|
55
55
|
}): string | null;
|
|
56
|
-
export declare function resolveRemoteFimoPin(options
|
|
56
|
+
export declare function resolveRemoteFimoPin(options: {
|
|
57
57
|
devSearchStart?: string | null;
|
|
58
|
-
fallback
|
|
58
|
+
fallback: string | null;
|
|
59
59
|
}): string | null;
|
|
60
60
|
export declare function readDevReleaseVersion(startPath?: string): DevReleaseVersion;
|
|
61
61
|
interface PkgWithDeps {
|
|
@@ -27,7 +27,6 @@ const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
|
27
27
|
const PACKAGE_ROOT = path.resolve(MODULE_DIR, '..', '..');
|
|
28
28
|
const RELEASE_FILE = path.join(PACKAGE_ROOT, 'release.json');
|
|
29
29
|
const PACKAGE_JSON_FILE = path.join(PACKAGE_ROOT, 'package.json');
|
|
30
|
-
const FALLBACK_REMOTE_FIMO_PIN = 'latest';
|
|
31
30
|
export const TEMPLATE_FRAMEWORKS = ['react-router', 'astro', 'next', 'vite'];
|
|
32
31
|
export const DEFAULT_TEMPLATE = 'react-router';
|
|
33
32
|
/**
|
|
@@ -105,7 +104,7 @@ export function computeFimoPin(meta, semver, options = {}) {
|
|
|
105
104
|
}
|
|
106
105
|
return null;
|
|
107
106
|
}
|
|
108
|
-
export function computeRemoteFimoPin(meta, semver, options
|
|
107
|
+
export function computeRemoteFimoPin(meta, semver, options) {
|
|
109
108
|
if (meta.source === 'npm') {
|
|
110
109
|
if (semver) {
|
|
111
110
|
return semver;
|
|
@@ -116,13 +115,13 @@ export function computeRemoteFimoPin(meta, semver, options = {}) {
|
|
|
116
115
|
if (devReleaseVersion) {
|
|
117
116
|
return devReleaseVersion;
|
|
118
117
|
}
|
|
119
|
-
return options.fallback
|
|
118
|
+
return options.fallback;
|
|
120
119
|
}
|
|
121
120
|
/** Filesystem-backed wrapper around computeFimoPin. */
|
|
122
121
|
export function resolveFimoPin(options = {}) {
|
|
123
122
|
return computeFimoPin(readReleaseMeta(), readCliSemver(), options);
|
|
124
123
|
}
|
|
125
|
-
export function resolveRemoteFimoPin(options
|
|
124
|
+
export function resolveRemoteFimoPin(options) {
|
|
126
125
|
const meta = readReleaseMeta();
|
|
127
126
|
const semver = readCliSemver();
|
|
128
127
|
const devRelease = meta.source === 'npm' ? { version: null } : readDevReleaseVersion(options.devSearchStart ?? PACKAGE_ROOT);
|
|
@@ -136,9 +135,15 @@ export function readDevReleaseVersion(startPath = PACKAGE_ROOT) {
|
|
|
136
135
|
if (!devJsonPath) {
|
|
137
136
|
return { devJsonPath: null, version: null };
|
|
138
137
|
}
|
|
139
|
-
|
|
138
|
+
let parsed;
|
|
139
|
+
try {
|
|
140
|
+
parsed = JSON.parse(fs.readFileSync(devJsonPath, 'utf8'));
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
throw new Error(`Invalid JSON in ${devJsonPath}. Run \`pnpm -F fimo publish-cli -- --id <task-name>\` from that Fimo checkout, then retry.`);
|
|
144
|
+
}
|
|
140
145
|
if (typeof parsed.version !== 'string' || parsed.version.trim().length === 0) {
|
|
141
|
-
throw new Error(`Missing "version" in ${devJsonPath}.
|
|
146
|
+
throw new Error(`Missing "version" in ${devJsonPath}. Run \`pnpm -F fimo publish-cli -- --id <task-name>\` from that Fimo checkout, then retry.`);
|
|
142
147
|
}
|
|
143
148
|
return {
|
|
144
149
|
devJsonPath,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { resolveFimoConfigPath } from '../runtime/shared/fimo-config.server.js';
|
|
3
4
|
import { parseRoutesSource } from './lib/parse-routes-file.js';
|
|
4
5
|
function readJson(filePath) {
|
|
5
6
|
try {
|
|
@@ -27,7 +28,8 @@ function loadConfigRoutes(config) {
|
|
|
27
28
|
}
|
|
28
29
|
export function validateRouteMetadata(options = {}) {
|
|
29
30
|
const cwd = options.cwd ?? process.cwd();
|
|
30
|
-
const configFilePath =
|
|
31
|
+
const configFilePath = resolveFimoConfigPath(cwd);
|
|
32
|
+
const configDisplayPath = path.relative(cwd, configFilePath).replaceAll(path.sep, '/');
|
|
31
33
|
const srcDir = path.resolve(cwd, 'src');
|
|
32
34
|
const routesFilePath = path.resolve(srcDir, 'routes.ts');
|
|
33
35
|
const errors = [];
|
|
@@ -47,7 +49,6 @@ export function validateRouteMetadata(options = {}) {
|
|
|
47
49
|
pathToFile.set(fr.path, fr.file);
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
|
-
// Load route metadata from fimo-config.json
|
|
51
52
|
const config = readJson(configFilePath);
|
|
52
53
|
const i18n = validateI18nConfig(config);
|
|
53
54
|
errors.push(...i18n.errors);
|
|
@@ -59,14 +60,14 @@ export function validateRouteMetadata(options = {}) {
|
|
|
59
60
|
continue;
|
|
60
61
|
}
|
|
61
62
|
if (fileRoute.path && !configPaths.has(fileRoute.path)) {
|
|
62
|
-
errors.push(`Route with path "${fileRoute.path}" (file: "${fileRoute.file}") exists in src/routes.ts but has no matching entry in
|
|
63
|
+
errors.push(`Route with path "${fileRoute.path}" (file: "${fileRoute.file}") exists in src/routes.ts but has no matching entry in ${configDisplayPath}.\nFix: add a route entry to ${configDisplayPath} "routes" with a matching "path".`);
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
// Cross-validate: fimo-config routes should have matching routes.ts entries
|
|
66
67
|
const filePaths = new Set(fileRoutes.filter((r) => !r.isLayout && r.path).map((r) => r.path));
|
|
67
68
|
for (const route of configRoutes) {
|
|
68
69
|
if (!filePaths.has(route.path)) {
|
|
69
|
-
errors.push(`Route "${route.id}" (${route.path}) exists in
|
|
70
|
+
errors.push(`Route "${route.id}" (${route.path}) exists in ${configDisplayPath} but is not registered in src/routes.ts.\nFix: add the route to src/routes.ts. Example: route('${route.path.replace(/^\//, '')}', './pages/YourPage.tsx')`);
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
if (i18n.routingStrategy === 'path-prefix' && i18n.locales.length > 1) {
|
|
@@ -74,6 +75,7 @@ export function validateRouteMetadata(options = {}) {
|
|
|
74
75
|
configRoutes,
|
|
75
76
|
fileRoutes,
|
|
76
77
|
i18n,
|
|
78
|
+
configDisplayPath,
|
|
77
79
|
routesSourceUsesFimoRoutes: sourceUsesFimoRoutes(fileRoutes, routesFilePath),
|
|
78
80
|
}));
|
|
79
81
|
}
|
|
@@ -99,7 +101,7 @@ export function validateRouteMetadata(options = {}) {
|
|
|
99
101
|
}
|
|
100
102
|
if (route.paramNames.length > 0) {
|
|
101
103
|
if (!route.params) {
|
|
102
|
-
errors.push(`Dynamic route "${route.id}" (${route.path}) needs \`params\` metadata to generate prerender URLs.\nFix: add to the route in
|
|
104
|
+
errors.push(`Dynamic route "${route.id}" (${route.path}) needs \`params\` metadata to generate prerender URLs.\nFix: add to the route in ${configDisplayPath}: \`"params": { "${route.paramNames[0] || 'param'}": { "schema": "SchemaName", "queryField": "${route.paramNames[0] || 'slug'}", "displayField": "title" } }\``);
|
|
103
105
|
}
|
|
104
106
|
else {
|
|
105
107
|
for (const paramName of route.paramNames) {
|
|
@@ -200,7 +202,7 @@ function validateLocaleRouteCollisions(args) {
|
|
|
200
202
|
for (const route of args.configRoutes) {
|
|
201
203
|
const first = firstPathSegment(route.path);
|
|
202
204
|
if (first && reservedPrefixes.has(first.toLowerCase())) {
|
|
203
|
-
errors.push(`Route "${route.id}" (${route.path}) uses reserved locale prefix "${first}". Keep
|
|
205
|
+
errors.push(`Route "${route.id}" (${route.path}) uses reserved locale prefix "${first}". Keep ${args.configDisplayPath} routes canonical and unlocalized, for example "/pricing"; let fimoRoutes() or custom locale routing produce "/${first}/...".`);
|
|
204
206
|
}
|
|
205
207
|
}
|
|
206
208
|
if (!args.routesSourceUsesFimoRoutes) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fimo",
|
|
3
|
-
"version": "0.11.0-staging.
|
|
3
|
+
"version": "0.11.0-staging.6",
|
|
4
4
|
"description": "Fimo CLI - create, deploy, and manage Fimo projects",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fimo": "dist/cli/index.js"
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"./templates": {
|
|
42
42
|
"types": "./dist/runtime/templates.d.ts",
|
|
43
43
|
"import": "./dist/runtime/templates.js"
|
|
44
|
+
},
|
|
45
|
+
"./project-layout": {
|
|
46
|
+
"types": "./dist/runtime/project-layout.d.ts",
|
|
47
|
+
"import": "./dist/runtime/project-layout.js"
|
|
44
48
|
}
|
|
45
49
|
},
|
|
46
50
|
"publishConfig": {
|
|
@@ -76,7 +80,9 @@
|
|
|
76
80
|
"@fimo/agents-bundle": "workspace:*",
|
|
77
81
|
"@fimo/api-contracts": "workspace:*",
|
|
78
82
|
"@fimo/cli-kit": "workspace:*",
|
|
83
|
+
"@fimo/mcp": "workspace:*",
|
|
79
84
|
"@fimo/tsconfig": "workspace:*",
|
|
85
|
+
"@modelcontextprotocol/sdk": "1.27.1",
|
|
80
86
|
"@tanstack/react-query": "^5.80.6",
|
|
81
87
|
"@types/node": "^22.15.29",
|
|
82
88
|
"@types/react": "^19.2.14",
|
|
@@ -38,7 +38,7 @@ breaks that. Load `fimo-code` for the exact primitives, but the rule is:
|
|
|
38
38
|
|
|
39
39
|
- **Deploy is on-demand**, not a save step. Batch related edits into one deploy.
|
|
40
40
|
- Do not add a second deploy tool (Vercel, Netlify, etc.).
|
|
41
|
-
- **Never edit `.fimo.
|
|
41
|
+
- **Never edit `.fimo/project.json`** — it links this folder to the remote project.
|
|
42
42
|
- The scaffolded homepage is disposable starter content. Replace it on the first real generation.
|
|
43
43
|
- **Secrets live in the Fimo dashboard**, not in a committed `.env`.
|
|
44
44
|
- When scripting, pass flags to `fimo` commands (`fimo deploy -m "…"`, `fimo delete -y`) — bare interactive forms hang in non-TTY shells.
|
|
File without changes
|