blume 0.6.7 → 0.8.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/CHANGELOG.md +618 -0
- package/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/cli/index.js +2609 -1041
- package/dist/cli/index.js.map +110 -103
- package/dist/types/ai/component-markdown.d.ts +34 -0
- package/dist/types/components/content/youtube.d.ts +18 -0
- package/dist/types/core/base-path.d.ts +47 -0
- package/dist/types/core/config-input.d.ts +110 -12
- package/dist/types/core/config.d.ts +6 -4
- package/dist/types/core/data.d.ts +4 -0
- package/dist/types/core/i18n-ui.d.ts +477 -135
- package/dist/types/core/schema.d.ts +309 -195
- package/dist/types/core/sources/types.d.ts +2 -0
- package/dist/types/core/types.d.ts +6 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/openapi/references.d.ts +60 -0
- package/docs/01-quickstart.mdx +5 -2
- package/docs/02-deployment.mdx +24 -9
- package/docs/03-faq.mdx +46 -16
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/configuration/ai.mdx +49 -10
- package/docs/configuration/customization.mdx +11 -0
- package/docs/configuration/index.mdx +33 -3
- package/docs/configuration/seo.mdx +2 -2
- package/docs/content/components.mdx +30 -3
- package/docs/content/i18n.mdx +1 -1
- package/docs/content/islands.mdx +8 -0
- package/docs/content/navigation.mdx +3 -3
- package/docs/content/sources.mdx +1 -1
- package/docs/content/syntax.mdx +17 -2
- package/docs/index.mdx +2 -2
- package/docs/reference/cli.mdx +8 -6
- package/package.json +15 -4
- package/skills/blume/SKILL.md +5 -3
- package/skills/blume-update-docs/SKILL.md +3 -2
- package/src/ai/agent-readability.ts +11 -5
- package/src/ai/ask-context.ts +7 -2
- package/src/ai/ask-data.ts +3 -0
- package/src/ai/ask.ts +12 -7
- package/src/ai/component-markdown.ts +461 -0
- package/src/ai/llms.ts +143 -23
- package/src/ai/markdown.ts +35 -6
- package/src/ai/mcp/data.ts +33 -8
- package/src/ai/mcp/discovery.ts +10 -3
- package/src/ai/mcp/server.ts +24 -7
- package/src/ai/visibility.ts +74 -0
- package/src/astro/component-slots.ts +16 -4
- package/src/astro/examples.ts +12 -7
- package/src/astro/generate.ts +393 -189
- package/src/astro/index.ts +5 -1
- package/src/astro/integration.ts +9 -5
- package/src/astro/islands.ts +11 -5
- package/src/astro/markdown-negotiation.ts +2 -2
- package/src/astro/pages.ts +89 -22
- package/src/astro/templates.ts +259 -25
- package/src/blume-modules.d.ts +8 -0
- package/src/cli/commands/build.ts +131 -38
- package/src/cli/commands/check.ts +1 -1
- package/src/cli/commands/dev.ts +71 -17
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/eject.ts +47 -19
- package/src/cli/commands/init.ts +120 -180
- package/src/cli/commands/preview.ts +4 -1
- package/src/cli/commands/validate.ts +44 -2
- package/src/cli/dev-lock.ts +34 -19
- package/src/cli/eject-scripts.ts +72 -0
- package/src/cli/env.ts +15 -5
- package/src/cli/init/questions.ts +158 -0
- package/src/cli/init/scaffold.ts +380 -0
- package/src/cli/required-secrets.ts +2 -1
- package/src/components/content/AccordionItem.astro +23 -4
- package/src/components/content/Badge.astro +3 -1
- package/src/components/content/Card.astro +4 -2
- package/src/components/content/CodeBlock.astro +3 -0
- package/src/components/content/Component.astro +30 -16
- package/src/components/content/Diff.astro +3 -1
- package/src/components/content/Step.astro +10 -1
- package/src/components/content/Tabs.astro +15 -3
- package/src/components/content/Tile.astro +2 -1
- package/src/components/content/Tooltip.astro +3 -1
- package/src/components/content/Update.astro +9 -2
- package/src/components/content/auto-type-table.ts +25 -9
- package/src/components/content/base-href.ts +33 -0
- package/src/components/content/changelog-element.ts +9 -2
- package/src/components/content/diff.ts +12 -6
- package/src/components/content/mermaid-element.ts +10 -2
- package/src/components/index.ts +23 -1
- package/src/components/islands/AskAI.astro +5 -2
- package/src/components/islands/ask-ai.tsx +68 -12
- package/src/components/islands/base-path.ts +28 -0
- package/src/components/islands/hooks.ts +44 -9
- package/src/components/layout/Banner.astro +12 -3
- package/src/components/layout/Breadcrumbs.astro +2 -1
- package/src/components/layout/Favicon.astro +3 -2
- package/src/components/layout/Header.astro +15 -5
- package/src/components/layout/LanguageSwitcher.astro +2 -1
- package/src/components/layout/Logo.astro +13 -4
- package/src/components/layout/NavSelector.astro +2 -1
- package/src/components/layout/NavTree.astro +22 -7
- package/src/components/layout/PageActions.astro +25 -10
- package/src/components/layout/PageFeedback.astro +4 -1
- package/src/components/layout/PageLayout.astro +51 -9
- package/src/components/layout/Pagination.astro +3 -2
- package/src/components/layout/ReferenceLayout.astro +8 -1
- package/src/components/layout/RootLayout.astro +74 -13
- package/src/components/layout/Search.astro +107 -27
- package/src/components/layout/nav-utils.ts +18 -10
- package/src/components/layout/search/algolia.ts +11 -2
- package/src/components/layout/search/endpoint.ts +11 -5
- package/src/components/layout/search/orama-cloud.ts +8 -2
- package/src/components/layout/search/pagefind.ts +3 -0
- package/src/components/layout/search/types.ts +5 -1
- package/src/components/layout/search/typesense.ts +4 -1
- package/src/components/layout/toc-element.ts +8 -2
- package/src/components/openapi/ApiTagOperations.astro +2 -1
- package/src/components/openapi/Operation.astro +47 -40
- package/src/components/openapi/RequestPanel.astro +8 -2
- package/src/components/openapi/helpers.ts +71 -3
- package/src/components/openapi/panel.ts +1 -1
- package/src/components/openapi/snippets.ts +25 -11
- package/src/core/base-path.ts +94 -0
- package/src/core/builtin-tags.ts +2 -0
- package/src/core/component-overrides.ts +103 -74
- package/src/core/config-input.ts +118 -17
- package/src/core/config.ts +8 -5
- package/src/core/content.ts +2 -0
- package/src/core/data.ts +4 -0
- package/src/core/diagnostics.ts +54 -34
- package/src/core/gitignore.ts +4 -1
- package/src/core/graph.ts +166 -88
- package/src/core/i18n-ui.ts +63 -3
- package/src/core/last-modified.ts +15 -6
- package/src/core/links.ts +69 -25
- package/src/core/manifest.ts +62 -45
- package/src/core/nav-diagnostics.ts +1 -1
- package/src/core/navigation.ts +144 -58
- package/src/core/package-json.ts +17 -2
- package/src/core/project-graph.ts +25 -15
- package/src/core/schema.ts +605 -620
- package/src/core/sources/assets.ts +6 -1
- package/src/core/sources/filesystem.ts +4 -0
- package/src/core/sources/github-releases.ts +2 -1
- package/src/core/sources/mdx-remote.ts +76 -63
- package/src/core/sources/normalize.ts +236 -91
- package/src/core/sources/notion.ts +27 -18
- package/src/core/sources/types.ts +2 -0
- package/src/core/tsconfig-aliases.ts +59 -30
- package/src/core/types.ts +6 -1
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +1 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +1 -0
- package/src/core/ui-packs/cs.ts +1 -0
- package/src/core/ui-packs/da.ts +1 -0
- package/src/core/ui-packs/de.ts +1 -0
- package/src/core/ui-packs/el.ts +1 -0
- package/src/core/ui-packs/es.ts +1 -0
- package/src/core/ui-packs/fa.ts +1 -0
- package/src/core/ui-packs/fi.ts +1 -0
- package/src/core/ui-packs/fr.ts +2 -1
- package/src/core/ui-packs/he.ts +1 -0
- package/src/core/ui-packs/hi.ts +1 -0
- package/src/core/ui-packs/hr.ts +1 -0
- package/src/core/ui-packs/hu.ts +1 -0
- package/src/core/ui-packs/id.ts +1 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +1 -0
- package/src/core/ui-packs/ko.ts +1 -0
- package/src/core/ui-packs/nl.ts +1 -0
- package/src/core/ui-packs/no.ts +1 -0
- package/src/core/ui-packs/pl.ts +1 -0
- package/src/core/ui-packs/pt-br.ts +1 -0
- package/src/core/ui-packs/pt.ts +1 -0
- package/src/core/ui-packs/ro.ts +1 -0
- package/src/core/ui-packs/ru.ts +1 -0
- package/src/core/ui-packs/sk.ts +1 -0
- package/src/core/ui-packs/sr.ts +1 -0
- package/src/core/ui-packs/sv.ts +1 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +1 -0
- package/src/core/ui-packs/uk.ts +1 -0
- package/src/core/ui-packs/vi.ts +1 -0
- package/src/core/ui-packs/zh-tw.ts +1 -0
- package/src/core/ui-packs/zh.ts +1 -0
- package/src/deploy/adapter-output.ts +18 -8
- package/src/deploy/redirects.ts +25 -2
- package/src/deploy/robots.ts +6 -1
- package/src/deploy/rss.ts +10 -3
- package/src/deploy/sitemap.ts +59 -13
- package/src/index.ts +5 -0
- package/src/markdown/base-links.ts +60 -0
- package/src/markdown/code-title.ts +11 -14
- package/src/markdown/index.ts +46 -9
- package/src/markdown/inline-code.ts +14 -4
- package/src/markdown/package-commands.ts +10 -4
- package/src/markdown/themes.ts +24 -0
- package/src/openapi/model.ts +15 -5
- package/src/openapi/parse.ts +21 -0
- package/src/openapi/references.ts +75 -21
- package/src/openapi/render-mdx.ts +11 -6
- package/src/openapi/scalar.ts +32 -16
- package/src/openapi/source.ts +59 -10
- package/src/registry/eject.ts +247 -19
- package/src/registry/registry.ts +0 -3
- package/src/search/build.ts +3 -0
- package/src/search/documents.ts +36 -4
- package/src/search/sync/typesense.ts +6 -4
- package/src/seo/jsonld.ts +28 -17
- package/src/theme/entry.ts +85 -20
package/src/astro/index.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
generateRuntime,
|
|
3
|
+
prerenderDepsPlugin,
|
|
4
|
+
serverAppResolvePlugin,
|
|
5
|
+
} from "./generate.ts";
|
|
2
6
|
export type { GenerateResult } from "./generate.ts";
|
|
3
7
|
export { blumeIntegration } from "./integration.ts";
|
|
4
8
|
export type { BlumeIntegrationOptions, BlumePageRoute } from "./integration.ts";
|
package/src/astro/integration.ts
CHANGED
|
@@ -30,17 +30,21 @@ const overlayChannel = (): OverlayChannel | undefined =>
|
|
|
30
30
|
* overlay clears itself on the next successful HMR update.
|
|
31
31
|
*/
|
|
32
32
|
export const showBlumeErrorOverlay = (diagnostics: Diagnostic[]): void => {
|
|
33
|
-
const errors =
|
|
34
|
-
|
|
35
|
-
.
|
|
33
|
+
const errors: Diagnostic[] = [];
|
|
34
|
+
for (const diagnostic of diagnostics) {
|
|
35
|
+
if (diagnostic.severity === "error") {
|
|
36
|
+
errors.push(enrichDiagnostic(diagnostic));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
36
39
|
const channel = overlayChannel();
|
|
37
40
|
if (errors.length === 0 || !channel) {
|
|
38
41
|
return;
|
|
39
42
|
}
|
|
40
43
|
const body = errors
|
|
41
44
|
.map((diagnostic) => {
|
|
45
|
+
const lineSuffix = diagnostic.line ? `:${diagnostic.line}` : "";
|
|
42
46
|
const where = diagnostic.file
|
|
43
|
-
? `\n at ${diagnostic.file}${
|
|
47
|
+
? `\n at ${diagnostic.file}${lineSuffix}`
|
|
44
48
|
: "";
|
|
45
49
|
const fix = diagnostic.suggestion
|
|
46
50
|
? `\n fix: ${diagnostic.suggestion}`
|
|
@@ -104,7 +108,7 @@ const negotiateMarkdown =
|
|
|
104
108
|
* Blume's Astro integration. Mounts user-authored pages from `pages/` into the
|
|
105
109
|
* generated runtime via `injectRoute`, keeping each file in its original
|
|
106
110
|
* location so relative imports and `getStaticPaths` keep working, and teaches
|
|
107
|
-
* the dev server to
|
|
111
|
+
* the dev server to honor `Accept: text/markdown`.
|
|
108
112
|
*/
|
|
109
113
|
export const blumeIntegration = (
|
|
110
114
|
options: BlumeIntegrationOptions
|
package/src/astro/islands.ts
CHANGED
|
@@ -97,12 +97,14 @@ export const discoverIslands = async (
|
|
|
97
97
|
const warnings: string[] = [];
|
|
98
98
|
const seen = new Map<string, string>();
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
// Extracted so the skip paths become early `return`s (one `continue` budget
|
|
101
|
+
// per loop under the lint rule) instead of `continue` statements.
|
|
102
|
+
const collectIsland = (file: string, source: string): void => {
|
|
101
103
|
const base = basename(file);
|
|
102
104
|
const ext = base.match(ISLAND_FILE)?.groups?.ext;
|
|
103
105
|
const framework = ext ? FRAMEWORK_BY_EXT[ext] : undefined;
|
|
104
106
|
if (!framework) {
|
|
105
|
-
|
|
107
|
+
return;
|
|
106
108
|
}
|
|
107
109
|
const name = base.replace(ISLAND_FILE, "");
|
|
108
110
|
// The name is used verbatim as both an MDX tag and an unquoted object key
|
|
@@ -113,22 +115,26 @@ export const discoverIslands = async (
|
|
|
113
115
|
warnings.push(
|
|
114
116
|
`Island "${file}" must have a PascalCase identifier filename to be used in MDX (letters, digits, and underscores only, e.g. Counter.tsx → <Counter />); skipping it.`
|
|
115
117
|
);
|
|
116
|
-
|
|
118
|
+
return;
|
|
117
119
|
}
|
|
118
120
|
const existing = seen.get(name);
|
|
119
121
|
if (existing) {
|
|
120
122
|
warnings.push(
|
|
121
123
|
`Two islands both resolve to <${name}> ("${existing}" and "${file}"); ignoring the second. Give them distinct filenames.`
|
|
122
124
|
);
|
|
123
|
-
|
|
125
|
+
return;
|
|
124
126
|
}
|
|
125
127
|
seen.set(name, file);
|
|
126
128
|
islands.push({
|
|
127
|
-
client: readClientMode(
|
|
129
|
+
client: readClientMode(source, file, warnings),
|
|
128
130
|
file,
|
|
129
131
|
framework,
|
|
130
132
|
name,
|
|
131
133
|
});
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
for (const [index, file] of files.entries()) {
|
|
137
|
+
collectIsland(file, sources[index] ?? "");
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
return { islands, warnings };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HTTP content negotiation for the raw-Markdown variants. The `<route>.md`
|
|
3
3
|
* endpoints already serve a page's source verbatim; these helpers let the dev
|
|
4
|
-
* server
|
|
4
|
+
* server honor `Accept: text/markdown` by transparently rewriting a page
|
|
5
5
|
* request to its `.md` variant.
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -18,7 +18,7 @@ const parseAccept = (accept: string): AcceptEntry[] =>
|
|
|
18
18
|
.slice(1)
|
|
19
19
|
.map((segment) => segment.trim())
|
|
20
20
|
.find((segment) => segment.startsWith("q="));
|
|
21
|
-
const q = qSegment ? Number
|
|
21
|
+
const q = qSegment ? Number(qSegment.slice(2)) : 1;
|
|
22
22
|
return { q: Number.isNaN(q) ? 1 : q, type };
|
|
23
23
|
});
|
|
24
24
|
|
package/src/astro/pages.ts
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { extname, relative } from "pathe";
|
|
2
|
-
import { glob } from "tinyglobby";
|
|
2
|
+
import { glob, globSync } from "tinyglobby";
|
|
3
3
|
|
|
4
|
+
import type { BlumeProject } from "../core/project-graph.ts";
|
|
4
5
|
import type { BlumePageRoute } from "./integration.ts";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
* Discover user `.astro` pages and map them to route patterns. Files keep their
|
|
8
|
-
* original location; only the route pattern is derived (index -> parent,
|
|
9
|
-
* dynamic `[param]` segments preserved).
|
|
10
|
-
*/
|
|
11
|
-
export const discoverPages = async (
|
|
12
|
-
pagesRoot: string
|
|
13
|
-
): Promise<BlumePageRoute[]> => {
|
|
14
|
-
const files = await glob(["**/*.astro"], {
|
|
15
|
-
absolute: true,
|
|
16
|
-
cwd: pagesRoot,
|
|
17
|
-
onlyFiles: true,
|
|
18
|
-
});
|
|
19
|
-
files.sort();
|
|
7
|
+
const PAGE_GLOB = ["**/*.astro"];
|
|
20
8
|
|
|
9
|
+
/** Map discovered page files to routes; shared by the async/sync discoverers. */
|
|
10
|
+
const toPageRoutes = (pagesRoot: string, files: string[]): BlumePageRoute[] => {
|
|
11
|
+
files.sort();
|
|
21
12
|
return files.map((file) => {
|
|
22
13
|
const rel = relative(pagesRoot, file);
|
|
23
14
|
const withoutExt = rel.slice(0, rel.length - extname(rel).length);
|
|
@@ -32,6 +23,26 @@ export const discoverPages = async (
|
|
|
32
23
|
});
|
|
33
24
|
};
|
|
34
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Discover user `.astro` pages and map them to route patterns. Files keep their
|
|
28
|
+
* original location; only the route pattern is derived (index -> parent,
|
|
29
|
+
* dynamic `[param]` segments preserved).
|
|
30
|
+
*/
|
|
31
|
+
export const discoverPages = async (
|
|
32
|
+
pagesRoot: string
|
|
33
|
+
): Promise<BlumePageRoute[]> =>
|
|
34
|
+
toPageRoutes(
|
|
35
|
+
pagesRoot,
|
|
36
|
+
await glob(PAGE_GLOB, { absolute: true, cwd: pagesRoot, onlyFiles: true })
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/** {@link discoverPages} for synchronous callers (e.g. the sitemap builder). */
|
|
40
|
+
export const discoverPagesSync = (pagesRoot: string): BlumePageRoute[] =>
|
|
41
|
+
toPageRoutes(
|
|
42
|
+
pagesRoot,
|
|
43
|
+
globSync(PAGE_GLOB, { absolute: true, cwd: pagesRoot, onlyFiles: true })
|
|
44
|
+
);
|
|
45
|
+
|
|
35
46
|
/**
|
|
36
47
|
* Whether the project already owns `route` — through a custom `.astro` page
|
|
37
48
|
* (injected, so matched on `pattern`) or a content page (matched on `route`).
|
|
@@ -57,6 +68,59 @@ export interface OgCustomRoute {
|
|
|
57
68
|
/** Skip private (`_partial`, `.well-known`) and Astro dynamic (`[param]`) parts. */
|
|
58
69
|
const PRIVATE_SEGMENT = /^[._]/u;
|
|
59
70
|
|
|
71
|
+
/** Segments of a static, shareable page pattern; null for dynamic/private ones. */
|
|
72
|
+
const staticSegments = (pattern: string): string[] | null => {
|
|
73
|
+
const segments = pattern.split("/").filter(Boolean);
|
|
74
|
+
return segments.some(
|
|
75
|
+
(part) => PRIVATE_SEGMENT.test(part) || part.includes("[")
|
|
76
|
+
)
|
|
77
|
+
? null
|
|
78
|
+
: segments;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The static routes served by custom `.astro` pages — the same filtering as
|
|
83
|
+
* {@link customOgRoutes}, but yielding the routes themselves. Feeds the route
|
|
84
|
+
* sets that must know every servable page beyond the content graph (the link
|
|
85
|
+
* checker, the sitemap); dynamic (`[param]`) and private segments are skipped
|
|
86
|
+
* because their concrete URLs can't be enumerated statically.
|
|
87
|
+
*/
|
|
88
|
+
export const customStaticRoutes = (pages: { pattern: string }[]): string[] => {
|
|
89
|
+
const routes = new Set<string>();
|
|
90
|
+
for (const { pattern } of pages) {
|
|
91
|
+
const segments = staticSegments(pattern);
|
|
92
|
+
if (segments !== null) {
|
|
93
|
+
routes.add(segments.length === 0 ? "/" : `/${segments.join("/")}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return [...routes];
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Whether the generated `/changelog` index route exists for this project —
|
|
101
|
+
* `generate.ts` (which writes the page) and the sitemap/link validator all
|
|
102
|
+
* share this check: there are visible `type: changelog` entries — or a
|
|
103
|
+
* release-backed changelog source, whose route must resolve even when a fetch
|
|
104
|
+
* fails — and no user content or custom page already owns `/changelog`.
|
|
105
|
+
*/
|
|
106
|
+
export const hasGeneratedChangelog = (
|
|
107
|
+
project: BlumeProject,
|
|
108
|
+
userPages: { pattern: string }[]
|
|
109
|
+
): boolean => {
|
|
110
|
+
const hasChangelog = project.graph.pages.some(
|
|
111
|
+
(page) =>
|
|
112
|
+
page.contentType === "changelog" &&
|
|
113
|
+
!(page.meta.draft || page.meta.sidebar.hidden)
|
|
114
|
+
);
|
|
115
|
+
const hasChangelogSource = (project.config.content.sources ?? []).some(
|
|
116
|
+
(source) => source.type === "github-releases"
|
|
117
|
+
);
|
|
118
|
+
return (
|
|
119
|
+
(hasChangelog || hasChangelogSource) &&
|
|
120
|
+
!routeIsTaken(userPages, project.graph.pages, "/changelog")
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
60
124
|
const humanizeSegment = (segment: string): string =>
|
|
61
125
|
segment
|
|
62
126
|
.split(/[-_]/u)
|
|
@@ -80,20 +144,23 @@ export const customOgRoutes = (
|
|
|
80
144
|
): OgCustomRoute[] => {
|
|
81
145
|
const seen = new Set<string>();
|
|
82
146
|
const routes: OgCustomRoute[] = [];
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
) {
|
|
88
|
-
|
|
147
|
+
// Extracted so the skip paths become early `return`s (one `continue` budget
|
|
148
|
+
// per loop under the lint rule) instead of `continue` statements.
|
|
149
|
+
const collectRoute = (pattern: string): void => {
|
|
150
|
+
const segments = staticSegments(pattern);
|
|
151
|
+
if (segments === null) {
|
|
152
|
+
return;
|
|
89
153
|
}
|
|
90
154
|
const slug = segments.length === 0 ? "index" : segments.join("/");
|
|
91
155
|
if (seen.has(slug)) {
|
|
92
|
-
|
|
156
|
+
return;
|
|
93
157
|
}
|
|
94
158
|
seen.add(slug);
|
|
95
159
|
const last = segments.at(-1);
|
|
96
160
|
routes.push({ slug, title: last ? humanizeSegment(last) : siteTitle });
|
|
161
|
+
};
|
|
162
|
+
for (const { pattern } of pages) {
|
|
163
|
+
collectRoute(pattern);
|
|
97
164
|
}
|
|
98
165
|
return routes;
|
|
99
166
|
};
|