blume 0.3.0 → 0.5.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/dist/cli/index.js +1631 -940
- package/dist/cli/index.js.map +62 -50
- package/dist/types/core/data.d.ts +2 -0
- package/dist/types/core/project.d.ts +12 -2
- package/dist/types/core/schema.d.ts +442 -292
- package/dist/types/core/types.d.ts +7 -0
- package/dist/types/migrate/mintlify/assets.d.ts +8 -0
- package/docs/01-quickstart.mdx +5 -16
- package/docs/02-deployment.mdx +21 -54
- package/docs/advanced/api-reference.mdx +34 -51
- package/docs/advanced/blog.mdx +9 -25
- package/docs/advanced/bridge.mdx +74 -0
- package/docs/advanced/changelog.mdx +10 -33
- package/docs/advanced/custom-pages.mdx +21 -78
- package/docs/advanced/meta.ts +8 -1
- package/docs/advanced/migrate.mdx +119 -0
- package/docs/configuration/ai.mdx +42 -103
- package/docs/configuration/analytics.mdx +20 -38
- package/docs/configuration/customization.mdx +40 -73
- package/docs/configuration/export.mdx +9 -34
- package/docs/configuration/index.mdx +67 -87
- package/docs/configuration/search.mdx +17 -54
- package/docs/configuration/seo.mdx +17 -48
- package/docs/configuration/theming.mdx +20 -42
- package/docs/content/components.mdx +95 -101
- package/docs/content/i18n.mdx +21 -72
- package/docs/content/index.mdx +18 -48
- package/docs/content/islands.mdx +25 -52
- package/docs/content/meta.mdx +23 -50
- package/docs/content/navigation.mdx +23 -62
- package/docs/content/sources.mdx +20 -83
- package/docs/content/syntax.mdx +37 -105
- package/docs/index.mdx +12 -41
- package/docs/reference/cli.mdx +47 -30
- package/docs/reference/frontmatter.mdx +7 -5
- package/package.json +11 -1
- package/src/astro/generate.ts +18 -8
- package/src/astro/integration.ts +26 -3
- package/src/astro/islands.ts +6 -2
- package/src/astro/markdown-negotiation.ts +17 -3
- package/src/astro/pages.ts +6 -1
- package/src/astro/static-assets.ts +117 -0
- package/src/astro/templates.ts +76 -30
- package/src/cli/args.ts +23 -0
- package/src/cli/commands/build.ts +129 -62
- package/src/cli/commands/check.ts +20 -0
- package/src/cli/commands/dev.ts +11 -2
- package/src/cli/commands/doctor.ts +10 -1
- package/src/cli/commands/eject.ts +3 -1
- package/src/cli/commands/init.ts +21 -1
- package/src/cli/commands/preview.ts +2 -1
- package/src/cli/commands/validate.ts +12 -1
- package/src/cli/dev-lock.ts +92 -0
- package/src/cli/log.ts +11 -0
- package/src/cli/prepare.ts +3 -0
- package/src/components/BlumePage.astro +8 -0
- package/src/components/Icon.astro +13 -10
- package/src/components/content/ApiField.astro +75 -0
- package/src/components/content/ParamField.astro +39 -0
- package/src/components/content/RequestField.astro +23 -0
- package/src/components/content/ResponseField.astro +23 -0
- package/src/components/content/Step.astro +1 -1
- package/src/components/content/YouTube.astro +35 -0
- package/src/components/content/youtube.ts +46 -0
- package/src/components/islands/ask-ai.tsx +14 -14
- package/src/components/layout/Breadcrumbs.astro +7 -2
- package/src/components/layout/NavTree.astro +24 -8
- package/src/components/layout/RootLayout.astro +56 -34
- package/src/components/layout/Search.astro +1 -1
- package/src/components/openapi/ApiOverview.astro +84 -0
- package/src/components/openapi/MethodBadge.astro +28 -0
- package/src/components/openapi/Operation.astro +140 -0
- package/src/components/openapi/ParametersTable.astro +97 -0
- package/src/components/openapi/RequestBody.astro +58 -0
- package/src/components/openapi/RequestPanel.astro +169 -0
- package/src/components/openapi/Responses.astro +91 -0
- package/src/components/openapi/SchemaProperty.astro +118 -0
- package/src/components/openapi/SchemaTable.astro +86 -0
- package/src/components/openapi/helpers.ts +238 -0
- package/src/components/openapi/panel.ts +59 -0
- package/src/components/openapi/snippets.ts +201 -0
- package/src/components/props.ts +3 -0
- package/src/core/assets.ts +31 -0
- package/src/core/bridge.ts +10 -0
- package/src/core/builtin-tags.ts +6 -0
- package/src/core/data.ts +2 -0
- package/src/core/diagnostics.ts +6 -1
- package/src/core/gitignore.ts +30 -0
- package/src/core/links.ts +60 -19
- package/src/core/project-graph.ts +5 -1
- package/src/core/project.ts +25 -3
- package/src/core/schema.ts +54 -6
- package/src/core/sources/mdx-remote.ts +54 -8
- package/src/core/sources/mintlify.ts +1 -1
- package/src/core/sources/normalize.ts +6 -1
- package/src/core/sources/notion.ts +49 -5
- package/src/core/sources/resolve.ts +28 -6
- package/src/core/sources/sanity.ts +5 -1
- package/src/core/types.ts +7 -0
- package/src/deploy/rss.ts +1 -8
- package/src/deploy/sitemap.ts +20 -1
- package/src/deploy/xml.ts +8 -0
- package/src/markdown/directives.ts +15 -7
- package/src/markdown/package-commands.ts +26 -4
- package/src/migrate/fumadocs/content.ts +14 -1
- package/src/migrate/fumadocs/groups.ts +7 -0
- package/src/migrate/fumadocs/index.ts +5 -2
- package/src/migrate/mintlify/assets.ts +46 -0
- package/src/migrate/mintlify/config.ts +153 -1
- package/src/migrate/mintlify/content.ts +8 -2
- package/src/migrate/mintlify/index.ts +111 -46
- package/src/migrate/shared.ts +12 -27
- package/src/og/card.ts +14 -2
- package/src/openapi/model.ts +174 -0
- package/src/openapi/parse.ts +48 -0
- package/src/openapi/references.ts +164 -0
- package/src/openapi/render-mdx.ts +76 -0
- package/src/openapi/scalar.ts +15 -103
- package/src/openapi/source.ts +140 -0
- package/src/registry/eject.ts +28 -5
- package/src/registry/registry.ts +6 -0
- package/src/registry/rewrite-imports.ts +31 -19
- package/src/search/documents.ts +23 -5
- package/src/search/sync/algolia.ts +5 -1
- package/src/search/sync/typesense.ts +24 -16
- package/src/theme/chrome-icons.ts +22 -0
- package/src/theme/icons.ts +151 -161
- package/src/theme/palette.ts +26 -7
package/dist/cli/index.js
CHANGED
|
@@ -209,6 +209,12 @@ var CONTENT_COMPONENTS = [
|
|
|
209
209
|
file: "Prompt.astro",
|
|
210
210
|
name: "prompt",
|
|
211
211
|
tag: "Prompt"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
description: "A responsive, privacy-friendly YouTube embed.",
|
|
215
|
+
file: "YouTube.astro",
|
|
216
|
+
name: "youtube",
|
|
217
|
+
tag: "YouTube"
|
|
212
218
|
}
|
|
213
219
|
];
|
|
214
220
|
var registry = [
|
|
@@ -254,18 +260,22 @@ var findItem = (name) => registry.find((item) => item.name === name);
|
|
|
254
260
|
|
|
255
261
|
// src/registry/rewrite-imports.ts
|
|
256
262
|
import { dirname as dirname2, relative, resolve } from "pathe";
|
|
257
|
-
var
|
|
258
|
-
var
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
263
|
+
var FROM_IMPORT = /(?<prefix>^[ \t]*(?:import|export)\b[^;]*?\bfrom[ \t]*)(?<quote>["'])(?<spec>\.[^"']*)\k<quote>/gmu;
|
|
264
|
+
var SIDE_EFFECT_IMPORT = /(?<prefix>^[ \t]*import[ \t]+)(?<quote>["'])(?<spec>\.[^"']*)\k<quote>/gmu;
|
|
265
|
+
var rewriteImports = (content, sourceFile, srcRoot) => {
|
|
266
|
+
const rewrite = (match, prefix, quote, spec) => {
|
|
267
|
+
const resolved = resolve(dirname2(sourceFile), spec);
|
|
268
|
+
if (resolved === sourceFile) {
|
|
269
|
+
return match;
|
|
270
|
+
}
|
|
271
|
+
const rel = relative(srcRoot, resolved);
|
|
272
|
+
if (rel.startsWith("..")) {
|
|
273
|
+
return match;
|
|
274
|
+
}
|
|
275
|
+
return `${prefix}${quote}blume/${rel}${quote}`;
|
|
276
|
+
};
|
|
277
|
+
return content.replaceAll(FROM_IMPORT, rewrite).replaceAll(SIDE_EFFECT_IMPORT, rewrite);
|
|
278
|
+
};
|
|
269
279
|
|
|
270
280
|
// src/cli/log.ts
|
|
271
281
|
import { consola } from "consola";
|
|
@@ -326,7 +336,7 @@ var locatePath = (source, path) => {
|
|
|
326
336
|
if (typeof segment !== "string") {
|
|
327
337
|
continue;
|
|
328
338
|
}
|
|
329
|
-
const matcher = new RegExp(
|
|
339
|
+
const matcher = new RegExp(`(?<![\\w$])${escapeRegExp(segment)}\\s*[:=]`, "gu");
|
|
330
340
|
matcher.lastIndex = cursor;
|
|
331
341
|
const match = matcher.exec(source);
|
|
332
342
|
if (!match) {
|
|
@@ -408,6 +418,9 @@ var countBySeverity = (diagnostics) => {
|
|
|
408
418
|
|
|
409
419
|
// src/cli/log.ts
|
|
410
420
|
var logger = consola.withTag("blume");
|
|
421
|
+
var flushStdout = () => new Promise((resolve2) => {
|
|
422
|
+
process.stdout.write("", () => resolve2());
|
|
423
|
+
});
|
|
411
424
|
var reportDiagnosticsJson = (diagnostics, root) => {
|
|
412
425
|
const enriched = diagnostics.map((diagnostic) => {
|
|
413
426
|
const withDocs = enrichDiagnostic(diagnostic);
|
|
@@ -500,11 +513,11 @@ Next steps:
|
|
|
500
513
|
});
|
|
501
514
|
|
|
502
515
|
// src/cli/commands/build.ts
|
|
503
|
-
import { existsSync as
|
|
504
|
-
import { readdir, stat, writeFile as
|
|
516
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
517
|
+
import { readdir, stat, writeFile as writeFile7 } from "node:fs/promises";
|
|
505
518
|
import { build } from "astro";
|
|
506
519
|
import { defineCommand as defineCommand2 } from "citty";
|
|
507
|
-
import { join as
|
|
520
|
+
import { join as join28 } from "pathe";
|
|
508
521
|
|
|
509
522
|
// src/core/frontmatter.ts
|
|
510
523
|
import baseMatter from "gray-matter";
|
|
@@ -595,6 +608,29 @@ var buildLlmsFiles = async (project) => ({
|
|
|
595
608
|
index: buildIndex(project)
|
|
596
609
|
});
|
|
597
610
|
|
|
611
|
+
// src/core/gitignore.ts
|
|
612
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
613
|
+
import { readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
614
|
+
import { join as join5 } from "pathe";
|
|
615
|
+
var gitignoreKey = (line) => line.trim().replace(/\/+$/u, "");
|
|
616
|
+
var ensureGitignore = async (root, entries) => {
|
|
617
|
+
const path = join5(root, ".gitignore");
|
|
618
|
+
const existing = existsSync3(path) ? await readFile3(path, "utf-8") : "";
|
|
619
|
+
const present = new Set(existing.split(`
|
|
620
|
+
`).map(gitignoreKey).filter(Boolean));
|
|
621
|
+
const added = entries.filter((entry) => !present.has(gitignoreKey(entry)));
|
|
622
|
+
if (added.length === 0) {
|
|
623
|
+
return [];
|
|
624
|
+
}
|
|
625
|
+
const gap = existing.length > 0 && !existing.endsWith(`
|
|
626
|
+
`) ? `
|
|
627
|
+
` : "";
|
|
628
|
+
await writeFile2(path, `${existing}${gap}${added.join(`
|
|
629
|
+
`)}
|
|
630
|
+
`, "utf-8");
|
|
631
|
+
return added;
|
|
632
|
+
};
|
|
633
|
+
|
|
598
634
|
// src/search/providers.ts
|
|
599
635
|
var SEARCH_PROVIDERS = {
|
|
600
636
|
algolia: {
|
|
@@ -699,14 +735,24 @@ var buildRobots = (project) => {
|
|
|
699
735
|
`;
|
|
700
736
|
};
|
|
701
737
|
|
|
738
|
+
// src/deploy/xml.ts
|
|
739
|
+
var escapeXml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
740
|
+
|
|
702
741
|
// src/deploy/sitemap.ts
|
|
742
|
+
var lastmodTag = (value) => {
|
|
743
|
+
if (!value) {
|
|
744
|
+
return "";
|
|
745
|
+
}
|
|
746
|
+
const date = new Date(value);
|
|
747
|
+
return Number.isNaN(date.getTime()) ? "" : `<lastmod>${date.toISOString().slice(0, 10)}</lastmod>`;
|
|
748
|
+
};
|
|
703
749
|
var buildSitemap = (project) => {
|
|
704
750
|
const { site } = project.config.deployment;
|
|
705
751
|
if (!(site && project.config.seo.sitemap)) {
|
|
706
752
|
return null;
|
|
707
753
|
}
|
|
708
754
|
const base = site.replace(/\/$/u, "");
|
|
709
|
-
const urls = project.graph.pages.filter((page) => !(page.meta.draft || page.meta.sidebar.hidden || page.meta.seo.noindex)).map((page) => ` <url><loc>${base}${page.route}</loc
|
|
755
|
+
const urls = project.graph.pages.filter((page) => !(page.meta.draft || page.meta.sidebar.hidden || page.meta.seo.noindex)).map((page) => ` <url><loc>${escapeXml(encodeURI(`${base}${page.route}`))}</loc>${lastmodTag(page.lastModified)}</url>`).toSorted();
|
|
710
756
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
711
757
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
712
758
|
${urls.join(`
|
|
@@ -716,7 +762,7 @@ ${urls.join(`
|
|
|
716
762
|
};
|
|
717
763
|
|
|
718
764
|
// src/search/build.ts
|
|
719
|
-
import { join as
|
|
765
|
+
import { join as join6 } from "pathe";
|
|
720
766
|
var buildSearchIndex = async (outDir) => {
|
|
721
767
|
const pagefind = await import("pagefind");
|
|
722
768
|
const { index } = await pagefind.createIndex({});
|
|
@@ -724,7 +770,7 @@ var buildSearchIndex = async (outDir) => {
|
|
|
724
770
|
throw new Error("Failed to create Pagefind index.");
|
|
725
771
|
}
|
|
726
772
|
const result = await index.addDirectory({ path: outDir });
|
|
727
|
-
await index.writeFiles({ outputPath:
|
|
773
|
+
await index.writeFiles({ outputPath: join6(outDir, "pagefind") });
|
|
728
774
|
await pagefind.close();
|
|
729
775
|
return result.page_count;
|
|
730
776
|
};
|
|
@@ -2580,7 +2626,19 @@ var LINK = /\[(?<text>[^\]]*)\]\([^)]*\)/gu;
|
|
|
2580
2626
|
var HEADING_MARK = /^#{1,6}\s+/gmu;
|
|
2581
2627
|
var MARKDOWN_PUNCT = /[*_~>]+/gu;
|
|
2582
2628
|
var WHITESPACE = /\s+/gu;
|
|
2583
|
-
var toPlainText = (markdown) =>
|
|
2629
|
+
var toPlainText = (markdown) => {
|
|
2630
|
+
const withoutBlocks = markdown.replaceAll(CODE_FENCE, " ").replaceAll(IMAGE, " ").replaceAll(LINK, "$<text>");
|
|
2631
|
+
const pieces = [];
|
|
2632
|
+
let cursor = 0;
|
|
2633
|
+
for (const match of withoutBlocks.matchAll(INLINE_CODE)) {
|
|
2634
|
+
const start = match.index ?? 0;
|
|
2635
|
+
pieces.push(withoutBlocks.slice(cursor, start).replaceAll(HTML_OR_JSX, " "));
|
|
2636
|
+
pieces.push(match.groups?.code ?? "");
|
|
2637
|
+
cursor = start + match[0].length;
|
|
2638
|
+
}
|
|
2639
|
+
pieces.push(withoutBlocks.slice(cursor).replaceAll(HTML_OR_JSX, " "));
|
|
2640
|
+
return pieces.join("").replaceAll(HEADING_MARK, "").replaceAll(MARKDOWN_PUNCT, " ").replaceAll(WHITESPACE, " ").trim();
|
|
2641
|
+
};
|
|
2584
2642
|
var buildCrumbIndex = (sidebar) => {
|
|
2585
2643
|
const index = new Map;
|
|
2586
2644
|
const walk = (nodes, trail) => {
|
|
@@ -2654,7 +2712,7 @@ var syncAlgolia = async (records, config) => {
|
|
|
2654
2712
|
}
|
|
2655
2713
|
const { algoliasearch } = await import("algoliasearch");
|
|
2656
2714
|
const client = algoliasearch(config.appId, adminKey);
|
|
2657
|
-
await client.
|
|
2715
|
+
await client.replaceAllObjects({
|
|
2658
2716
|
indexName: config.indexName,
|
|
2659
2717
|
objects: records.map((record) => ({ ...record, objectID: record._id }))
|
|
2660
2718
|
});
|
|
@@ -2706,20 +2764,21 @@ var syncTypesense = async (records, config) => {
|
|
|
2706
2764
|
}
|
|
2707
2765
|
]
|
|
2708
2766
|
});
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
await
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
name:
|
|
2721
|
-
|
|
2722
|
-
|
|
2767
|
+
const collection = client.collections(config.collection);
|
|
2768
|
+
const exists = await collection.retrieve().then(() => true).catch(() => false);
|
|
2769
|
+
if (exists) {
|
|
2770
|
+
await collection.delete();
|
|
2771
|
+
}
|
|
2772
|
+
await client.collections().create({
|
|
2773
|
+
fields: [
|
|
2774
|
+
{ name: "title", type: "string" },
|
|
2775
|
+
{ name: "description", optional: true, type: "string" },
|
|
2776
|
+
{ name: "content", type: "string" },
|
|
2777
|
+
{ name: "url", type: "string" },
|
|
2778
|
+
{ facet: true, name: "tag", optional: true, type: "string" }
|
|
2779
|
+
],
|
|
2780
|
+
name: config.collection
|
|
2781
|
+
});
|
|
2723
2782
|
const documents = records.map((record) => ({
|
|
2724
2783
|
content: record.content,
|
|
2725
2784
|
description: record.description,
|
|
@@ -2753,20 +2812,112 @@ var syncSearchProvider = async (project, reporter) => {
|
|
|
2753
2812
|
}
|
|
2754
2813
|
};
|
|
2755
2814
|
|
|
2815
|
+
// src/cli/dev-lock.ts
|
|
2816
|
+
import {
|
|
2817
|
+
existsSync as existsSync5,
|
|
2818
|
+
mkdirSync,
|
|
2819
|
+
readFileSync as readFileSync2,
|
|
2820
|
+
rmSync,
|
|
2821
|
+
writeFileSync
|
|
2822
|
+
} from "node:fs";
|
|
2823
|
+
import { join as join8 } from "pathe";
|
|
2824
|
+
|
|
2825
|
+
// src/core/project.ts
|
|
2826
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
2827
|
+
import { isAbsolute, join as join7, resolve as resolve2 } from "pathe";
|
|
2828
|
+
var CONFIG_FILENAMES = [
|
|
2829
|
+
"blume.config.ts",
|
|
2830
|
+
"blume.config.mjs",
|
|
2831
|
+
"blume.config.js"
|
|
2832
|
+
];
|
|
2833
|
+
var THEME_FILENAMES = ["theme.css"];
|
|
2834
|
+
var COMPONENTS_FILENAMES = ["components.tsx", "components.ts"];
|
|
2835
|
+
var firstExisting = (root, names) => {
|
|
2836
|
+
for (const name of names) {
|
|
2837
|
+
const candidate = join7(root, name);
|
|
2838
|
+
if (existsSync4(candidate)) {
|
|
2839
|
+
return candidate;
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
return null;
|
|
2843
|
+
};
|
|
2844
|
+
var findConfigFile = (root) => firstExisting(root, CONFIG_FILENAMES);
|
|
2845
|
+
var resolveRuntimeDir = (root, runtimeDir = ".blume") => isAbsolute(runtimeDir) ? runtimeDir : join7(resolve2(root), runtimeDir);
|
|
2846
|
+
var resolveProjectContext = (root, config, options) => {
|
|
2847
|
+
const absoluteRoot = resolve2(root);
|
|
2848
|
+
const contentRoot = isAbsolute(config.content.root) ? config.content.root : join7(absoluteRoot, config.content.root);
|
|
2849
|
+
const pagesPath = join7(absoluteRoot, config.content.pages);
|
|
2850
|
+
const pagesRoot = existsSync4(pagesPath) ? pagesPath : null;
|
|
2851
|
+
const outDir = resolveRuntimeDir(absoluteRoot, options?.runtimeDir);
|
|
2852
|
+
const distDir = options?.runtimeDir ? join7(outDir, "dist") : join7(absoluteRoot, "dist");
|
|
2853
|
+
return {
|
|
2854
|
+
componentsFile: firstExisting(absoluteRoot, COMPONENTS_FILENAMES),
|
|
2855
|
+
configFile: findConfigFile(absoluteRoot),
|
|
2856
|
+
contentRoot,
|
|
2857
|
+
distDir,
|
|
2858
|
+
outDir,
|
|
2859
|
+
pagesRoot,
|
|
2860
|
+
root: absoluteRoot,
|
|
2861
|
+
themeFile: firstExisting(absoluteRoot, THEME_FILENAMES)
|
|
2862
|
+
};
|
|
2863
|
+
};
|
|
2864
|
+
|
|
2865
|
+
// src/cli/dev-lock.ts
|
|
2866
|
+
var lockPath = (outDir) => join8(outDir, "dev.lock");
|
|
2867
|
+
var isDevLocked = (outDir) => {
|
|
2868
|
+
const path = lockPath(outDir);
|
|
2869
|
+
if (!existsSync5(path)) {
|
|
2870
|
+
return false;
|
|
2871
|
+
}
|
|
2872
|
+
const pid = Number.parseInt(readFileSync2(path, "utf-8").trim(), 10);
|
|
2873
|
+
if (!(Number.isInteger(pid) && pid > 0)) {
|
|
2874
|
+
return false;
|
|
2875
|
+
}
|
|
2876
|
+
try {
|
|
2877
|
+
process.kill(pid, 0);
|
|
2878
|
+
return true;
|
|
2879
|
+
} catch {
|
|
2880
|
+
return false;
|
|
2881
|
+
}
|
|
2882
|
+
};
|
|
2883
|
+
var acquireDevLock = (outDir) => {
|
|
2884
|
+
const path = lockPath(outDir);
|
|
2885
|
+
mkdirSync(outDir, { recursive: true });
|
|
2886
|
+
writeFileSync(path, String(process.pid));
|
|
2887
|
+
let released = false;
|
|
2888
|
+
return () => {
|
|
2889
|
+
if (released) {
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2892
|
+
released = true;
|
|
2893
|
+
try {
|
|
2894
|
+
if (existsSync5(path) && readFileSync2(path, "utf-8").trim() === String(process.pid)) {
|
|
2895
|
+
rmSync(path, { force: true });
|
|
2896
|
+
}
|
|
2897
|
+
} catch {}
|
|
2898
|
+
};
|
|
2899
|
+
};
|
|
2900
|
+
var refuseIfDevRunning = (root, action, runtimeDir) => {
|
|
2901
|
+
if (isDevLocked(resolveRuntimeDir(root, runtimeDir))) {
|
|
2902
|
+
logger.error(`A \`blume dev\` server is running against .blume; ${action} would corrupt it. Stop the dev server, or re-run with --isolated to build/verify against .blume-verify without touching it.`);
|
|
2903
|
+
process.exit(1);
|
|
2904
|
+
}
|
|
2905
|
+
};
|
|
2906
|
+
|
|
2756
2907
|
// src/astro/generate.ts
|
|
2757
|
-
import { existsSync as
|
|
2908
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, realpathSync } from "node:fs";
|
|
2758
2909
|
import {
|
|
2759
2910
|
lstat,
|
|
2760
|
-
mkdir as
|
|
2761
|
-
readFile as
|
|
2911
|
+
mkdir as mkdir3,
|
|
2912
|
+
readFile as readFile10,
|
|
2762
2913
|
rename,
|
|
2763
2914
|
rm,
|
|
2764
2915
|
symlink,
|
|
2765
|
-
writeFile as
|
|
2916
|
+
writeFile as writeFile4
|
|
2766
2917
|
} from "node:fs/promises";
|
|
2767
|
-
import { createRequire as
|
|
2918
|
+
import { createRequire as createRequire5 } from "node:module";
|
|
2768
2919
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
2769
|
-
import { basename as basename2, dirname as dirname7, join as
|
|
2920
|
+
import { basename as basename2, dirname as dirname7, join as join17, normalize as normalize3, relative as relative6 } from "pathe";
|
|
2770
2921
|
import { glob as glob4 } from "tinyglobby";
|
|
2771
2922
|
|
|
2772
2923
|
// src/ai/ask-data.ts
|
|
@@ -2838,7 +2989,7 @@ var askBackendRuntimeDep = (ask) => {
|
|
|
2838
2989
|
};
|
|
2839
2990
|
|
|
2840
2991
|
// src/ai/markdown.ts
|
|
2841
|
-
import { readFile as
|
|
2992
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
2842
2993
|
var buildRawMarkdown = async (project) => {
|
|
2843
2994
|
const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
|
|
2844
2995
|
const readRoute = async (route) => {
|
|
@@ -2846,7 +2997,7 @@ var buildRawMarkdown = async (project) => {
|
|
|
2846
2997
|
if (page) {
|
|
2847
2998
|
return await readEntryText(project, page);
|
|
2848
2999
|
}
|
|
2849
|
-
return route.sourcePath ? await
|
|
3000
|
+
return route.sourcePath ? await readFile4(route.sourcePath, "utf-8") : "";
|
|
2850
3001
|
};
|
|
2851
3002
|
const entries = await Promise.all(project.manifest.routes.map(async (route) => [route.path, await readRoute(route)]));
|
|
2852
3003
|
return Object.fromEntries(entries);
|
|
@@ -2943,6 +3094,7 @@ var buildMcpServerCard = (input) => ({
|
|
|
2943
3094
|
var BUILTIN_MDX_TAGS = new Set([
|
|
2944
3095
|
"Accordion",
|
|
2945
3096
|
"AccordionItem",
|
|
3097
|
+
"ApiOverview",
|
|
2946
3098
|
"AutoTypeTable",
|
|
2947
3099
|
"Badge",
|
|
2948
3100
|
"Callout",
|
|
@@ -2961,8 +3113,12 @@ var BUILTIN_MDX_TAGS = new Set([
|
|
|
2961
3113
|
"GithubInfo",
|
|
2962
3114
|
"Icon",
|
|
2963
3115
|
"Math",
|
|
3116
|
+
"Operation",
|
|
2964
3117
|
"Panel",
|
|
3118
|
+
"ParamField",
|
|
2965
3119
|
"Prompt",
|
|
3120
|
+
"RequestField",
|
|
3121
|
+
"ResponseField",
|
|
2966
3122
|
"Step",
|
|
2967
3123
|
"Steps",
|
|
2968
3124
|
"Tab",
|
|
@@ -2971,7 +3127,8 @@ var BUILTIN_MDX_TAGS = new Set([
|
|
|
2971
3127
|
"Tooltip",
|
|
2972
3128
|
"Tree",
|
|
2973
3129
|
"TypeTable",
|
|
2974
|
-
"Visibility"
|
|
3130
|
+
"Visibility",
|
|
3131
|
+
"YouTube"
|
|
2975
3132
|
]);
|
|
2976
3133
|
|
|
2977
3134
|
// src/core/component-diagnostics.ts
|
|
@@ -3000,8 +3157,8 @@ var validateUsedComponents = (pages, extraTags, registryNames) => {
|
|
|
3000
3157
|
};
|
|
3001
3158
|
|
|
3002
3159
|
// src/core/component-overrides.ts
|
|
3003
|
-
import { existsSync as
|
|
3004
|
-
import { dirname as dirname4, extname, isAbsolute, resolve as
|
|
3160
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
3161
|
+
import { dirname as dirname4, extname, isAbsolute as isAbsolute2, resolve as resolve3 } from "pathe";
|
|
3005
3162
|
import ts from "typescript";
|
|
3006
3163
|
var GROUPS = ["mdx", "layout", "islands"];
|
|
3007
3164
|
var FRAMEWORK_BY_EXT = {
|
|
@@ -3089,18 +3246,18 @@ var findDefaultExportObject = (sourceFile) => {
|
|
|
3089
3246
|
var probeExtension = (base) => {
|
|
3090
3247
|
for (const extension of COMPONENT_EXTS) {
|
|
3091
3248
|
const candidate = `${base}.${extension}`;
|
|
3092
|
-
if (
|
|
3249
|
+
if (existsSync6(candidate)) {
|
|
3093
3250
|
return candidate;
|
|
3094
3251
|
}
|
|
3095
3252
|
}
|
|
3096
3253
|
return null;
|
|
3097
3254
|
};
|
|
3098
3255
|
var toImport = (specifier, imported, dir) => {
|
|
3099
|
-
const relative4 = specifier.startsWith(".") ||
|
|
3256
|
+
const relative4 = specifier.startsWith(".") || isAbsolute2(specifier);
|
|
3100
3257
|
let path = specifier;
|
|
3101
3258
|
let extension = extname(specifier).slice(1).toLowerCase();
|
|
3102
3259
|
if (relative4) {
|
|
3103
|
-
const absolute =
|
|
3260
|
+
const absolute = isAbsolute2(specifier) ? specifier : resolve3(dir, specifier);
|
|
3104
3261
|
if (extension) {
|
|
3105
3262
|
path = absolute;
|
|
3106
3263
|
} else {
|
|
@@ -3348,147 +3505,104 @@ var resolveUIStrings = (locale, options) => {
|
|
|
3348
3505
|
};
|
|
3349
3506
|
|
|
3350
3507
|
// src/theme/icons.ts
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
"
|
|
3357
|
-
|
|
3358
|
-
"
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
"
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
"
|
|
3386
|
-
|
|
3387
|
-
"
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
}
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
"fa-linkedin": "linkedin",
|
|
3415
|
-
"fa-x-twitter": "brand-x",
|
|
3416
|
-
"file-lines": "file",
|
|
3417
|
-
javascript: "js",
|
|
3418
|
-
"panel-left-open": "panel-left",
|
|
3419
|
-
"panel-right-open": "panel-right",
|
|
3420
|
-
times: "x",
|
|
3421
|
-
"x-twitter": "brand-x"
|
|
3422
|
-
};
|
|
3423
|
-
var libraryPrefixes = [
|
|
3424
|
-
"fa-brands",
|
|
3425
|
-
"fa-duotone",
|
|
3426
|
-
"fa-light",
|
|
3427
|
-
"fa-regular",
|
|
3428
|
-
"fa-sharp-solid",
|
|
3429
|
-
"fa-solid",
|
|
3430
|
-
"fa-thin",
|
|
3431
|
-
"fa",
|
|
3432
|
-
"fab",
|
|
3433
|
-
"fad",
|
|
3434
|
-
"fal",
|
|
3435
|
-
"far",
|
|
3436
|
-
"fas",
|
|
3437
|
-
"fat",
|
|
3438
|
-
"lucide",
|
|
3439
|
-
"tabler",
|
|
3440
|
-
"ti"
|
|
3441
|
-
];
|
|
3442
|
-
var normalizedIconName = (name) => name.trim().toLowerCase().replaceAll(/[\s_]+/gu, "-");
|
|
3443
|
-
var isString = (value) => typeof value === "string";
|
|
3444
|
-
var withoutLibraryPrefix = (name) => {
|
|
3445
|
-
let normalized = normalizedIconName(name).replaceAll(/^icon-/gu, "");
|
|
3446
|
-
let changed = true;
|
|
3447
|
-
while (changed) {
|
|
3448
|
-
changed = false;
|
|
3449
|
-
for (const prefix of libraryPrefixes) {
|
|
3450
|
-
if (normalized.startsWith(`${prefix}-`)) {
|
|
3451
|
-
normalized = normalized.slice(prefix.length + 1);
|
|
3452
|
-
changed = true;
|
|
3453
|
-
}
|
|
3454
|
-
if (normalized.startsWith(`${prefix}:`)) {
|
|
3455
|
-
normalized = normalized.slice(prefix.length + 1);
|
|
3456
|
-
changed = true;
|
|
3457
|
-
}
|
|
3458
|
-
}
|
|
3508
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
3509
|
+
import { getIconData, iconToSVG } from "@iconify/utils";
|
|
3510
|
+
var requireJson = createRequire2(import.meta.url);
|
|
3511
|
+
var loadSet = (pkg) => requireJson(pkg);
|
|
3512
|
+
var SETS = {
|
|
3513
|
+
"fa6-brands": loadSet("@iconify-json/fa6-brands/icons.json"),
|
|
3514
|
+
"fa6-regular": loadSet("@iconify-json/fa6-regular/icons.json"),
|
|
3515
|
+
"fa6-solid": loadSet("@iconify-json/fa6-solid/icons.json"),
|
|
3516
|
+
lucide: loadSet("@iconify-json/lucide/icons.json"),
|
|
3517
|
+
tabler: loadSet("@iconify-json/tabler/icons.json")
|
|
3518
|
+
};
|
|
3519
|
+
var DEFAULT_SET = "lucide";
|
|
3520
|
+
var LIBRARY_SETS = {
|
|
3521
|
+
fa: "fa6-solid",
|
|
3522
|
+
"font-awesome": "fa6-solid",
|
|
3523
|
+
fontawesome: "fa6-solid",
|
|
3524
|
+
lucide: "lucide",
|
|
3525
|
+
tabler: "tabler"
|
|
3526
|
+
};
|
|
3527
|
+
var ICON_TYPE_SETS = {
|
|
3528
|
+
brands: "fa6-brands",
|
|
3529
|
+
duotone: "fa6-solid",
|
|
3530
|
+
light: "fa6-solid",
|
|
3531
|
+
regular: "fa6-regular",
|
|
3532
|
+
"sharp-solid": "fa6-solid",
|
|
3533
|
+
solid: "fa6-solid",
|
|
3534
|
+
thin: "fa6-solid"
|
|
3535
|
+
};
|
|
3536
|
+
var PREFIX_SETS = {
|
|
3537
|
+
...LIBRARY_SETS,
|
|
3538
|
+
fa: "fa6-solid",
|
|
3539
|
+
"fa-brands": "fa6-brands",
|
|
3540
|
+
"fa-regular": "fa6-regular",
|
|
3541
|
+
"fa-solid": "fa6-solid",
|
|
3542
|
+
"fa6-brands": "fa6-brands",
|
|
3543
|
+
"fa6-regular": "fa6-regular",
|
|
3544
|
+
"fa6-solid": "fa6-solid",
|
|
3545
|
+
fab: "fa6-brands",
|
|
3546
|
+
far: "fa6-regular",
|
|
3547
|
+
fas: "fa6-solid",
|
|
3548
|
+
ti: "tabler"
|
|
3549
|
+
};
|
|
3550
|
+
var normalize = (name) => name.trim().toLowerCase().replaceAll(/[\s_]+/gu, "-");
|
|
3551
|
+
var setFor = (options) => {
|
|
3552
|
+
if (options.iconType) {
|
|
3553
|
+
const set = ICON_TYPE_SETS[normalize(options.iconType)];
|
|
3554
|
+
if (set) {
|
|
3555
|
+
return set;
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
if (options.library) {
|
|
3559
|
+
const set = LIBRARY_SETS[normalize(options.library)];
|
|
3560
|
+
if (set) {
|
|
3561
|
+
return set;
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
return DEFAULT_SET;
|
|
3565
|
+
};
|
|
3566
|
+
var fromSet = (setName, iconName) => {
|
|
3567
|
+
const set = SETS[setName];
|
|
3568
|
+
const data = set && getIconData(set, iconName);
|
|
3569
|
+
if (!data) {
|
|
3570
|
+
return null;
|
|
3459
3571
|
}
|
|
3460
|
-
|
|
3572
|
+
const { attributes, body } = iconToSVG(data, { height: "auto" });
|
|
3573
|
+
return { body, name: iconName, viewBox: attributes.viewBox };
|
|
3461
3574
|
};
|
|
3462
|
-
var
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
const
|
|
3466
|
-
const
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
iconAliases[stripped]
|
|
3472
|
-
].filter(isString);
|
|
3473
|
-
for (const candidate of candidates) {
|
|
3474
|
-
const markup = icons[candidate];
|
|
3475
|
-
if (markup) {
|
|
3476
|
-
return { markup, name: candidate };
|
|
3575
|
+
var fromFaSet = (setName, name) => fromSet(setName, name) ?? fromSet("fa6-brands", name);
|
|
3576
|
+
var resolveInSet = (setName, name) => setName.startsWith("fa6-") ? fromFaSet(setName, name) : fromSet(setName, name);
|
|
3577
|
+
var resolveIcon = (name, options = {}) => {
|
|
3578
|
+
const normalized = normalize(name);
|
|
3579
|
+
const colon = normalized.indexOf(":");
|
|
3580
|
+
if (colon > 0) {
|
|
3581
|
+
const setName = PREFIX_SETS[normalized.slice(0, colon)];
|
|
3582
|
+
if (setName) {
|
|
3583
|
+
return resolveInSet(setName, normalized.slice(colon + 1));
|
|
3477
3584
|
}
|
|
3478
3585
|
}
|
|
3479
|
-
return
|
|
3586
|
+
return resolveInSet(setFor(options), normalized);
|
|
3587
|
+
};
|
|
3588
|
+
var hasIcon = (name, options = {}) => {
|
|
3589
|
+
if (resolveIcon(name, options)) {
|
|
3590
|
+
return true;
|
|
3591
|
+
}
|
|
3592
|
+
const normalized = normalize(name);
|
|
3593
|
+
const bare = normalized.includes(":") ? normalized.slice(normalized.indexOf(":") + 1) : normalized;
|
|
3594
|
+
return Object.values(SETS).some((set) => getIconData(set, bare) !== null);
|
|
3480
3595
|
};
|
|
3481
|
-
var hasIcon = (name, iconType) => resolveIcon(name, iconType) !== null;
|
|
3482
3596
|
|
|
3483
3597
|
// src/core/nav-diagnostics.ts
|
|
3484
3598
|
var IMAGE_ICON = /^(?:https?:\/\/|data:image\/|\/|\.{1,2}\/)|\.(?:avif|gif|jpe?g|png|svg|webp)$/iu;
|
|
3485
3599
|
var isAssetIcon = (value) => value.startsWith("<") || IMAGE_ICON.test(value);
|
|
3486
3600
|
var flattenNodes = (nodes) => nodes.flatMap((node) => node.kind === "group" ? [node, ...flattenNodes(node.children)] : [node]);
|
|
3487
3601
|
var collectIcons = (navigation) => {
|
|
3488
|
-
const
|
|
3602
|
+
const icons = [];
|
|
3489
3603
|
const push = (icon, where) => {
|
|
3490
3604
|
if (icon) {
|
|
3491
|
-
|
|
3605
|
+
icons.push({ icon, where });
|
|
3492
3606
|
}
|
|
3493
3607
|
};
|
|
3494
3608
|
for (const tab of navigation.tabs) {
|
|
@@ -3511,7 +3625,7 @@ var collectIcons = (navigation) => {
|
|
|
3511
3625
|
push(node.icon, `"${node.label}"`);
|
|
3512
3626
|
}
|
|
3513
3627
|
}
|
|
3514
|
-
return
|
|
3628
|
+
return icons;
|
|
3515
3629
|
};
|
|
3516
3630
|
var validateNavIcons = (navigation) => {
|
|
3517
3631
|
const seen = new Set;
|
|
@@ -3621,10 +3735,10 @@ var validateNavStructure = (navigation, pages) => [
|
|
|
3621
3735
|
];
|
|
3622
3736
|
|
|
3623
3737
|
// src/core/tsconfig-aliases.ts
|
|
3624
|
-
import { existsSync as
|
|
3625
|
-
import { createRequire as
|
|
3738
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3, statSync } from "node:fs";
|
|
3739
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
3626
3740
|
import { pathToFileURL } from "node:url";
|
|
3627
|
-
import { dirname as dirname5, isAbsolute as
|
|
3741
|
+
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join9, resolve as resolve4 } from "pathe";
|
|
3628
3742
|
var stripJsonComments = (text) => {
|
|
3629
3743
|
let out = "";
|
|
3630
3744
|
let inString = false;
|
|
@@ -3678,16 +3792,16 @@ var isFile = (path) => {
|
|
|
3678
3792
|
}
|
|
3679
3793
|
};
|
|
3680
3794
|
var resolveExtends = (spec, fromDir) => {
|
|
3681
|
-
if (spec.startsWith(".") ||
|
|
3682
|
-
const candidates = spec.endsWith(".json") ? [
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3795
|
+
if (spec.startsWith(".") || isAbsolute3(spec)) {
|
|
3796
|
+
const candidates = spec.endsWith(".json") ? [resolve4(fromDir, spec)] : [
|
|
3797
|
+
resolve4(fromDir, `${spec}.json`),
|
|
3798
|
+
resolve4(fromDir, spec, "tsconfig.json"),
|
|
3799
|
+
resolve4(fromDir, spec)
|
|
3686
3800
|
];
|
|
3687
3801
|
return candidates.find(isFile) ?? null;
|
|
3688
3802
|
}
|
|
3689
3803
|
try {
|
|
3690
|
-
const require_ =
|
|
3804
|
+
const require_ = createRequire3(pathToFileURL(join9(fromDir, "_.js")).href);
|
|
3691
3805
|
for (const sub of [`${spec}/tsconfig.json`, spec]) {
|
|
3692
3806
|
try {
|
|
3693
3807
|
return require_.resolve(sub);
|
|
@@ -3697,11 +3811,11 @@ var resolveExtends = (spec, fromDir) => {
|
|
|
3697
3811
|
return null;
|
|
3698
3812
|
};
|
|
3699
3813
|
var loadPaths = (file, seen) => {
|
|
3700
|
-
if (seen.has(file) || !
|
|
3814
|
+
if (seen.has(file) || !existsSync7(file)) {
|
|
3701
3815
|
return null;
|
|
3702
3816
|
}
|
|
3703
3817
|
seen.add(file);
|
|
3704
|
-
const json = parseJsonc(
|
|
3818
|
+
const json = parseJsonc(readFileSync3(file, "utf-8"));
|
|
3705
3819
|
if (!json) {
|
|
3706
3820
|
return null;
|
|
3707
3821
|
}
|
|
@@ -3709,7 +3823,7 @@ var loadPaths = (file, seen) => {
|
|
|
3709
3823
|
if (options.paths && typeof options.paths === "object") {
|
|
3710
3824
|
const baseUrl = typeof options.baseUrl === "string" ? options.baseUrl : ".";
|
|
3711
3825
|
return {
|
|
3712
|
-
baseDir:
|
|
3826
|
+
baseDir: resolve4(dirname5(file), baseUrl),
|
|
3713
3827
|
paths: options.paths
|
|
3714
3828
|
};
|
|
3715
3829
|
}
|
|
@@ -3736,10 +3850,10 @@ var toAlias = (key, value, baseDir) => {
|
|
|
3736
3850
|
if (find === "" || find === "*") {
|
|
3737
3851
|
return null;
|
|
3738
3852
|
}
|
|
3739
|
-
return { find, replacement:
|
|
3853
|
+
return { find, replacement: resolve4(baseDir, target) };
|
|
3740
3854
|
};
|
|
3741
3855
|
var resolveTsconfigAliases = (root) => {
|
|
3742
|
-
const entry = ["tsconfig.json", "jsconfig.json"].map((name) =>
|
|
3856
|
+
const entry = ["tsconfig.json", "jsconfig.json"].map((name) => join9(root, name)).find((file) => existsSync7(file));
|
|
3743
3857
|
if (!entry) {
|
|
3744
3858
|
return {};
|
|
3745
3859
|
}
|
|
@@ -3798,7 +3912,6 @@ var buildRssFeeds = (project) => {
|
|
|
3798
3912
|
}
|
|
3799
3913
|
return feeds;
|
|
3800
3914
|
};
|
|
3801
|
-
var escapeXml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
3802
3915
|
var renderItem = (item) => {
|
|
3803
3916
|
const parts = [
|
|
3804
3917
|
` <title>${escapeXml(item.title)}</title>`,
|
|
@@ -3836,13 +3949,98 @@ ${items}
|
|
|
3836
3949
|
`;
|
|
3837
3950
|
};
|
|
3838
3951
|
|
|
3952
|
+
// src/openapi/references.ts
|
|
3953
|
+
var NON_SLUG = /[^a-z0-9]+/gu;
|
|
3954
|
+
var SLUG_EDGES = /^-+|-+$/gu;
|
|
3955
|
+
var ROUTE_EDGES = /^\/+|\/+$/gu;
|
|
3956
|
+
var TRAILING_SLASH = /\/+$/u;
|
|
3957
|
+
var slugify = (text) => text.toLowerCase().replace(NON_SLUG, "-").replace(SLUG_EDGES, "");
|
|
3958
|
+
var normalizeRoute = (route) => {
|
|
3959
|
+
const trimmed = route.trim();
|
|
3960
|
+
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
3961
|
+
const noTrailing = withSlash.replace(TRAILING_SLASH, "");
|
|
3962
|
+
return noTrailing === "" ? "/" : noTrailing;
|
|
3963
|
+
};
|
|
3964
|
+
var routeSlug = (route) => slugify(route.replace(ROUTE_EDGES, "")) || "reference";
|
|
3965
|
+
var sourcesOf = (block) => {
|
|
3966
|
+
const sources = [...block.sources];
|
|
3967
|
+
if (block.spec) {
|
|
3968
|
+
sources.unshift({ spec: block.spec });
|
|
3969
|
+
}
|
|
3970
|
+
return sources;
|
|
3971
|
+
};
|
|
3972
|
+
var referencesFor = (kind, block, defaultLabel, renderer, display) => {
|
|
3973
|
+
if (!block.enabled) {
|
|
3974
|
+
return [];
|
|
3975
|
+
}
|
|
3976
|
+
const sources = sourcesOf(block);
|
|
3977
|
+
const base = normalizeRoute(block.route);
|
|
3978
|
+
return sources.map((source, index) => {
|
|
3979
|
+
const label = source.label ?? (sources.length > 1 ? `${defaultLabel} ${index + 1}` : defaultLabel);
|
|
3980
|
+
let route;
|
|
3981
|
+
if (source.route) {
|
|
3982
|
+
route = normalizeRoute(source.route);
|
|
3983
|
+
} else if (sources.length === 1) {
|
|
3984
|
+
route = base;
|
|
3985
|
+
} else {
|
|
3986
|
+
const suffix = source.label ? slugify(source.label) : "";
|
|
3987
|
+
route = normalizeRoute(`${base}/${suffix || index + 1}`);
|
|
3988
|
+
}
|
|
3989
|
+
return {
|
|
3990
|
+
display,
|
|
3991
|
+
kind,
|
|
3992
|
+
label,
|
|
3993
|
+
renderer,
|
|
3994
|
+
route,
|
|
3995
|
+
slug: routeSlug(route),
|
|
3996
|
+
spec: source.spec,
|
|
3997
|
+
theme: block.theme
|
|
3998
|
+
};
|
|
3999
|
+
});
|
|
4000
|
+
};
|
|
4001
|
+
var NO_DISPLAY = { codeSamples: [], expandSchemas: false };
|
|
4002
|
+
var resolveReferences = (config) => [
|
|
4003
|
+
...referencesFor("openapi", config.openapi, "API Reference", config.openapi.renderer, {
|
|
4004
|
+
codeSamples: config.openapi.codeSamples,
|
|
4005
|
+
expandSchemas: config.openapi.expandSchemas
|
|
4006
|
+
}),
|
|
4007
|
+
...referencesFor("asyncapi", config.asyncapi, "Events", "scalar", NO_DISPLAY)
|
|
4008
|
+
];
|
|
4009
|
+
var referenceTabs = (config) => resolveReferences(config).map((ref) => ({
|
|
4010
|
+
label: ref.label,
|
|
4011
|
+
path: ref.route
|
|
4012
|
+
}));
|
|
4013
|
+
var blumeReferences = (config) => {
|
|
4014
|
+
const seen = new Set;
|
|
4015
|
+
const result = [];
|
|
4016
|
+
for (const ref of resolveReferences(config)) {
|
|
4017
|
+
if (ref.kind !== "openapi" || ref.renderer !== "blume") {
|
|
4018
|
+
continue;
|
|
4019
|
+
}
|
|
4020
|
+
if (seen.has(ref.route)) {
|
|
4021
|
+
continue;
|
|
4022
|
+
}
|
|
4023
|
+
seen.add(ref.route);
|
|
4024
|
+
result.push(ref);
|
|
4025
|
+
}
|
|
4026
|
+
return result;
|
|
4027
|
+
};
|
|
4028
|
+
var hasScalarReferences = (config) => resolveReferences(config).some((ref) => ref.renderer === "scalar");
|
|
4029
|
+
|
|
3839
4030
|
// src/openapi/scalar.ts
|
|
3840
|
-
import { readFile as
|
|
3841
|
-
import { isAbsolute as
|
|
4031
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
4032
|
+
import { isAbsolute as isAbsolute4, join as join12 } from "pathe";
|
|
3842
4033
|
|
|
3843
4034
|
// src/astro/templates.ts
|
|
3844
|
-
import { existsSync as
|
|
3845
|
-
import { dirname as dirname6, join as
|
|
4035
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
|
|
4036
|
+
import { dirname as dirname6, join as join11 } from "pathe";
|
|
4037
|
+
|
|
4038
|
+
// src/core/assets.ts
|
|
4039
|
+
import { join as join10 } from "pathe";
|
|
4040
|
+
var resolveAssetMounts = (root, assets) => assets.map((entry) => {
|
|
4041
|
+
const rel = entry.replace(/^[./]+/u, "").replaceAll(/\.\.\/?/gu, "").replace(/\/+$/u, "");
|
|
4042
|
+
return { dir: join10(root, rel), url: `/${rel}` };
|
|
4043
|
+
});
|
|
3846
4044
|
|
|
3847
4045
|
// src/theme/fonts.ts
|
|
3848
4046
|
var FALLBACKS = {
|
|
@@ -4000,17 +4198,17 @@ var WORKSPACE_MARKERS = [
|
|
|
4000
4198
|
"yarn.lock"
|
|
4001
4199
|
];
|
|
4002
4200
|
var hasWorkspacesField = (pkgPath) => {
|
|
4003
|
-
if (!
|
|
4201
|
+
if (!existsSync8(pkgPath)) {
|
|
4004
4202
|
return false;
|
|
4005
4203
|
}
|
|
4006
4204
|
try {
|
|
4007
|
-
const pkg = JSON.parse(
|
|
4205
|
+
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
4008
4206
|
return pkg.workspaces !== undefined;
|
|
4009
4207
|
} catch {
|
|
4010
4208
|
return false;
|
|
4011
4209
|
}
|
|
4012
4210
|
};
|
|
4013
|
-
var hasWorkspaceMarker = (dir) => hasWorkspacesField(
|
|
4211
|
+
var hasWorkspaceMarker = (dir) => hasWorkspacesField(join11(dir, "package.json")) || WORKSPACE_MARKERS.some((marker) => existsSync8(join11(dir, marker)));
|
|
4014
4212
|
var findWorkspaceRoot = (start) => {
|
|
4015
4213
|
let dir = start;
|
|
4016
4214
|
for (;; ) {
|
|
@@ -4045,7 +4243,7 @@ var runtimeDependencies = (options) => {
|
|
|
4045
4243
|
if (needsSvelte) {
|
|
4046
4244
|
deps.push("@astrojs/svelte");
|
|
4047
4245
|
}
|
|
4048
|
-
if (config
|
|
4246
|
+
if (hasScalarReferences(config)) {
|
|
4049
4247
|
deps.push("@scalar/astro");
|
|
4050
4248
|
}
|
|
4051
4249
|
deps.push(...searchProviderMeta(config.search.provider).runtimeDeps);
|
|
@@ -4078,6 +4276,7 @@ var RENDER_EXTERNAL_DEPS = [
|
|
|
4078
4276
|
];
|
|
4079
4277
|
var renderUserAliases = (aliases) => Object.entries(aliases ?? {}).toSorted(([a], [b]) => b.length - a.length).map(([find, replacement]) => `
|
|
4080
4278
|
${JSON.stringify(find)}: ${JSON.stringify(replacement)},`).join("");
|
|
4279
|
+
var astroOutDir = (context) => context.distDir ?? `${context.root}/dist`;
|
|
4081
4280
|
var astroConfigTemplate = (options) => {
|
|
4082
4281
|
const { context, config, needsReact, pages, dataPath, themePath } = options;
|
|
4083
4282
|
const {
|
|
@@ -4085,6 +4284,7 @@ var astroConfigTemplate = (options) => {
|
|
|
4085
4284
|
examplesPath,
|
|
4086
4285
|
needsSvelte,
|
|
4087
4286
|
needsVue,
|
|
4287
|
+
openapiPath,
|
|
4088
4288
|
searchClientPath
|
|
4089
4289
|
} = options;
|
|
4090
4290
|
const { deployment } = config;
|
|
@@ -4144,7 +4344,8 @@ var astroConfigTemplate = (options) => {
|
|
|
4144
4344
|
if (needsSvelte) {
|
|
4145
4345
|
integrations.push("svelte()");
|
|
4146
4346
|
}
|
|
4147
|
-
|
|
4347
|
+
const assets = resolveAssetMounts(context.root, config.content.assets);
|
|
4348
|
+
integrations.push(`blumeIntegration(${JSON.stringify({ assets, base: deployment.base, contentRoutes, pages })})`);
|
|
4148
4349
|
return `// Generated by Blume. Do not edit; this file is recreated on each run.
|
|
4149
4350
|
${defineConfigImport}
|
|
4150
4351
|
import mdx from "@astrojs/mdx";
|
|
@@ -4154,7 +4355,7 @@ ${twoslashImport}${reactImport}${vueImport}${svelteImport}${blumeImport}${adapte
|
|
|
4154
4355
|
export default defineConfig({
|
|
4155
4356
|
root: ${JSON.stringify(context.outDir)},
|
|
4156
4357
|
srcDir: ${JSON.stringify(`${context.outDir}/src`)},
|
|
4157
|
-
outDir: ${JSON.stringify(
|
|
4358
|
+
outDir: ${JSON.stringify(astroOutDir(context))},
|
|
4158
4359
|
publicDir: ${JSON.stringify(`${context.root}/public`)},
|
|
4159
4360
|
output: ${JSON.stringify(deployment.output)},${adapterOption}${siteOption}${baseOption}${redirectsOption}${i18nOption}${fontsOption}
|
|
4160
4361
|
integrations: [${integrations.join(", ")}],
|
|
@@ -4187,6 +4388,7 @@ export default defineConfig({
|
|
|
4187
4388
|
alias: {
|
|
4188
4389
|
"blume:data": ${JSON.stringify(dataPath)},
|
|
4189
4390
|
"blume:examples": ${JSON.stringify(examplesPath)},
|
|
4391
|
+
"blume:openapi": ${JSON.stringify(openapiPath)},
|
|
4190
4392
|
"blume:search-client": ${JSON.stringify(searchClientPath)},
|
|
4191
4393
|
"blume:theme": ${JSON.stringify(themePath)},${userAliasLines}
|
|
4192
4394
|
},
|
|
@@ -4200,7 +4402,7 @@ export default defineConfig({
|
|
|
4200
4402
|
});
|
|
4201
4403
|
`;
|
|
4202
4404
|
};
|
|
4203
|
-
var stagedContentDir = (outDir) =>
|
|
4405
|
+
var stagedContentDir = (outDir) => join11(outDir, "content");
|
|
4204
4406
|
var contentConfigTemplate = (options) => {
|
|
4205
4407
|
const { context, config } = options;
|
|
4206
4408
|
const stagedBase = options.stagedBase ?? stagedContentDir(context.outDir);
|
|
@@ -4257,31 +4459,44 @@ const provider = createOpenAICompatible({
|
|
|
4257
4459
|
modelExpr = `provider(${JSON.stringify(backend.model)})`;
|
|
4258
4460
|
}
|
|
4259
4461
|
if (grounded) {
|
|
4260
|
-
imports.push('import { createAskContext } from "blume/ai/ask-context.ts";', 'import askData from "
|
|
4462
|
+
imports.push('import { createAskContext } from "blume/ai/ask-context.ts";', 'import askData from "../../generated/ask-data.json";');
|
|
4261
4463
|
setup += `
|
|
4262
4464
|
const ground = createAskContext(askData);
|
|
4263
4465
|
`;
|
|
4264
4466
|
}
|
|
4265
|
-
const
|
|
4266
|
-
const
|
|
4267
|
-
|
|
4268
|
-
(
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
messages,
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
}
|
|
4277
|
-
const
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4467
|
+
const validate = ` const body = await request.json().catch(() => null);
|
|
4468
|
+
const messages = body?.messages;
|
|
4469
|
+
if (
|
|
4470
|
+
!Array.isArray(messages) ||
|
|
4471
|
+
messages.length === 0 ||
|
|
4472
|
+
messages.length > 40 ||
|
|
4473
|
+
JSON.stringify(messages).length > 24_000
|
|
4474
|
+
) {
|
|
4475
|
+
return new Response("Invalid request: send 1-40 messages.", {
|
|
4476
|
+
status: 400,
|
|
4477
|
+
});
|
|
4478
|
+
}`;
|
|
4479
|
+
const stream = grounded ? ` const system =
|
|
4480
|
+
(await ground(messages, body.page)) ??
|
|
4481
|
+
"You are a helpful documentation assistant. Answer using the project's documentation.";
|
|
4482
|
+
const result = streamText({
|
|
4483
|
+
model: ${modelExpr},
|
|
4484
|
+
system,
|
|
4485
|
+
messages,
|
|
4486
|
+
});` : ` const result = streamText({
|
|
4487
|
+
model: ${modelExpr},
|
|
4488
|
+
system:
|
|
4489
|
+
"You are a helpful documentation assistant. Answer using the project's documentation.",
|
|
4490
|
+
messages,
|
|
4491
|
+
});`;
|
|
4492
|
+
const handler = `export const POST: APIRoute = async ({ request }) => {
|
|
4493
|
+
${validate}
|
|
4494
|
+
try {
|
|
4495
|
+
${stream}
|
|
4496
|
+
return result.toTextStreamResponse();
|
|
4497
|
+
} catch {
|
|
4498
|
+
return new Response("Failed to generate a response.", { status: 500 });
|
|
4499
|
+
}
|
|
4285
4500
|
};`;
|
|
4286
4501
|
return `// Generated by Blume. Do not edit.
|
|
4287
4502
|
${imports.join(`
|
|
@@ -4593,7 +4808,10 @@ import FileTree from "blume/components/content/FileTree.astro";
|
|
|
4593
4808
|
import Frame from "blume/components/content/Frame.astro";
|
|
4594
4809
|
import GithubInfo from "blume/components/content/GithubInfo.astro";
|
|
4595
4810
|
import Panel from "blume/components/content/Panel.astro";
|
|
4811
|
+
import ParamField from "blume/components/content/ParamField.astro";
|
|
4596
4812
|
import Prompt from "blume/components/content/Prompt.astro";
|
|
4813
|
+
import RequestField from "blume/components/content/RequestField.astro";
|
|
4814
|
+
import ResponseField from "blume/components/content/ResponseField.astro";
|
|
4597
4815
|
import Step from "blume/components/content/Step.astro";
|
|
4598
4816
|
import Steps from "blume/components/content/Steps.astro";
|
|
4599
4817
|
import Tab from "blume/components/content/Tab.astro";
|
|
@@ -4605,7 +4823,10 @@ import TreeFile from "blume/components/content/TreeFile.astro";
|
|
|
4605
4823
|
import TreeFolder from "blume/components/content/TreeFolder.astro";
|
|
4606
4824
|
import TypeTable from "blume/components/content/TypeTable.astro";
|
|
4607
4825
|
import Visibility from "blume/components/content/Visibility.astro";
|
|
4826
|
+
import YouTube from "blume/components/content/YouTube.astro";
|
|
4608
4827
|
import Icon from "blume/components/Icon.astro";
|
|
4828
|
+
import ApiOverview from "blume/components/openapi/ApiOverview.astro";
|
|
4829
|
+
import Operation from "blume/components/openapi/Operation.astro";
|
|
4609
4830
|
${mathImport}import { mdxComponents as userMdx, layoutOverrides } from "../generated/components.ts";
|
|
4610
4831
|
import { islandComponents } from "../generated/islands.ts";
|
|
4611
4832
|
import data from "../generated/data.json";
|
|
@@ -4622,6 +4843,7 @@ export const prerender = true;
|
|
|
4622
4843
|
const components = {
|
|
4623
4844
|
Accordion,
|
|
4624
4845
|
AccordionItem,
|
|
4846
|
+
ApiOverview,
|
|
4625
4847
|
AutoTypeTable,
|
|
4626
4848
|
Badge,
|
|
4627
4849
|
Callout,
|
|
@@ -4639,8 +4861,12 @@ const components = {
|
|
|
4639
4861
|
Frame,
|
|
4640
4862
|
GithubInfo,
|
|
4641
4863
|
Icon,
|
|
4864
|
+
Operation,
|
|
4642
4865
|
Panel,
|
|
4866
|
+
ParamField,
|
|
4643
4867
|
Prompt,
|
|
4868
|
+
RequestField,
|
|
4869
|
+
ResponseField,
|
|
4644
4870
|
Step,
|
|
4645
4871
|
Steps,
|
|
4646
4872
|
Tab,
|
|
@@ -4650,6 +4876,7 @@ const components = {
|
|
|
4650
4876
|
Tree,
|
|
4651
4877
|
TypeTable,
|
|
4652
4878
|
Visibility,
|
|
4879
|
+
YouTube,
|
|
4653
4880
|
${mathEntry}...islandComponents,
|
|
4654
4881
|
...userMdx,
|
|
4655
4882
|
};
|
|
@@ -5039,6 +5266,11 @@ declare module "blume:data" {
|
|
|
5039
5266
|
export default data;
|
|
5040
5267
|
}
|
|
5041
5268
|
|
|
5269
|
+
declare module "blume:openapi" {
|
|
5270
|
+
const specs: import("blume/openapi/model.ts").OpenApiData;
|
|
5271
|
+
export default specs;
|
|
5272
|
+
}
|
|
5273
|
+
|
|
5042
5274
|
declare module "blume:search-client" {
|
|
5043
5275
|
export const createSearch: () =>
|
|
5044
5276
|
| import("blume/components/layout/search/types.ts").SearchFn
|
|
@@ -5061,8 +5293,9 @@ var runtimeTsconfigTemplate = () => `${JSON.stringify({
|
|
|
5061
5293
|
`;
|
|
5062
5294
|
|
|
5063
5295
|
// src/theme/palette.ts
|
|
5296
|
+
var FALLBACK_ACCENT = "oklch(0.62 0.16 250)";
|
|
5064
5297
|
var ACCENTS = {
|
|
5065
|
-
blue:
|
|
5298
|
+
blue: FALLBACK_ACCENT,
|
|
5066
5299
|
green: "oklch(0.6 0.16 150)",
|
|
5067
5300
|
orange: "oklch(0.68 0.17 50)",
|
|
5068
5301
|
pink: "oklch(0.65 0.2 350)",
|
|
@@ -5070,6 +5303,9 @@ var ACCENTS = {
|
|
|
5070
5303
|
red: "oklch(0.58 0.22 25)",
|
|
5071
5304
|
teal: "oklch(0.6 0.12 195)"
|
|
5072
5305
|
};
|
|
5306
|
+
var CSS_COLOR = /^[\w\s#%.,()/+-]+$/u;
|
|
5307
|
+
var safeColor = (value, fallback) => CSS_COLOR.test(value.trim()) ? value.trim() : fallback;
|
|
5308
|
+
var safeColorOrNull = (value) => value && CSS_COLOR.test(value.trim()) ? value.trim() : null;
|
|
5073
5309
|
var RADII = {
|
|
5074
5310
|
lg: "0.75rem",
|
|
5075
5311
|
md: "0.5rem",
|
|
@@ -5104,7 +5340,7 @@ var themeRootCss = (theme, options) => [
|
|
|
5104
5340
|
` --blume-accent: ${options.accent};`,
|
|
5105
5341
|
...cssToken("--blume-action", options.action),
|
|
5106
5342
|
...cssToken("--blume-action-foreground", options.action ? "oklch(1 0 0)" : null),
|
|
5107
|
-
...cssToken("--blume-background", theme.background),
|
|
5343
|
+
...cssToken("--blume-background", safeColorOrNull(theme.background)),
|
|
5108
5344
|
...cssToken("--blume-background-image", theme.backgroundImage ? backgroundImageCss(theme.backgroundImage) : null),
|
|
5109
5345
|
options.backgroundDecoration.trimEnd(),
|
|
5110
5346
|
` --blume-radius: ${options.radius};`
|
|
@@ -5113,7 +5349,7 @@ var themeRootCss = (theme, options) => [
|
|
|
5113
5349
|
var themeDarkCss = (theme, accentDark) => {
|
|
5114
5350
|
const tokens = [
|
|
5115
5351
|
...cssToken("--blume-accent", accentDark),
|
|
5116
|
-
...cssToken("--blume-background", theme.backgroundDark),
|
|
5352
|
+
...cssToken("--blume-background", safeColorOrNull(theme.backgroundDark)),
|
|
5117
5353
|
...cssToken("--blume-background-image", theme.backgroundImageDark ? backgroundImageCss(theme.backgroundImageDark) : null)
|
|
5118
5354
|
];
|
|
5119
5355
|
if (tokens.length === 0) {
|
|
@@ -5125,12 +5361,12 @@ ${tokens.join(`
|
|
|
5125
5361
|
}
|
|
5126
5362
|
`;
|
|
5127
5363
|
};
|
|
5128
|
-
var resolveAccent = (theme) => ACCENTS[theme.accent] ?? theme.accent;
|
|
5364
|
+
var resolveAccent = (theme) => ACCENTS[theme.accent] ?? safeColor(theme.accent, FALLBACK_ACCENT);
|
|
5129
5365
|
var resolveRadius = (theme) => RADII[theme.radius];
|
|
5130
5366
|
var buildThemeCss = (theme) => {
|
|
5131
|
-
const accent = ACCENTS[theme.accent] ?? theme.accent;
|
|
5132
|
-
const accentDark = theme.accentDark ? ACCENTS[theme.accentDark] ?? theme.accentDark : null;
|
|
5133
|
-
const action = theme.action ? ACCENTS[theme.action] ?? theme.action : null;
|
|
5367
|
+
const accent = ACCENTS[theme.accent] ?? safeColor(theme.accent, FALLBACK_ACCENT);
|
|
5368
|
+
const accentDark = theme.accentDark ? ACCENTS[theme.accentDark] ?? safeColor(theme.accentDark, FALLBACK_ACCENT) : null;
|
|
5369
|
+
const action = theme.action ? ACCENTS[theme.action] ?? safeColor(theme.action, FALLBACK_ACCENT) : null;
|
|
5134
5370
|
const backgroundDecoration = backgroundDecorationCss(theme.backgroundDecoration);
|
|
5135
5371
|
const radius = RADII[theme.radius];
|
|
5136
5372
|
const root = themeRootCss(theme, {
|
|
@@ -5149,57 +5385,11 @@ ${dark}`;
|
|
|
5149
5385
|
|
|
5150
5386
|
// src/openapi/scalar.ts
|
|
5151
5387
|
var URL_SPEC = /^https?:\/\//u;
|
|
5152
|
-
var
|
|
5153
|
-
var SLUG_EDGES = /^-+|-+$/gu;
|
|
5154
|
-
var ROUTE_EDGES = /^\/+|\/+$/gu;
|
|
5155
|
-
var TRAILING_SLASH = /\/+$/u;
|
|
5156
|
-
var slugify = (text) => text.toLowerCase().replace(NON_SLUG, "-").replace(SLUG_EDGES, "");
|
|
5157
|
-
var normalizeRoute = (route) => {
|
|
5158
|
-
const trimmed = route.trim();
|
|
5159
|
-
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
5160
|
-
const noTrailing = withSlash.replace(TRAILING_SLASH, "");
|
|
5161
|
-
return noTrailing === "" ? "/" : noTrailing;
|
|
5162
|
-
};
|
|
5388
|
+
var ROUTE_EDGES2 = /^\/+|\/+$/gu;
|
|
5163
5389
|
var referencePagePath = (route) => {
|
|
5164
|
-
const segments = route.replace(
|
|
5390
|
+
const segments = route.replace(ROUTE_EDGES2, "");
|
|
5165
5391
|
return `${segments === "" ? "index" : segments}.astro`;
|
|
5166
5392
|
};
|
|
5167
|
-
var sourcesOf = (block) => {
|
|
5168
|
-
const sources = [...block.sources];
|
|
5169
|
-
if (block.spec) {
|
|
5170
|
-
sources.unshift({ spec: block.spec });
|
|
5171
|
-
}
|
|
5172
|
-
return sources;
|
|
5173
|
-
};
|
|
5174
|
-
var referencesFor = (kind, block, defaultLabel) => {
|
|
5175
|
-
if (!block.enabled) {
|
|
5176
|
-
return [];
|
|
5177
|
-
}
|
|
5178
|
-
const sources = sourcesOf(block);
|
|
5179
|
-
const base = normalizeRoute(block.route);
|
|
5180
|
-
return sources.map((source, index) => {
|
|
5181
|
-
const label = source.label ?? (sources.length > 1 ? `${defaultLabel} ${index + 1}` : defaultLabel);
|
|
5182
|
-
let route;
|
|
5183
|
-
if (source.route) {
|
|
5184
|
-
route = normalizeRoute(source.route);
|
|
5185
|
-
} else if (sources.length === 1) {
|
|
5186
|
-
route = base;
|
|
5187
|
-
} else {
|
|
5188
|
-
const suffix = source.label ? slugify(source.label) : "";
|
|
5189
|
-
route = normalizeRoute(`${base}/${suffix || index + 1}`);
|
|
5190
|
-
}
|
|
5191
|
-
return { kind, label, route, spec: source.spec, theme: block.theme };
|
|
5192
|
-
});
|
|
5193
|
-
};
|
|
5194
|
-
var resolveReferences = (config) => [
|
|
5195
|
-
...referencesFor("openapi", config.openapi, "API Reference"),
|
|
5196
|
-
...referencesFor("asyncapi", config.asyncapi, "Events")
|
|
5197
|
-
];
|
|
5198
|
-
var referenceTabs = (config) => resolveReferences(config).map((ref) => ({
|
|
5199
|
-
label: ref.label,
|
|
5200
|
-
path: ref.route
|
|
5201
|
-
}));
|
|
5202
|
-
var hasReferences = (config) => config.openapi.enabled || config.asyncapi.enabled;
|
|
5203
5393
|
var darkModeConfig = (mode) => {
|
|
5204
5394
|
if (mode === "dark") {
|
|
5205
5395
|
return { darkMode: true };
|
|
@@ -5224,9 +5414,9 @@ var specConfiguration = async (spec, root) => {
|
|
|
5224
5414
|
if (URL_SPEC.test(spec)) {
|
|
5225
5415
|
return { config: { url: spec } };
|
|
5226
5416
|
}
|
|
5227
|
-
const absolute =
|
|
5417
|
+
const absolute = isAbsolute4(spec) ? spec : join12(root, spec);
|
|
5228
5418
|
try {
|
|
5229
|
-
return { config: { content: await
|
|
5419
|
+
return { config: { content: await readFile5(absolute, "utf-8") } };
|
|
5230
5420
|
} catch {
|
|
5231
5421
|
return {
|
|
5232
5422
|
config: { url: spec },
|
|
@@ -5240,6 +5430,9 @@ var buildReferenceFiles = async (options) => {
|
|
|
5240
5430
|
const seen = new Set;
|
|
5241
5431
|
const accepted = [];
|
|
5242
5432
|
for (const ref of resolveReferences(config)) {
|
|
5433
|
+
if (ref.renderer !== "scalar") {
|
|
5434
|
+
continue;
|
|
5435
|
+
}
|
|
5243
5436
|
if (seen.has(ref.route)) {
|
|
5244
5437
|
warnings.push(`Two API reference sources resolve to ${ref.route}; keeping the first.`);
|
|
5245
5438
|
continue;
|
|
@@ -5278,37 +5471,315 @@ var buildReferenceFiles = async (options) => {
|
|
|
5278
5471
|
return { files, warnings };
|
|
5279
5472
|
};
|
|
5280
5473
|
|
|
5281
|
-
// src/
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5474
|
+
// src/core/sources/cache.ts
|
|
5475
|
+
import { mkdir as mkdir2, readFile as readFile6, writeFile as writeFile3 } from "node:fs/promises";
|
|
5476
|
+
import { join as join13 } from "pathe";
|
|
5477
|
+
var hashText = (text) => {
|
|
5478
|
+
let hash = 5381;
|
|
5479
|
+
for (let i = 0;i < text.length; i += 1) {
|
|
5480
|
+
hash = (hash * 33 + (text.codePointAt(i) ?? 0)) % 2147483647;
|
|
5481
|
+
}
|
|
5482
|
+
return hash.toString(36);
|
|
5483
|
+
};
|
|
5484
|
+
var entriesDigest = (entries) => hashText(entries.map((entry) => `${entry.ref}:${entry.hash ?? hashText(entry.body.text)}`).join("|"));
|
|
5485
|
+
var pollingWatch = (load2, intervalSeconds) => (onChange) => {
|
|
5486
|
+
let last = "";
|
|
5487
|
+
const tick = async () => {
|
|
5488
|
+
try {
|
|
5489
|
+
const { entries } = await load2();
|
|
5490
|
+
const next = entriesDigest(entries);
|
|
5491
|
+
if (last && next !== last) {
|
|
5492
|
+
onChange();
|
|
5493
|
+
}
|
|
5494
|
+
last = next;
|
|
5495
|
+
} catch {}
|
|
5496
|
+
};
|
|
5497
|
+
const timer = setInterval(() => {
|
|
5498
|
+
tick();
|
|
5499
|
+
}, intervalSeconds * 1000);
|
|
5500
|
+
return () => clearInterval(timer);
|
|
5501
|
+
};
|
|
5502
|
+
var snapshotCache = (cacheDir) => {
|
|
5503
|
+
const file = join13(cacheDir, "entries.json");
|
|
5504
|
+
return {
|
|
5505
|
+
read: async () => {
|
|
5506
|
+
try {
|
|
5507
|
+
return JSON.parse(await readFile6(file, "utf-8"));
|
|
5508
|
+
} catch {
|
|
5509
|
+
return [];
|
|
5510
|
+
}
|
|
5511
|
+
},
|
|
5512
|
+
write: async (entries) => {
|
|
5513
|
+
try {
|
|
5514
|
+
await mkdir2(cacheDir, { recursive: true });
|
|
5515
|
+
await writeFile3(file, `${JSON.stringify(entries)}
|
|
5516
|
+
`, "utf-8");
|
|
5517
|
+
} catch {}
|
|
5518
|
+
}
|
|
5519
|
+
};
|
|
5520
|
+
};
|
|
5521
|
+
var loadWithCache = async (name, cache, fetchEntries, refresh = true) => {
|
|
5522
|
+
if (!refresh) {
|
|
5523
|
+
const cached3 = await cache.read();
|
|
5524
|
+
if (cached3.length > 0) {
|
|
5525
|
+
return { diagnostics: [], entries: cached3 };
|
|
5526
|
+
}
|
|
5527
|
+
}
|
|
5528
|
+
try {
|
|
5529
|
+
const entries = await fetchEntries();
|
|
5530
|
+
await cache.write(entries);
|
|
5531
|
+
return { diagnostics: [], entries };
|
|
5532
|
+
} catch (error) {
|
|
5533
|
+
const fallback = await cache.read();
|
|
5534
|
+
if (fallback.length > 0) {
|
|
5535
|
+
const diagnostic = {
|
|
5536
|
+
code: "BLUME_SOURCE_OFFLINE",
|
|
5537
|
+
message: `Source "${name}" could not be fetched (${error.message}); served ${fallback.length} cached entries.`,
|
|
5538
|
+
severity: "warning"
|
|
5539
|
+
};
|
|
5540
|
+
return { diagnostics: [diagnostic], entries: fallback };
|
|
5541
|
+
}
|
|
5542
|
+
throw new BlumeError({
|
|
5543
|
+
code: "BLUME_SOURCE_FETCH_FAILED",
|
|
5544
|
+
message: `Source "${name}" failed to load and no cache is available: ${error.message}`,
|
|
5545
|
+
severity: "error"
|
|
5546
|
+
});
|
|
5547
|
+
}
|
|
5548
|
+
};
|
|
5549
|
+
|
|
5550
|
+
// src/openapi/model.ts
|
|
5551
|
+
var NON_SLUG2 = /[^a-z0-9]+/gu;
|
|
5552
|
+
var SLUG_EDGES2 = /^-+|-+$/gu;
|
|
5553
|
+
var slugify2 = (text) => text.toLowerCase().replace(NON_SLUG2, "-").replace(SLUG_EDGES2, "");
|
|
5554
|
+
var HTTP_METHODS = [
|
|
5555
|
+
"get",
|
|
5556
|
+
"put",
|
|
5557
|
+
"post",
|
|
5558
|
+
"delete",
|
|
5559
|
+
"options",
|
|
5560
|
+
"head",
|
|
5561
|
+
"patch",
|
|
5562
|
+
"trace"
|
|
5563
|
+
];
|
|
5564
|
+
var UNTAGGED = "Operations";
|
|
5565
|
+
var operationKey = (method, path, operationId) => {
|
|
5566
|
+
const fromId = operationId ? slugify2(operationId) : "";
|
|
5567
|
+
return fromId || slugify2(`${method}-${path}`);
|
|
5568
|
+
};
|
|
5569
|
+
var isOperation = (value) => typeof value === "object" && value !== null;
|
|
5570
|
+
var extractOperations = (document, baseRoute) => {
|
|
5571
|
+
const operations = [];
|
|
5572
|
+
const tagOrder = [];
|
|
5573
|
+
const tagMeta = new Map((document.tags ?? []).map((tag) => [tag.name, tag.description ?? ""]));
|
|
5574
|
+
const seen = new Set;
|
|
5575
|
+
for (const [path, rawItem] of Object.entries(document.paths ?? {})) {
|
|
5576
|
+
const item = rawItem;
|
|
5577
|
+
if (!item || "$ref" in item) {
|
|
5578
|
+
continue;
|
|
5579
|
+
}
|
|
5580
|
+
for (const method of HTTP_METHODS) {
|
|
5581
|
+
const operation = item[method];
|
|
5582
|
+
if (!isOperation(operation)) {
|
|
5583
|
+
continue;
|
|
5584
|
+
}
|
|
5585
|
+
const tag = operation.tags?.[0] ?? UNTAGGED;
|
|
5586
|
+
const tagSlug = slugify2(tag) || "operations";
|
|
5587
|
+
if (!tagOrder.includes(tag)) {
|
|
5588
|
+
tagOrder.push(tag);
|
|
5589
|
+
}
|
|
5590
|
+
let key = operationKey(method, path, operation.operationId);
|
|
5591
|
+
while (seen.has(key)) {
|
|
5592
|
+
key = `${key}-${method}`;
|
|
5593
|
+
}
|
|
5594
|
+
seen.add(key);
|
|
5595
|
+
operations.push({
|
|
5596
|
+
deprecated: operation.deprecated ?? false,
|
|
5597
|
+
description: operation.description ?? "",
|
|
5598
|
+
key,
|
|
5599
|
+
method,
|
|
5600
|
+
operationId: operation.operationId,
|
|
5601
|
+
path,
|
|
5602
|
+
route: `${baseRoute}/${tagSlug}/${key}`,
|
|
5603
|
+
summary: operation.summary ?? "",
|
|
5604
|
+
tag,
|
|
5605
|
+
tagSlug
|
|
5606
|
+
});
|
|
5607
|
+
}
|
|
5608
|
+
}
|
|
5609
|
+
const tags = tagOrder.map((name) => ({
|
|
5610
|
+
description: tagMeta.get(name) ?? "",
|
|
5611
|
+
name,
|
|
5612
|
+
slug: slugify2(name) || "operations"
|
|
5613
|
+
}));
|
|
5614
|
+
return { operations, tags };
|
|
5615
|
+
};
|
|
5616
|
+
|
|
5617
|
+
// src/openapi/parse.ts
|
|
5618
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
5619
|
+
import { normalize as normalize2, upgrade } from "@scalar/openapi-parser";
|
|
5620
|
+
import { isAbsolute as isAbsolute5, join as join14 } from "pathe";
|
|
5621
|
+
var URL_SPEC2 = /^https?:\/\//u;
|
|
5622
|
+
var readSpecText = async (spec, root) => {
|
|
5623
|
+
if (URL_SPEC2.test(spec)) {
|
|
5624
|
+
const response = await fetch(spec);
|
|
5625
|
+
if (!response.ok) {
|
|
5626
|
+
throw new Error(`${spec} -> ${response.status} ${response.statusText}`);
|
|
5627
|
+
}
|
|
5628
|
+
return await response.text();
|
|
5629
|
+
}
|
|
5630
|
+
const absolute = isAbsolute5(spec) ? spec : join14(root, spec);
|
|
5631
|
+
return await readFile7(absolute, "utf-8");
|
|
5632
|
+
};
|
|
5633
|
+
var parseSpec = async (spec, root) => {
|
|
5634
|
+
const text = await readSpecText(spec, root);
|
|
5635
|
+
const normalized = normalize2(text);
|
|
5636
|
+
const { specification } = upgrade(normalized);
|
|
5637
|
+
return { document: specification, warnings: [] };
|
|
5638
|
+
};
|
|
5639
|
+
|
|
5640
|
+
// src/openapi/render-mdx.ts
|
|
5641
|
+
var MDX_UNSAFE = /[<>{}]/gu;
|
|
5642
|
+
var ENTITIES = {
|
|
5643
|
+
"<": "<",
|
|
5644
|
+
">": ">",
|
|
5645
|
+
"{": "{",
|
|
5646
|
+
"}": "}"
|
|
5647
|
+
};
|
|
5648
|
+
var mdxSafe = (text) => text.replace(MDX_UNSAFE, (char) => ENTITIES[char] ?? char);
|
|
5649
|
+
var withDescription = (description, component) => description.trim() ? `${mdxSafe(description.trim())}
|
|
5650
|
+
|
|
5651
|
+
${component}` : component;
|
|
5652
|
+
var operationMdx = (spec, operation) => {
|
|
5653
|
+
const method = operation.method.toUpperCase();
|
|
5654
|
+
const title = operation.summary || `${method} ${operation.path}`;
|
|
5655
|
+
const description = operation.description.trim() === operation.summary.trim() ? "" : operation.description;
|
|
5656
|
+
return {
|
|
5657
|
+
body: withDescription(description, `<Operation source="${spec.slug}" id="${operation.key}" />`),
|
|
5658
|
+
data: {
|
|
5659
|
+
...operation.deprecated ? { deprecated: true } : {},
|
|
5660
|
+
search: { tags: [operation.tag, method] },
|
|
5661
|
+
sidebar: { badge: method, label: operation.summary || operation.path },
|
|
5662
|
+
title,
|
|
5663
|
+
type: "openapi-operation"
|
|
5664
|
+
}
|
|
5665
|
+
};
|
|
5666
|
+
};
|
|
5667
|
+
var overviewMdx = (spec) => ({
|
|
5668
|
+
body: withDescription(spec.description, `<ApiOverview source="${spec.slug}" />`),
|
|
5669
|
+
data: {
|
|
5670
|
+
sidebar: { label: "Overview" },
|
|
5671
|
+
title: spec.title || spec.label
|
|
5672
|
+
}
|
|
5673
|
+
});
|
|
5674
|
+
|
|
5675
|
+
// src/openapi/source.ts
|
|
5676
|
+
var isOpenApiSource = (source) => source.kind === "openapi-source";
|
|
5677
|
+
var routeToRef = (route) => route.replace(/^\/+/u, "");
|
|
5678
|
+
var toEntry = (rendered, ref) => {
|
|
5679
|
+
const raw = frontmatter_default.stringify(`${rendered.body}
|
|
5680
|
+
`, rendered.data);
|
|
5681
|
+
return {
|
|
5682
|
+
body: { format: "mdx", text: rendered.body },
|
|
5683
|
+
data: rendered.data,
|
|
5684
|
+
hash: hashText(raw),
|
|
5685
|
+
raw,
|
|
5686
|
+
ref
|
|
5687
|
+
};
|
|
5688
|
+
};
|
|
5689
|
+
var specEntries = (spec, operations) => {
|
|
5690
|
+
const entries = operations.map((operation) => toEntry(operationMdx(spec, operation), `${routeToRef(operation.route)}.mdx`));
|
|
5691
|
+
entries.push(toEntry(overviewMdx(spec), `${routeToRef(spec.route)}/index.mdx`));
|
|
5692
|
+
return entries;
|
|
5693
|
+
};
|
|
5694
|
+
var openApiSource = (references, ctx) => {
|
|
5695
|
+
let parsed = {};
|
|
5696
|
+
const loadReference = async (reference) => {
|
|
5697
|
+
try {
|
|
5698
|
+
const { document } = await parseSpec(reference.spec, ctx.projectRoot);
|
|
5699
|
+
const { operations, tags } = extractOperations(document, reference.route);
|
|
5700
|
+
const info = document.info ?? { title: reference.label, version: "" };
|
|
5701
|
+
const spec = {
|
|
5702
|
+
codeSamples: reference.display.codeSamples,
|
|
5703
|
+
description: info.description ?? "",
|
|
5704
|
+
document,
|
|
5705
|
+
expandSchemas: reference.display.expandSchemas,
|
|
5706
|
+
label: reference.label,
|
|
5707
|
+
operations: Object.fromEntries(operations.map((operation) => [operation.key, operation])),
|
|
5708
|
+
route: reference.route,
|
|
5709
|
+
slug: reference.slug,
|
|
5710
|
+
tags,
|
|
5711
|
+
title: info.title ?? reference.label,
|
|
5712
|
+
version: info.version ?? ""
|
|
5713
|
+
};
|
|
5714
|
+
return {
|
|
5715
|
+
entries: specEntries(spec, operations),
|
|
5716
|
+
slug: reference.slug,
|
|
5717
|
+
spec
|
|
5718
|
+
};
|
|
5719
|
+
} catch (error) {
|
|
5720
|
+
return {
|
|
5721
|
+
code: "BLUME_OPENAPI_UNAVAILABLE",
|
|
5722
|
+
message: `Could not load OpenAPI spec "${reference.spec}" for ${reference.route} (${error.message}); its reference pages were skipped.`,
|
|
5723
|
+
severity: "warning"
|
|
5724
|
+
};
|
|
5725
|
+
}
|
|
5726
|
+
};
|
|
5727
|
+
const load2 = async () => {
|
|
5728
|
+
const results = await Promise.all(references.map(loadReference));
|
|
5729
|
+
const entries = [];
|
|
5730
|
+
const diagnostics = [];
|
|
5731
|
+
const data = {};
|
|
5732
|
+
for (const result of results) {
|
|
5733
|
+
if ("severity" in result) {
|
|
5734
|
+
diagnostics.push(result);
|
|
5735
|
+
continue;
|
|
5736
|
+
}
|
|
5737
|
+
data[result.slug] = result.spec;
|
|
5738
|
+
entries.push(...result.entries);
|
|
5739
|
+
}
|
|
5740
|
+
parsed = data;
|
|
5741
|
+
return { diagnostics, entries };
|
|
5742
|
+
};
|
|
5743
|
+
return {
|
|
5744
|
+
kind: "openapi-source",
|
|
5745
|
+
load: load2,
|
|
5746
|
+
name: "openapi",
|
|
5747
|
+
openApiData: () => parsed,
|
|
5748
|
+
staged: true
|
|
5749
|
+
};
|
|
5750
|
+
};
|
|
5751
|
+
|
|
5752
|
+
// src/theme/entry.ts
|
|
5753
|
+
var tailwindEntryTemplate = (options) => `/* Generated by Blume. Do not edit. */
|
|
5754
|
+
@import "tailwindcss";
|
|
5755
|
+
@plugin "@tailwindcss/typography";
|
|
5756
|
+
|
|
5757
|
+
/* Scan Blume's components and the user's project for utility classes. */
|
|
5758
|
+
${options.sources.map((source) => `@source "${source}";`).join(`
|
|
5759
|
+
`)}
|
|
5760
|
+
|
|
5761
|
+
/* Dark mode is driven by data-theme on the <html> element. */
|
|
5762
|
+
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
|
|
5763
|
+
|
|
5764
|
+
:root {
|
|
5765
|
+
--blume-background: oklch(1 0 0);
|
|
5766
|
+
--blume-background-decoration: none;
|
|
5767
|
+
--blume-background-decoration-repeat: no-repeat;
|
|
5768
|
+
--blume-background-decoration-size: auto;
|
|
5769
|
+
--blume-background-image: none;
|
|
5770
|
+
--blume-background-image-repeat: no-repeat;
|
|
5771
|
+
--blume-background-image-size: cover;
|
|
5772
|
+
--blume-foreground: oklch(0.145 0 0);
|
|
5773
|
+
--blume-muted: oklch(0.965 0 0);
|
|
5774
|
+
--blume-muted-foreground: oklch(0.54 0 0);
|
|
5775
|
+
--blume-border: oklch(0.88 0.006 260 / 0.72);
|
|
5776
|
+
--blume-accent: oklch(0.145 0 0);
|
|
5777
|
+
--blume-accent-foreground: oklch(1 0 0);
|
|
5778
|
+
--blume-action: var(--blume-accent);
|
|
5779
|
+
--blume-action-foreground: var(--blume-accent-foreground);
|
|
5780
|
+
--blume-code-background: oklch(0.99 0 0);
|
|
5781
|
+
/* Shiki notation transformers: line/word highlight, diff add/remove. */
|
|
5782
|
+
--blume-code-highlight: oklch(0.55 0.16 255 / 0.1);
|
|
5312
5783
|
--blume-code-highlight-border: oklch(0.55 0.16 255 / 0.55);
|
|
5313
5784
|
--blume-code-add: oklch(0.72 0.16 150 / 0.16);
|
|
5314
5785
|
--blume-code-add-border: oklch(0.52 0.15 150 / 0.7);
|
|
@@ -5897,9 +6368,9 @@ ${options.userTheme}
|
|
|
5897
6368
|
`;
|
|
5898
6369
|
|
|
5899
6370
|
// src/theme/twoslash.ts
|
|
5900
|
-
import { readFileSync as
|
|
5901
|
-
import { createRequire as
|
|
5902
|
-
var require2 =
|
|
6371
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
6372
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
6373
|
+
var require2 = createRequire4(import.meta.url);
|
|
5903
6374
|
var OVERRIDES = `
|
|
5904
6375
|
/* Twoslash: theme the rich renderer with Blume tokens. */
|
|
5905
6376
|
:root {
|
|
@@ -5959,7 +6430,7 @@ var OVERRIDES = `
|
|
|
5959
6430
|
`;
|
|
5960
6431
|
var twoslashCss = () => {
|
|
5961
6432
|
const file = require2.resolve("@shikijs/twoslash/style-rich.css");
|
|
5962
|
-
return `${
|
|
6433
|
+
return `${readFileSync5(file, "utf-8")}
|
|
5963
6434
|
${OVERRIDES}`;
|
|
5964
6435
|
};
|
|
5965
6436
|
|
|
@@ -6061,13 +6532,13 @@ export const layoutOverrides = { ...(overrides.layout ?? {})${layoutEntries.leng
|
|
|
6061
6532
|
};
|
|
6062
6533
|
|
|
6063
6534
|
// src/astro/examples.ts
|
|
6064
|
-
import { readFile as
|
|
6065
|
-
import { join as
|
|
6535
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
6536
|
+
import { join as join16, relative as relative4 } from "pathe";
|
|
6066
6537
|
import { glob as glob2 } from "tinyglobby";
|
|
6067
6538
|
|
|
6068
6539
|
// src/astro/islands.ts
|
|
6069
|
-
import { readFile as
|
|
6070
|
-
import { basename, join as
|
|
6540
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
6541
|
+
import { basename, join as join15 } from "pathe";
|
|
6071
6542
|
import { glob } from "tinyglobby";
|
|
6072
6543
|
var DEFAULT_CLIENT = "visible";
|
|
6073
6544
|
var VALID_MODES = new Set([
|
|
@@ -6096,14 +6567,14 @@ var readClientMode = (source, file, warnings) => {
|
|
|
6096
6567
|
return mode;
|
|
6097
6568
|
};
|
|
6098
6569
|
var discoverIslands = async (root) => {
|
|
6099
|
-
const dir =
|
|
6570
|
+
const dir = join15(root, "islands");
|
|
6100
6571
|
const matches = await glob(["**/*.{jsx,svelte,tsx,vue}"], {
|
|
6101
6572
|
absolute: true,
|
|
6102
6573
|
cwd: dir,
|
|
6103
6574
|
onlyFiles: true
|
|
6104
6575
|
});
|
|
6105
6576
|
const files = matches.toSorted();
|
|
6106
|
-
const sources = await Promise.all(files.map((file) =>
|
|
6577
|
+
const sources = await Promise.all(files.map((file) => readFile8(file, "utf-8")));
|
|
6107
6578
|
const islands = [];
|
|
6108
6579
|
const warnings = [];
|
|
6109
6580
|
const seen = new Map;
|
|
@@ -6115,8 +6586,8 @@ var discoverIslands = async (root) => {
|
|
|
6115
6586
|
continue;
|
|
6116
6587
|
}
|
|
6117
6588
|
const name = base.replace(ISLAND_FILE, "");
|
|
6118
|
-
if (!/^[A-Z]
|
|
6119
|
-
warnings.push(`Island "${file}" must have a PascalCase filename to be used in MDX (e.g. Counter.tsx → <Counter />); skipping it.`);
|
|
6589
|
+
if (!/^[A-Z][A-Za-z0-9_]*$/u.test(name)) {
|
|
6590
|
+
warnings.push(`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.`);
|
|
6120
6591
|
continue;
|
|
6121
6592
|
}
|
|
6122
6593
|
const existing = seen.get(name);
|
|
@@ -6159,14 +6630,14 @@ var splitGlobBase = (pattern) => {
|
|
|
6159
6630
|
};
|
|
6160
6631
|
var discoverExamples = async (root, pattern = "examples") => {
|
|
6161
6632
|
const { base, rest } = GLOB_MAGIC.test(pattern) ? splitGlobBase(pattern) : { base: pattern, rest: DEFAULT_EXAMPLE_GLOB };
|
|
6162
|
-
const dir =
|
|
6633
|
+
const dir = join16(root, base);
|
|
6163
6634
|
const matches = await glob2([rest], {
|
|
6164
6635
|
absolute: true,
|
|
6165
6636
|
cwd: dir,
|
|
6166
6637
|
onlyFiles: true
|
|
6167
6638
|
});
|
|
6168
6639
|
const files = matches.toSorted();
|
|
6169
|
-
const sources = await Promise.all(files.map((file) =>
|
|
6640
|
+
const sources = await Promise.all(files.map((file) => readFile9(file, "utf-8")));
|
|
6170
6641
|
const examples = [];
|
|
6171
6642
|
const warnings = [];
|
|
6172
6643
|
const seen = new Map;
|
|
@@ -6209,7 +6680,10 @@ var discoverPages = async (pagesRoot) => {
|
|
|
6209
6680
|
return files.map((file) => {
|
|
6210
6681
|
const rel = relative5(pagesRoot, file);
|
|
6211
6682
|
const withoutExt = rel.slice(0, rel.length - extname2(rel).length);
|
|
6212
|
-
const parts = withoutExt.split("/")
|
|
6683
|
+
const parts = withoutExt.split("/");
|
|
6684
|
+
if (parts.at(-1) === "index") {
|
|
6685
|
+
parts.pop();
|
|
6686
|
+
}
|
|
6213
6687
|
const pattern = parts.length === 0 ? "/" : `/${parts.join("/")}`;
|
|
6214
6688
|
return { entrypoint: file, pattern };
|
|
6215
6689
|
});
|
|
@@ -6237,10 +6711,10 @@ var customOgRoutes = (pages, siteTitle) => {
|
|
|
6237
6711
|
};
|
|
6238
6712
|
|
|
6239
6713
|
// src/astro/generate.ts
|
|
6240
|
-
var BLUME_SRC =
|
|
6714
|
+
var BLUME_SRC = join17(packageRoot(), "src");
|
|
6241
6715
|
var canResolveFrom = (fromDir, spec) => {
|
|
6242
6716
|
try {
|
|
6243
|
-
|
|
6717
|
+
createRequire5(pathToFileURL2(join17(fromDir, "_.js")).href).resolve(spec);
|
|
6244
6718
|
return true;
|
|
6245
6719
|
} catch {
|
|
6246
6720
|
return false;
|
|
@@ -6248,15 +6722,15 @@ var canResolveFrom = (fromDir, spec) => {
|
|
|
6248
6722
|
};
|
|
6249
6723
|
var resolvedAstroPath = (fromDir) => {
|
|
6250
6724
|
try {
|
|
6251
|
-
const pkg =
|
|
6725
|
+
const pkg = createRequire5(pathToFileURL2(join17(fromDir, "_.js")).href).resolve("astro/package.json");
|
|
6252
6726
|
return realpathSync(pkg);
|
|
6253
6727
|
} catch {
|
|
6254
6728
|
return null;
|
|
6255
6729
|
}
|
|
6256
6730
|
};
|
|
6257
6731
|
var blumeDepsDir = (pkgDir = packageRoot()) => {
|
|
6258
|
-
const candidates = [
|
|
6259
|
-
return candidates.find((dir) =>
|
|
6732
|
+
const candidates = [join17(pkgDir, "node_modules"), dirname7(pkgDir)];
|
|
6733
|
+
return candidates.find((dir) => existsSync9(join17(dir, "astro"))) ?? null;
|
|
6260
6734
|
};
|
|
6261
6735
|
var linkDepsJunction = async (link, depsDir) => {
|
|
6262
6736
|
const existing = await lstat(link).catch(() => null);
|
|
@@ -6266,7 +6740,7 @@ var linkDepsJunction = async (link, depsDir) => {
|
|
|
6266
6740
|
}
|
|
6267
6741
|
await rm(link, { force: true });
|
|
6268
6742
|
}
|
|
6269
|
-
await
|
|
6743
|
+
await mkdir3(dirname7(link), { recursive: true });
|
|
6270
6744
|
await symlink(depsDir, link, "junction");
|
|
6271
6745
|
};
|
|
6272
6746
|
var readPkgVersion = (pkgJsonPath) => {
|
|
@@ -6274,7 +6748,7 @@ var readPkgVersion = (pkgJsonPath) => {
|
|
|
6274
6748
|
return null;
|
|
6275
6749
|
}
|
|
6276
6750
|
try {
|
|
6277
|
-
return JSON.parse(
|
|
6751
|
+
return JSON.parse(readFileSync6(pkgJsonPath, "utf-8")).version ?? null;
|
|
6278
6752
|
} catch {
|
|
6279
6753
|
return null;
|
|
6280
6754
|
}
|
|
@@ -6296,8 +6770,8 @@ var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
|
|
|
6296
6770
|
if (blumeAstro && outDirAstro === blumeAstro) {
|
|
6297
6771
|
return null;
|
|
6298
6772
|
}
|
|
6299
|
-
if (
|
|
6300
|
-
await linkDepsJunction(
|
|
6773
|
+
if (existsSync9(join17(depsDir, "@astrojs", "mdx"))) {
|
|
6774
|
+
await linkDepsJunction(join17(outDir, "node_modules"), depsDir);
|
|
6301
6775
|
return null;
|
|
6302
6776
|
}
|
|
6303
6777
|
return astroConflictWarning(blumeAstro, outDirAstro);
|
|
@@ -6321,7 +6795,7 @@ var readOptional = async (path) => {
|
|
|
6321
6795
|
return "";
|
|
6322
6796
|
}
|
|
6323
6797
|
try {
|
|
6324
|
-
return await
|
|
6798
|
+
return await readFile10(path, "utf-8");
|
|
6325
6799
|
} catch {
|
|
6326
6800
|
return "";
|
|
6327
6801
|
}
|
|
@@ -6337,16 +6811,16 @@ var detectNeedsReact = async (root) => {
|
|
|
6337
6811
|
var writeIfChanged = async (path, content) => {
|
|
6338
6812
|
let existing = null;
|
|
6339
6813
|
try {
|
|
6340
|
-
existing = await
|
|
6814
|
+
existing = await readFile10(path, "utf-8");
|
|
6341
6815
|
} catch {
|
|
6342
6816
|
existing = null;
|
|
6343
6817
|
}
|
|
6344
6818
|
if (existing === content) {
|
|
6345
6819
|
return false;
|
|
6346
6820
|
}
|
|
6347
|
-
await
|
|
6821
|
+
await mkdir3(dirname7(path), { recursive: true });
|
|
6348
6822
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
6349
|
-
await
|
|
6823
|
+
await writeFile4(tmp, content, "utf-8");
|
|
6350
6824
|
try {
|
|
6351
6825
|
await rename(tmp, path);
|
|
6352
6826
|
} catch (error) {
|
|
@@ -6361,7 +6835,7 @@ var pruneOrphans = async (srcDir, written) => {
|
|
|
6361
6835
|
cwd: srcDir,
|
|
6362
6836
|
onlyFiles: true
|
|
6363
6837
|
});
|
|
6364
|
-
await Promise.all(existing.map((path) =>
|
|
6838
|
+
await Promise.all(existing.map((path) => normalize3(path)).filter((path) => !written.has(path)).map((path) => rm(path, { force: true })));
|
|
6365
6839
|
};
|
|
6366
6840
|
var collectStaged = (project) => {
|
|
6367
6841
|
const staged = new Map;
|
|
@@ -6376,11 +6850,11 @@ var writeStagedContent = async (out, staged) => {
|
|
|
6376
6850
|
const contentDir = stagedContentDir(out);
|
|
6377
6851
|
const written = new Set;
|
|
6378
6852
|
await Promise.all([...staged].map(async ([entryId, text]) => {
|
|
6379
|
-
const path =
|
|
6380
|
-
written.add(
|
|
6853
|
+
const path = join17(contentDir, entryId);
|
|
6854
|
+
written.add(normalize3(path));
|
|
6381
6855
|
await writeIfChanged(path, text);
|
|
6382
6856
|
}));
|
|
6383
|
-
if (
|
|
6857
|
+
if (existsSync9(contentDir)) {
|
|
6384
6858
|
await pruneOrphans(contentDir, written);
|
|
6385
6859
|
}
|
|
6386
6860
|
};
|
|
@@ -6397,11 +6871,11 @@ var resolveLogo = (project) => {
|
|
|
6397
6871
|
if (light && light === dark && light.toLowerCase().endsWith(".svg")) {
|
|
6398
6872
|
const rel = light.replace(/^\//u, "");
|
|
6399
6873
|
const file = [
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
].find((path) =>
|
|
6874
|
+
join17(project.context.root, "public", rel),
|
|
6875
|
+
join17(project.context.root, rel)
|
|
6876
|
+
].find((path) => existsSync9(path));
|
|
6403
6877
|
if (file) {
|
|
6404
|
-
return { alt, href, svg:
|
|
6878
|
+
return { alt, href, svg: readFileSync6(file, "utf-8") };
|
|
6405
6879
|
}
|
|
6406
6880
|
}
|
|
6407
6881
|
return { alt, dark, href, light };
|
|
@@ -6425,9 +6899,9 @@ var faviconType = (name) => {
|
|
|
6425
6899
|
const ext = name.split(".").pop()?.toLowerCase();
|
|
6426
6900
|
return ext ? FAVICON_TYPES[ext] : undefined;
|
|
6427
6901
|
};
|
|
6428
|
-
var inlineDataUri = (file, type) => `data:${type};base64,${
|
|
6902
|
+
var inlineDataUri = (file, type) => `data:${type};base64,${readFileSync6(file).toString("base64")}`;
|
|
6429
6903
|
var defaultFavicon = () => ({
|
|
6430
|
-
href: inlineDataUri(
|
|
6904
|
+
href: inlineDataUri(join17(BLUME_SRC, "assets", "icon.png"), "image/png"),
|
|
6431
6905
|
type: "image/png"
|
|
6432
6906
|
});
|
|
6433
6907
|
var APPLE_ICON_CANDIDATES = [
|
|
@@ -6439,13 +6913,13 @@ var APPLE_ICON_CANDIDATES = [
|
|
|
6439
6913
|
var resolveIconFile = (project, candidates) => {
|
|
6440
6914
|
const { root } = project.context;
|
|
6441
6915
|
for (const name of candidates) {
|
|
6442
|
-
if (
|
|
6916
|
+
if (existsSync9(join17(root, "public", name))) {
|
|
6443
6917
|
return { href: `/${name}`, type: faviconType(name) };
|
|
6444
6918
|
}
|
|
6445
6919
|
}
|
|
6446
6920
|
for (const name of candidates) {
|
|
6447
|
-
const file =
|
|
6448
|
-
if (
|
|
6921
|
+
const file = join17(root, name);
|
|
6922
|
+
if (existsSync9(file)) {
|
|
6449
6923
|
const type = faviconType(name);
|
|
6450
6924
|
return { href: inlineDataUri(file, type ?? "image/x-icon"), type };
|
|
6451
6925
|
}
|
|
@@ -6527,6 +7001,7 @@ var buildRuntimeData = (project) => {
|
|
|
6527
7001
|
label
|
|
6528
7002
|
}))
|
|
6529
7003
|
} : null,
|
|
7004
|
+
icons: config.icons,
|
|
6530
7005
|
imageZoom: config.markdown.imageZoom,
|
|
6531
7006
|
logo: resolveLogo(project),
|
|
6532
7007
|
mcp: config.mcp.enabled ? { name: config.mcp.name ?? config.title, route: config.mcp.route } : null,
|
|
@@ -6573,7 +7048,7 @@ var buildRuntimeData = (project) => {
|
|
|
6573
7048
|
var planMcp = (project, srcDir) => {
|
|
6574
7049
|
const { config } = project;
|
|
6575
7050
|
const { route } = config.mcp;
|
|
6576
|
-
const dir =
|
|
7051
|
+
const dir = join17(srcDir, "blume-mcp");
|
|
6577
7052
|
const base = {
|
|
6578
7053
|
dir,
|
|
6579
7054
|
discoveryPages: [],
|
|
@@ -6597,11 +7072,11 @@ var planMcp = (project, srcDir) => {
|
|
|
6597
7072
|
...base,
|
|
6598
7073
|
discoveryPages: [
|
|
6599
7074
|
{
|
|
6600
|
-
entrypoint:
|
|
7075
|
+
entrypoint: join17(dir, "discovery.ts"),
|
|
6601
7076
|
pattern: "/.well-known/mcp.json"
|
|
6602
7077
|
},
|
|
6603
7078
|
{
|
|
6604
|
-
entrypoint:
|
|
7079
|
+
entrypoint: join17(dir, "server-card.ts"),
|
|
6605
7080
|
pattern: "/.well-known/mcp/server-card.json"
|
|
6606
7081
|
}
|
|
6607
7082
|
],
|
|
@@ -6620,11 +7095,11 @@ var writeMcpFiles = async (project, plan, write) => {
|
|
|
6620
7095
|
version: data.version
|
|
6621
7096
|
};
|
|
6622
7097
|
await Promise.all([
|
|
6623
|
-
write(
|
|
7098
|
+
write(join17(plan.srcDir, "generated", "mcp-data.json"), `${JSON.stringify(data)}
|
|
6624
7099
|
`),
|
|
6625
|
-
write(
|
|
6626
|
-
write(
|
|
6627
|
-
write(
|
|
7100
|
+
write(join17(plan.srcDir, "pages", mcpPageFile(plan.route)), mcpEndpointTemplate(plan.route)),
|
|
7101
|
+
write(join17(plan.dir, "discovery.ts"), staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput))),
|
|
7102
|
+
write(join17(plan.dir, "server-card.ts"), staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)))
|
|
6628
7103
|
]);
|
|
6629
7104
|
};
|
|
6630
7105
|
var writeAskFiles = async (project, srcDir, write) => {
|
|
@@ -6634,16 +7109,16 @@ var writeAskFiles = async (project, srcDir, write) => {
|
|
|
6634
7109
|
}
|
|
6635
7110
|
const grounded = ask.provider !== "inkeep";
|
|
6636
7111
|
if (grounded) {
|
|
6637
|
-
await write(
|
|
7112
|
+
await write(join17(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
|
|
6638
7113
|
`);
|
|
6639
7114
|
}
|
|
6640
|
-
await write(
|
|
7115
|
+
await write(join17(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
|
|
6641
7116
|
};
|
|
6642
7117
|
var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
|
|
6643
7118
|
if (routeIsTaken(pages, contentPages, "/404")) {
|
|
6644
7119
|
return;
|
|
6645
7120
|
}
|
|
6646
|
-
await write(
|
|
7121
|
+
await write(join17(srcDir, "pages", "404.astro"), notFoundPageTemplate());
|
|
6647
7122
|
};
|
|
6648
7123
|
var shouldGenerateChangelog = (project) => {
|
|
6649
7124
|
const hasChangelog = project.graph.pages.some((page) => page.contentType === "changelog" && !(page.meta.draft || page.meta.sidebar.hidden));
|
|
@@ -6652,7 +7127,7 @@ var shouldGenerateChangelog = (project) => {
|
|
|
6652
7127
|
return (hasChangelog || hasChangelogSource) && !changelogRouteTaken;
|
|
6653
7128
|
};
|
|
6654
7129
|
var buildComponentSlots = async (componentsFile) => {
|
|
6655
|
-
const analysis = componentsFile ? analyzeComponentOverrides(await
|
|
7130
|
+
const analysis = componentsFile ? analyzeComponentOverrides(await readFile10(componentsFile, "utf-8"), componentsFile) : null;
|
|
6656
7131
|
return {
|
|
6657
7132
|
plan: planComponentSlots(componentsFile, analysis),
|
|
6658
7133
|
tags: analysis ? [...analysis.mdx, ...analysis.islands].map((entry) => entry.key) : [],
|
|
@@ -6662,14 +7137,15 @@ var buildComponentSlots = async (componentsFile) => {
|
|
|
6662
7137
|
var generateRuntime = async (project) => {
|
|
6663
7138
|
const { context, config } = project;
|
|
6664
7139
|
const out = context.outDir;
|
|
6665
|
-
const srcDir =
|
|
6666
|
-
const dataPath =
|
|
6667
|
-
const themePath =
|
|
6668
|
-
const searchClientPath =
|
|
6669
|
-
const examplesPath =
|
|
7140
|
+
const srcDir = join17(out, "src");
|
|
7141
|
+
const dataPath = join17(srcDir, "generated", "data.json");
|
|
7142
|
+
const themePath = join17(srcDir, "generated", "app.css");
|
|
7143
|
+
const searchClientPath = join17(srcDir, "generated", "search-client.ts");
|
|
7144
|
+
const examplesPath = join17(srcDir, "generated", "examples.ts");
|
|
7145
|
+
const openapiPath = join17(srcDir, "generated", "openapi.json");
|
|
6670
7146
|
const written = new Set;
|
|
6671
7147
|
const write = (path, content) => {
|
|
6672
|
-
written.add(
|
|
7148
|
+
written.add(normalize3(path));
|
|
6673
7149
|
return writeIfChanged(path, content);
|
|
6674
7150
|
};
|
|
6675
7151
|
const depsLinkWarning = await ensureDepsLink(out);
|
|
@@ -6702,7 +7178,7 @@ var generateRuntime = async (project) => {
|
|
|
6702
7178
|
const staged = collectStaged(project);
|
|
6703
7179
|
const hasStaged = staged.size > 0;
|
|
6704
7180
|
const structural = await Promise.all([
|
|
6705
|
-
write(
|
|
7181
|
+
write(join17(out, "astro.config.mjs"), astroConfigTemplate({
|
|
6706
7182
|
aliases: resolveTsconfigAliases(context.root),
|
|
6707
7183
|
config,
|
|
6708
7184
|
contentRoutes: project.manifest.routes.map((route) => route.path),
|
|
@@ -6712,24 +7188,25 @@ var generateRuntime = async (project) => {
|
|
|
6712
7188
|
needsReact,
|
|
6713
7189
|
needsSvelte,
|
|
6714
7190
|
needsVue,
|
|
7191
|
+
openapiPath,
|
|
6715
7192
|
pages,
|
|
6716
7193
|
searchClientPath,
|
|
6717
7194
|
themePath
|
|
6718
7195
|
})),
|
|
6719
|
-
write(
|
|
6720
|
-
write(
|
|
6721
|
-
write(
|
|
6722
|
-
write(
|
|
6723
|
-
write(
|
|
7196
|
+
write(join17(out, "package.json"), runtimePackageTemplate(runtimeDependencies({ config, needsReact, needsSvelte, needsVue }))),
|
|
7197
|
+
write(join17(out, "tsconfig.json"), runtimeTsconfigTemplate()),
|
|
7198
|
+
write(join17(srcDir, "env.d.ts"), envTemplate()),
|
|
7199
|
+
write(join17(srcDir, "content.config.ts"), contentConfigTemplate({ config, context, staged: hasStaged })),
|
|
7200
|
+
write(join17(srcDir, "pages", "[...slug].astro"), catchAllPageTemplate({
|
|
6724
7201
|
askEnabled,
|
|
6725
7202
|
exportEpub,
|
|
6726
7203
|
exportPdf,
|
|
6727
7204
|
mathEnabled: config.markdown.math,
|
|
6728
7205
|
needsReact
|
|
6729
7206
|
})),
|
|
6730
|
-
write(
|
|
6731
|
-
write(
|
|
6732
|
-
write(
|
|
7207
|
+
write(join17(srcDir, "generated", "components.ts"), slotPlan.module),
|
|
7208
|
+
write(join17(srcDir, "generated", "islands.ts"), islandMapTemplate(islandDiscovery.islands)),
|
|
7209
|
+
write(join17(srcDir, "generated", "examples.ts"), exampleMapTemplate(exampleDiscovery.examples)),
|
|
6733
7210
|
write(themePath, tailwindEntryTemplate({
|
|
6734
7211
|
configTokens: `${buildThemeCss(config.theme)}${buildFontsCss(config.theme.fonts)}`,
|
|
6735
7212
|
sources: [
|
|
@@ -6740,16 +7217,16 @@ var generateRuntime = async (project) => {
|
|
|
6740
7217
|
userTheme
|
|
6741
7218
|
}))
|
|
6742
7219
|
]);
|
|
6743
|
-
await Promise.all(islandDiscovery.islands.map((island) => write(
|
|
6744
|
-
await Promise.all(slotPlan.wrappers.map((wrapper) => write(
|
|
6745
|
-
await Promise.all(exampleDiscovery.examples.map((example) => write(
|
|
7220
|
+
await Promise.all(islandDiscovery.islands.map((island) => write(join17(srcDir, "generated", "islands", `${island.name}.astro`), islandWrapperTemplate(island))));
|
|
7221
|
+
await Promise.all(slotPlan.wrappers.map((wrapper) => write(join17(srcDir, "generated", "component-slots", `${wrapper.name}.astro`), wrapper.content)));
|
|
7222
|
+
await Promise.all(exampleDiscovery.examples.map((example) => write(join17(srcDir, "generated", "examples", `${exampleSlug(example.path)}.astro`), exampleWrapperTemplate(example))));
|
|
6746
7223
|
await writeAskFiles(project, srcDir, write);
|
|
6747
7224
|
await writeMcpFiles(project, mcp, write);
|
|
6748
7225
|
if (config.seo.og.enabled) {
|
|
6749
|
-
await write(
|
|
7226
|
+
await write(join17(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes));
|
|
6750
7227
|
}
|
|
6751
7228
|
if (shouldGenerateChangelog(project)) {
|
|
6752
|
-
await write(
|
|
7229
|
+
await write(join17(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
|
|
6753
7230
|
askEnabled,
|
|
6754
7231
|
exportEpub,
|
|
6755
7232
|
exportPdf,
|
|
@@ -6761,27 +7238,27 @@ var generateRuntime = async (project) => {
|
|
|
6761
7238
|
await write(searchClientPath, searchClientTemplate(config));
|
|
6762
7239
|
if (servesStaticIndex(config.search.provider)) {
|
|
6763
7240
|
const documents = await buildSearchDocuments(project);
|
|
6764
|
-
await write(
|
|
7241
|
+
await write(join17(srcDir, "generated", "search.json"), `${JSON.stringify(documents)}
|
|
6765
7242
|
`);
|
|
6766
|
-
await write(
|
|
7243
|
+
await write(join17(srcDir, "pages", "blume-search.json.ts"), searchEndpointTemplate());
|
|
6767
7244
|
}
|
|
6768
7245
|
if (config.search.provider === "mixedbread") {
|
|
6769
|
-
await write(
|
|
7246
|
+
await write(join17(srcDir, "pages", "api", "search.ts"), mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""));
|
|
6770
7247
|
}
|
|
6771
7248
|
const rawMarkdown = await buildRawMarkdown(project);
|
|
6772
7249
|
await Promise.all([
|
|
6773
|
-
write(
|
|
7250
|
+
write(join17(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
|
|
6774
7251
|
`),
|
|
6775
|
-
write(
|
|
6776
|
-
write(
|
|
7252
|
+
write(join17(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate()),
|
|
7253
|
+
write(join17(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate())
|
|
6777
7254
|
]);
|
|
6778
7255
|
const feeds = buildRssFeeds(project);
|
|
6779
7256
|
if (feeds.length > 0) {
|
|
6780
7257
|
const feedXml = Object.fromEntries(feeds.map((feed) => [feed.type, renderRssFeed(feed)]));
|
|
6781
7258
|
await Promise.all([
|
|
6782
|
-
write(
|
|
7259
|
+
write(join17(srcDir, "generated", "rss.json"), `${JSON.stringify(feedXml)}
|
|
6783
7260
|
`),
|
|
6784
|
-
write(
|
|
7261
|
+
write(join17(srcDir, "pages", "[section]", "rss.xml.ts"), rssEndpointTemplate())
|
|
6785
7262
|
]);
|
|
6786
7263
|
}
|
|
6787
7264
|
const warnings = [
|
|
@@ -6811,17 +7288,20 @@ var generateRuntime = async (project) => {
|
|
|
6811
7288
|
}
|
|
6812
7289
|
}
|
|
6813
7290
|
warnings.push(...islandFrameworkWarnings(frameworks, context.root));
|
|
6814
|
-
if (
|
|
7291
|
+
if (hasScalarReferences(config)) {
|
|
6815
7292
|
const references = await buildReferenceFiles({
|
|
6816
7293
|
config,
|
|
6817
7294
|
contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
|
|
6818
7295
|
root: context.root
|
|
6819
7296
|
});
|
|
6820
7297
|
warnings.push(...references.warnings);
|
|
6821
|
-
await Promise.all(references.files.map((file) => write(
|
|
7298
|
+
await Promise.all(references.files.map((file) => write(join17(srcDir, "pages", file.pagePath), file.content)));
|
|
6822
7299
|
}
|
|
6823
|
-
await write(
|
|
6824
|
-
|
|
7300
|
+
await write(join17(srcDir, "generated", "data.json"), buildRuntimeData(project));
|
|
7301
|
+
const openApiSource2 = project.sources.find(isOpenApiSource);
|
|
7302
|
+
await write(openapiPath, `${JSON.stringify(openApiSource2 ? openApiSource2.openApiData() : {})}
|
|
7303
|
+
`);
|
|
7304
|
+
await write(join17(out, "blume.manifest.json"), `${JSON.stringify(project.manifest, null, 2)}
|
|
6825
7305
|
`);
|
|
6826
7306
|
await writeStagedContent(out, staged);
|
|
6827
7307
|
await pruneOrphans(srcDir, written);
|
|
@@ -6829,16 +7309,48 @@ var generateRuntime = async (project) => {
|
|
|
6829
7309
|
};
|
|
6830
7310
|
|
|
6831
7311
|
// src/core/config.ts
|
|
6832
|
-
import { existsSync as
|
|
7312
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
|
|
6833
7313
|
|
|
6834
7314
|
// src/core/bridge.ts
|
|
6835
|
-
import { existsSync as
|
|
6836
|
-
import { readFile as
|
|
6837
|
-
import { join as
|
|
7315
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
7316
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
7317
|
+
import { join as join18 } from "pathe";
|
|
7318
|
+
|
|
7319
|
+
// src/migrate/mintlify/assets.ts
|
|
7320
|
+
var assetRefs = (config) => {
|
|
7321
|
+
const refs = ["/images"];
|
|
7322
|
+
const logo = config.logo;
|
|
7323
|
+
if (typeof logo === "string") {
|
|
7324
|
+
refs.push(logo);
|
|
7325
|
+
} else if (logo) {
|
|
7326
|
+
refs.push(logo.light, logo.dark);
|
|
7327
|
+
}
|
|
7328
|
+
const favicon = config.favicon;
|
|
7329
|
+
if (typeof favicon === "string") {
|
|
7330
|
+
refs.push(favicon);
|
|
7331
|
+
} else if (favicon) {
|
|
7332
|
+
refs.push(favicon.light, favicon.dark);
|
|
7333
|
+
}
|
|
7334
|
+
refs.push(config.theme?.backgroundImage, config.theme?.backgroundImageDark);
|
|
7335
|
+
return refs;
|
|
7336
|
+
};
|
|
7337
|
+
var assetSegments = (config) => {
|
|
7338
|
+
const segments = new Set;
|
|
7339
|
+
for (const ref of assetRefs(config)) {
|
|
7340
|
+
if (typeof ref !== "string" || !ref.startsWith("/")) {
|
|
7341
|
+
continue;
|
|
7342
|
+
}
|
|
7343
|
+
const [segment] = ref.replace(/^\/+/u, "").split("/");
|
|
7344
|
+
if (segment) {
|
|
7345
|
+
segments.add(segment);
|
|
7346
|
+
}
|
|
7347
|
+
}
|
|
7348
|
+
return [...segments];
|
|
7349
|
+
};
|
|
6838
7350
|
|
|
6839
7351
|
// src/migrate/mintlify/config.ts
|
|
6840
|
-
import { readFile as
|
|
6841
|
-
import { dirname as dirname8, relative as relative7, resolve as
|
|
7352
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
7353
|
+
import { dirname as dirname8, relative as relative7, resolve as resolve5 } from "pathe";
|
|
6842
7354
|
var MINTLIFY_DEFAULT_IGNORES = [
|
|
6843
7355
|
"**/_*",
|
|
6844
7356
|
"**/.*",
|
|
@@ -6879,7 +7391,7 @@ var isInsideRoot = (root, candidate) => {
|
|
|
6879
7391
|
};
|
|
6880
7392
|
var readJsonFile = async (file) => {
|
|
6881
7393
|
try {
|
|
6882
|
-
return JSON.parse(await
|
|
7394
|
+
return JSON.parse(await readFile11(file, "utf-8"));
|
|
6883
7395
|
} catch (error) {
|
|
6884
7396
|
throw new BlumeError({
|
|
6885
7397
|
code: "BLUME_MINTLIFY_CONFIG_INVALID",
|
|
@@ -6899,7 +7411,7 @@ var resolveRefs = async (value, options) => {
|
|
|
6899
7411
|
}
|
|
6900
7412
|
const ref = asString(object.$ref);
|
|
6901
7413
|
if (ref) {
|
|
6902
|
-
const refFile =
|
|
7414
|
+
const refFile = resolve5(dirname8(options.file), ref);
|
|
6903
7415
|
if (!isInsideRoot(options.root, refFile)) {
|
|
6904
7416
|
throw new BlumeError({
|
|
6905
7417
|
code: "BLUME_MINTLIFY_REF_OUTSIDE_ROOT",
|
|
@@ -7255,13 +7767,15 @@ var mintlifySelectors = (spec) => {
|
|
|
7255
7767
|
};
|
|
7256
7768
|
var mintignorePatterns = async (root) => {
|
|
7257
7769
|
try {
|
|
7258
|
-
const raw = await
|
|
7770
|
+
const raw = await readFile11(resolve5(root, ".mintignore"), "utf-8");
|
|
7259
7771
|
return raw.split(`
|
|
7260
7772
|
`).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).filter((line) => !line.startsWith("!")).map((line) => line.endsWith("/") ? `${line}**` : line);
|
|
7261
7773
|
} catch {
|
|
7262
7774
|
return [];
|
|
7263
7775
|
}
|
|
7264
7776
|
};
|
|
7777
|
+
var REDIRECT_PARAM = /:(?<name>[A-Za-z_]\w*)(?<modifier>[*+?])?/gu;
|
|
7778
|
+
var toAstroRedirectPath = (path) => path.replaceAll(REDIRECT_PARAM, (_match, name, modifier) => modifier === "*" || modifier === "+" ? `[...${name}]` : `[${name}]`);
|
|
7265
7779
|
var mintlifyRedirects = (spec) => asArray(spec.redirects).flatMap((redirect) => {
|
|
7266
7780
|
const object = asObject(redirect);
|
|
7267
7781
|
if (!object) {
|
|
@@ -7272,8 +7786,72 @@ var mintlifyRedirects = (spec) => asArray(spec.redirects).flatMap((redirect) =>
|
|
|
7272
7786
|
if (!from || !to) {
|
|
7273
7787
|
return [];
|
|
7274
7788
|
}
|
|
7275
|
-
return [{ from, to }];
|
|
7789
|
+
return [{ from: toAstroRedirectPath(from), to: toAstroRedirectPath(to) }];
|
|
7276
7790
|
});
|
|
7791
|
+
var openapiRouteFromDirectory = (value) => {
|
|
7792
|
+
const directory = normalizeDirectory(asString(value) ?? "");
|
|
7793
|
+
return directory.length > 0 ? `/${directory}` : undefined;
|
|
7794
|
+
};
|
|
7795
|
+
var openapiSourcesFromValue = (value, context) => {
|
|
7796
|
+
if (typeof value === "string") {
|
|
7797
|
+
if (value.length === 0 || API_ENDPOINT_REF.test(value)) {
|
|
7798
|
+
return [];
|
|
7799
|
+
}
|
|
7800
|
+
return [
|
|
7801
|
+
withoutUndefined({
|
|
7802
|
+
label: context.label,
|
|
7803
|
+
route: context.route,
|
|
7804
|
+
spec: value
|
|
7805
|
+
})
|
|
7806
|
+
];
|
|
7807
|
+
}
|
|
7808
|
+
if (Array.isArray(value)) {
|
|
7809
|
+
return value.flatMap((item) => openapiSourcesFromValue(item, context));
|
|
7810
|
+
}
|
|
7811
|
+
const object = asObject(value);
|
|
7812
|
+
if (!object) {
|
|
7813
|
+
return [];
|
|
7814
|
+
}
|
|
7815
|
+
return openapiSourcesFromValue(object.source ?? object.openapi, {
|
|
7816
|
+
label: context.label,
|
|
7817
|
+
route: openapiRouteFromDirectory(object.directory) ?? context.route
|
|
7818
|
+
});
|
|
7819
|
+
};
|
|
7820
|
+
var mintlifyOpenapi = (spec) => {
|
|
7821
|
+
const drafts = [];
|
|
7822
|
+
const visit = (node) => {
|
|
7823
|
+
if (Array.isArray(node)) {
|
|
7824
|
+
for (const item of node) {
|
|
7825
|
+
visit(item);
|
|
7826
|
+
}
|
|
7827
|
+
return;
|
|
7828
|
+
}
|
|
7829
|
+
const object = asObject(node);
|
|
7830
|
+
if (!object) {
|
|
7831
|
+
return;
|
|
7832
|
+
}
|
|
7833
|
+
if (hasOwn(object, "openapi")) {
|
|
7834
|
+
drafts.push(...openapiSourcesFromValue(object.openapi, {
|
|
7835
|
+
label: labelForNavItem(object)
|
|
7836
|
+
}));
|
|
7837
|
+
}
|
|
7838
|
+
for (const children of childNavigationArrays(object)) {
|
|
7839
|
+
visit(children);
|
|
7840
|
+
}
|
|
7841
|
+
};
|
|
7842
|
+
visit(spec.navigation);
|
|
7843
|
+
drafts.push(...openapiSourcesFromValue(spec.openapi, {}));
|
|
7844
|
+
drafts.push(...openapiSourcesFromValue(asObject(spec.api)?.openapi, {}));
|
|
7845
|
+
const seen = new Set;
|
|
7846
|
+
const sources = drafts.flatMap((draft) => {
|
|
7847
|
+
if (seen.has(draft.spec)) {
|
|
7848
|
+
return [];
|
|
7849
|
+
}
|
|
7850
|
+
seen.add(draft.spec);
|
|
7851
|
+
return [draft];
|
|
7852
|
+
});
|
|
7853
|
+
return sources.length > 0 ? { enabled: true, sources } : undefined;
|
|
7854
|
+
};
|
|
7277
7855
|
var mintlifyLogo = (value) => {
|
|
7278
7856
|
if (typeof value === "string") {
|
|
7279
7857
|
return value;
|
|
@@ -7305,6 +7883,12 @@ var mintlifyFavicon = (value) => {
|
|
|
7305
7883
|
}
|
|
7306
7884
|
return withoutUndefined({ dark, light });
|
|
7307
7885
|
};
|
|
7886
|
+
var mintlifyIcons = (value) => {
|
|
7887
|
+
const library = asString(asObject(value)?.library);
|
|
7888
|
+
return {
|
|
7889
|
+
library: library === "lucide" || library === "tabler" ? library : "fontawesome"
|
|
7890
|
+
};
|
|
7891
|
+
};
|
|
7308
7892
|
var mintlifyBanner = (value) => {
|
|
7309
7893
|
const object = asObject(value);
|
|
7310
7894
|
const content = object ? asString(object.content) : undefined;
|
|
@@ -7395,6 +7979,27 @@ var mintlifyMarkdown = (value, styling) => {
|
|
|
7395
7979
|
schema: object?.schema === false ? false : undefined
|
|
7396
7980
|
});
|
|
7397
7981
|
};
|
|
7982
|
+
var FAMILY_TO_SLUG = Object.fromEntries(Object.entries(GOOGLE_FONTS).map(([slug, def]) => [
|
|
7983
|
+
def.family.toLowerCase(),
|
|
7984
|
+
slug
|
|
7985
|
+
]));
|
|
7986
|
+
var fontSlugForFamily = (value) => {
|
|
7987
|
+
const family = asString(value);
|
|
7988
|
+
return family ? FAMILY_TO_SLUG[family.toLowerCase()] : undefined;
|
|
7989
|
+
};
|
|
7990
|
+
var mintlifyFonts = (value) => {
|
|
7991
|
+
const object = asObject(value);
|
|
7992
|
+
if (!object) {
|
|
7993
|
+
return;
|
|
7994
|
+
}
|
|
7995
|
+
const heading = asObject(object.heading);
|
|
7996
|
+
const body = asObject(object.body);
|
|
7997
|
+
const fonts = withoutUndefined({
|
|
7998
|
+
body: fontSlugForFamily(body?.family) ?? fontSlugForFamily(object.family),
|
|
7999
|
+
display: fontSlugForFamily(heading?.family) ?? fontSlugForFamily(object.family)
|
|
8000
|
+
});
|
|
8001
|
+
return Object.keys(fonts).length > 0 ? fonts : undefined;
|
|
8002
|
+
};
|
|
7398
8003
|
var mintlifySeo = (value) => {
|
|
7399
8004
|
const object = asObject(value);
|
|
7400
8005
|
const metatags = asObject(object?.metatags);
|
|
@@ -7406,8 +8011,8 @@ var mintlifySeo = (value) => {
|
|
|
7406
8011
|
};
|
|
7407
8012
|
};
|
|
7408
8013
|
var loadMintlifyConfig = async (root, file) => {
|
|
7409
|
-
const projectRoot =
|
|
7410
|
-
const configFile =
|
|
8014
|
+
const projectRoot = resolve5(root);
|
|
8015
|
+
const configFile = resolve5(file);
|
|
7411
8016
|
const spec = asObject(await resolveRefs(await readJsonFile(configFile), {
|
|
7412
8017
|
file: configFile,
|
|
7413
8018
|
root: projectRoot,
|
|
@@ -7440,6 +8045,7 @@ var loadMintlifyConfig = async (root, file) => {
|
|
|
7440
8045
|
},
|
|
7441
8046
|
description: asString(spec.description),
|
|
7442
8047
|
favicon: mintlifyFavicon(spec.favicon),
|
|
8048
|
+
icons: mintlifyIcons(spec.icons),
|
|
7443
8049
|
logo: mintlifyLogo(spec.logo),
|
|
7444
8050
|
markdown: mintlifyMarkdown(spec.markdown, styling),
|
|
7445
8051
|
navigation: {
|
|
@@ -7449,6 +8055,7 @@ var loadMintlifyConfig = async (root, file) => {
|
|
|
7449
8055
|
sidebarVariants: await mintlifySidebarVariants(spec),
|
|
7450
8056
|
tabs: mintlifyTabs(spec)
|
|
7451
8057
|
},
|
|
8058
|
+
openapi: mintlifyOpenapi(spec),
|
|
7452
8059
|
redirects: mintlifyRedirects(spec),
|
|
7453
8060
|
search: {
|
|
7454
8061
|
indexing: {
|
|
@@ -7466,6 +8073,7 @@ var loadMintlifyConfig = async (root, file) => {
|
|
|
7466
8073
|
backgroundDecoration: mintlifyBackgroundDecoration(spec.background),
|
|
7467
8074
|
backgroundImage: backgroundImage.light,
|
|
7468
8075
|
backgroundImageDark: backgroundImage.dark,
|
|
8076
|
+
fonts: mintlifyFonts(spec.fonts ?? spec.font),
|
|
7469
8077
|
mode: appearance.default === "light" || appearance.default === "dark" || appearance.default === "system" ? appearance.default : "system",
|
|
7470
8078
|
strict: appearance.strict === true
|
|
7471
8079
|
},
|
|
@@ -7503,12 +8111,12 @@ var mintlifyI18n = (spec) => {
|
|
|
7503
8111
|
// src/core/bridge.ts
|
|
7504
8112
|
var MINTLIFY_CONFIG_FILES = ["docs.json", "mint.json"];
|
|
7505
8113
|
var detectMintlifyBridge = async (root) => {
|
|
7506
|
-
const configFile = MINTLIFY_CONFIG_FILES.map((name) =>
|
|
8114
|
+
const configFile = MINTLIFY_CONFIG_FILES.map((name) => join18(root, name)).find((candidate) => existsSync10(candidate));
|
|
7507
8115
|
if (!configFile) {
|
|
7508
8116
|
return null;
|
|
7509
8117
|
}
|
|
7510
8118
|
const config = await loadMintlifyConfig(root, configFile);
|
|
7511
|
-
const spec = JSON.parse(await
|
|
8119
|
+
const spec = JSON.parse(await readFile12(configFile, "utf-8"));
|
|
7512
8120
|
const i18n = mintlifyI18n(spec);
|
|
7513
8121
|
if (i18n) {
|
|
7514
8122
|
config.i18n = i18n;
|
|
@@ -7519,11 +8127,13 @@ var detectMintlifyBridge = async (root) => {
|
|
|
7519
8127
|
const variables = config.variables ?? {};
|
|
7520
8128
|
const root_ = config.content?.root ?? ".";
|
|
7521
8129
|
const exclude = config.content?.exclude ?? [];
|
|
8130
|
+
const assets = assetSegments(config).filter((segment) => segment !== "public" && existsSync10(join18(root, segment)));
|
|
7522
8131
|
return {
|
|
7523
8132
|
configFile,
|
|
7524
8133
|
raw: {
|
|
7525
8134
|
...config,
|
|
7526
8135
|
content: {
|
|
8136
|
+
assets,
|
|
7527
8137
|
exclude,
|
|
7528
8138
|
root: root_,
|
|
7529
8139
|
sources: [
|
|
@@ -7591,42 +8201,6 @@ var createModuleLoader = () => {
|
|
|
7591
8201
|
};
|
|
7592
8202
|
};
|
|
7593
8203
|
|
|
7594
|
-
// src/core/project.ts
|
|
7595
|
-
import { existsSync as existsSync8 } from "node:fs";
|
|
7596
|
-
import { isAbsolute as isAbsolute4, join as join13, resolve as resolve5 } from "pathe";
|
|
7597
|
-
var CONFIG_FILENAMES = [
|
|
7598
|
-
"blume.config.ts",
|
|
7599
|
-
"blume.config.mjs",
|
|
7600
|
-
"blume.config.js"
|
|
7601
|
-
];
|
|
7602
|
-
var THEME_FILENAMES = ["theme.css"];
|
|
7603
|
-
var COMPONENTS_FILENAMES = ["components.tsx", "components.ts"];
|
|
7604
|
-
var firstExisting = (root, names) => {
|
|
7605
|
-
for (const name of names) {
|
|
7606
|
-
const candidate = join13(root, name);
|
|
7607
|
-
if (existsSync8(candidate)) {
|
|
7608
|
-
return candidate;
|
|
7609
|
-
}
|
|
7610
|
-
}
|
|
7611
|
-
return null;
|
|
7612
|
-
};
|
|
7613
|
-
var findConfigFile = (root) => firstExisting(root, CONFIG_FILENAMES);
|
|
7614
|
-
var resolveProjectContext = (root, config) => {
|
|
7615
|
-
const absoluteRoot = resolve5(root);
|
|
7616
|
-
const contentRoot = isAbsolute4(config.content.root) ? config.content.root : join13(absoluteRoot, config.content.root);
|
|
7617
|
-
const pagesPath = join13(absoluteRoot, config.content.pages);
|
|
7618
|
-
const pagesRoot = existsSync8(pagesPath) ? pagesPath : null;
|
|
7619
|
-
return {
|
|
7620
|
-
componentsFile: firstExisting(absoluteRoot, COMPONENTS_FILENAMES),
|
|
7621
|
-
configFile: findConfigFile(absoluteRoot),
|
|
7622
|
-
contentRoot,
|
|
7623
|
-
outDir: join13(absoluteRoot, ".blume"),
|
|
7624
|
-
pagesRoot,
|
|
7625
|
-
root: absoluteRoot,
|
|
7626
|
-
themeFile: firstExisting(absoluteRoot, THEME_FILENAMES)
|
|
7627
|
-
};
|
|
7628
|
-
};
|
|
7629
|
-
|
|
7630
8204
|
// src/core/schema.ts
|
|
7631
8205
|
import { z as z2 } from "zod";
|
|
7632
8206
|
var iconName = z2.string().min(1);
|
|
@@ -7656,7 +8230,17 @@ var changelogMetaSchema = z2.object({
|
|
|
7656
8230
|
date: dateSchema.optional(),
|
|
7657
8231
|
version: z2.string().optional()
|
|
7658
8232
|
}).strict();
|
|
8233
|
+
var authorSchema = z2.union([
|
|
8234
|
+
z2.string(),
|
|
8235
|
+
z2.object({
|
|
8236
|
+
avatar: z2.string().optional(),
|
|
8237
|
+
image: z2.string().optional(),
|
|
8238
|
+
name: z2.string(),
|
|
8239
|
+
url: z2.string().optional()
|
|
8240
|
+
}).passthrough()
|
|
8241
|
+
]);
|
|
7659
8242
|
var pageMetaBaseSchema = z2.object({
|
|
8243
|
+
authors: z2.union([authorSchema, z2.array(authorSchema)]).optional(),
|
|
7660
8244
|
changelog: changelogMetaSchema.optional(),
|
|
7661
8245
|
date: dateSchema.optional(),
|
|
7662
8246
|
deprecated: z2.boolean().default(false),
|
|
@@ -7810,6 +8394,7 @@ var contentSourceSchema = z2.discriminatedUnion("type", [
|
|
|
7810
8394
|
customSourceSchema
|
|
7811
8395
|
]);
|
|
7812
8396
|
var contentConfigSchema = z2.object({
|
|
8397
|
+
assets: z2.array(z2.string()).default([]),
|
|
7813
8398
|
defaultType: z2.string().default("doc"),
|
|
7814
8399
|
exclude: z2.array(z2.string()).default(["**/_*", "**/.*"]),
|
|
7815
8400
|
include: z2.array(z2.string()).default(["**/*.{md,mdx}"]),
|
|
@@ -8096,7 +8681,10 @@ var openapiSourceSchema = z2.object({
|
|
|
8096
8681
|
spec: z2.string()
|
|
8097
8682
|
}).strict();
|
|
8098
8683
|
var openapiConfigSchema = z2.object({
|
|
8684
|
+
codeSamples: z2.array(z2.string()).default(["curl", "js", "python"]),
|
|
8099
8685
|
enabled: z2.boolean().default(false),
|
|
8686
|
+
expandSchemas: z2.boolean().default(false),
|
|
8687
|
+
renderer: z2.enum(["blume", "scalar"]).default("blume"),
|
|
8100
8688
|
route: z2.string().default("/reference"),
|
|
8101
8689
|
sources: z2.array(openapiSourceSchema).default([]),
|
|
8102
8690
|
spec: z2.string().optional(),
|
|
@@ -8125,6 +8713,9 @@ var tocConfigSchema = z2.union([
|
|
|
8125
8713
|
minLevel: value.minHeadingLevel ?? 2
|
|
8126
8714
|
};
|
|
8127
8715
|
});
|
|
8716
|
+
var iconsConfigSchema = z2.object({
|
|
8717
|
+
library: z2.enum(["lucide", "fontawesome", "tabler"]).default("lucide")
|
|
8718
|
+
}).strict();
|
|
8128
8719
|
var blumeConfigSchema = z2.object({
|
|
8129
8720
|
ai: aiConfigSchema.default({}),
|
|
8130
8721
|
analytics: analyticsConfigSchema.optional(),
|
|
@@ -8139,6 +8730,7 @@ var blumeConfigSchema = z2.object({
|
|
|
8139
8730
|
feedback: z2.boolean().default(true),
|
|
8140
8731
|
github: githubConfigSchema.optional(),
|
|
8141
8732
|
i18n: i18nConfigSchema.optional(),
|
|
8733
|
+
icons: iconsConfigSchema.default({}),
|
|
8142
8734
|
lastModified: lastModifiedConfigSchema.default(false),
|
|
8143
8735
|
logo: logoConfigSchema.optional(),
|
|
8144
8736
|
markdown: markdownConfigSchema.default({}),
|
|
@@ -8180,7 +8772,7 @@ var loadConfig = async (root, options = {}) => {
|
|
|
8180
8772
|
const sourceFile = bridge?.configFile ?? configFile;
|
|
8181
8773
|
const parsed = blumeConfigSchema.safeParse(raw ?? {});
|
|
8182
8774
|
if (!parsed.success) {
|
|
8183
|
-
const source = sourceFile &&
|
|
8775
|
+
const source = sourceFile && existsSync11(sourceFile) ? readFileSync7(sourceFile, "utf-8") : undefined;
|
|
8184
8776
|
const diagnostics = diagnosticsFromZod(parsed.error, {
|
|
8185
8777
|
code: "BLUME_CONFIG_INVALID",
|
|
8186
8778
|
file: sourceFile ?? undefined,
|
|
@@ -8657,7 +9249,7 @@ var discoverFolderMeta = async (contentRoot) => {
|
|
|
8657
9249
|
};
|
|
8658
9250
|
|
|
8659
9251
|
// src/core/sources/normalize.ts
|
|
8660
|
-
import { existsSync as
|
|
9252
|
+
import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
|
|
8661
9253
|
import GithubSlugger from "github-slugger";
|
|
8662
9254
|
import { extname as extname4 } from "pathe";
|
|
8663
9255
|
var NUMERIC_PREFIX2 = /^\d+[-_.]/u;
|
|
@@ -8665,7 +9257,7 @@ var GROUP_FOLDER2 = /^\((?<label>.+)\)$/u;
|
|
|
8665
9257
|
var WORD_SPLIT2 = /[-_]/u;
|
|
8666
9258
|
var stripNumericPrefix = (segment) => segment.replace(NUMERIC_PREFIX2, "");
|
|
8667
9259
|
var groupLabel = (segment) => segment.match(GROUP_FOLDER2)?.groups?.label ?? null;
|
|
8668
|
-
var
|
|
9260
|
+
var slugify3 = (text) => text.toLowerCase().trim().replaceAll(/[^\w\s-]/gu, "").replaceAll(/[\s_]+/gu, "-").replaceAll(/-+/gu, "-").replaceAll(/^-|-$/gu, "");
|
|
8669
9261
|
var titleCase = (value) => value.split(WORD_SPLIT2).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
8670
9262
|
var mapRoute = (relativePath) => {
|
|
8671
9263
|
const withoutExt = relativePath.slice(0, relativePath.length - extname4(relativePath).length);
|
|
@@ -8731,8 +9323,9 @@ var extractLinks = (body) => {
|
|
|
8731
9323
|
if (target === undefined || match.index === undefined) {
|
|
8732
9324
|
continue;
|
|
8733
9325
|
}
|
|
9326
|
+
const targetOffset = match.index + match[0].indexOf("](") + "](".length;
|
|
8734
9327
|
links.push({
|
|
8735
|
-
column:
|
|
9328
|
+
column: targetOffset + 1,
|
|
8736
9329
|
line: lineNumber,
|
|
8737
9330
|
target
|
|
8738
9331
|
});
|
|
@@ -8782,7 +9375,7 @@ var normalizeEntry2 = (entry, ctx) => {
|
|
|
8782
9375
|
const ext = format === "mdx" ? ".mdx" : ".md";
|
|
8783
9376
|
const result = pageMetaSchema.safeParse(entry.data);
|
|
8784
9377
|
if (!result.success) {
|
|
8785
|
-
const source = entry.raw ?? (entry.sourcePath &&
|
|
9378
|
+
const source = entry.raw ?? (entry.sourcePath && existsSync12(entry.sourcePath) ? readFileSync8(entry.sourcePath, "utf-8") : undefined);
|
|
8786
9379
|
return {
|
|
8787
9380
|
diagnostics: diagnosticsFromZod(result.error, {
|
|
8788
9381
|
code: "BLUME_FRONTMATTER_INVALID",
|
|
@@ -8831,15 +9424,15 @@ var normalizeEntry2 = (entry, ctx) => {
|
|
|
8831
9424
|
};
|
|
8832
9425
|
|
|
8833
9426
|
// src/core/sources/resolve.ts
|
|
8834
|
-
import { join as
|
|
9427
|
+
import { join as join26 } from "pathe";
|
|
8835
9428
|
|
|
8836
9429
|
// src/core/sources/filesystem.ts
|
|
8837
|
-
import { existsSync as
|
|
8838
|
-
import { readFile as
|
|
8839
|
-
import { extname as extname5, isAbsolute as
|
|
9430
|
+
import { existsSync as existsSync13, watch as fsWatch } from "node:fs";
|
|
9431
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
9432
|
+
import { extname as extname5, isAbsolute as isAbsolute6, join as join19, relative as relative10, resolve as resolve6 } from "pathe";
|
|
8840
9433
|
import { glob as glob6 } from "tinyglobby";
|
|
8841
9434
|
var filesystemSource = (options) => {
|
|
8842
|
-
const contentRoot =
|
|
9435
|
+
const contentRoot = isAbsolute6(options.root) ? options.root : join19(resolve6(options.projectRoot), options.root);
|
|
8843
9436
|
const load2 = async () => {
|
|
8844
9437
|
const files = await glob6(options.include, {
|
|
8845
9438
|
absolute: true,
|
|
@@ -8849,123 +9442,47 @@ var filesystemSource = (options) => {
|
|
|
8849
9442
|
});
|
|
8850
9443
|
files.sort();
|
|
8851
9444
|
const entries = await Promise.all(files.map(async (file) => {
|
|
8852
|
-
const source = await
|
|
9445
|
+
const source = await readFile13(file, "utf-8");
|
|
8853
9446
|
const ext = extname5(file).toLowerCase();
|
|
8854
9447
|
const format = ext === ".mdx" ? "mdx" : "md";
|
|
8855
|
-
const parsed = frontmatter_default(source);
|
|
8856
|
-
return {
|
|
8857
|
-
body: { format, text: parsed.content },
|
|
8858
|
-
data: parsed.data,
|
|
8859
|
-
ref: relative10(contentRoot, file),
|
|
8860
|
-
sourcePath: file
|
|
8861
|
-
};
|
|
8862
|
-
}));
|
|
8863
|
-
return { diagnostics: [], entries };
|
|
8864
|
-
};
|
|
8865
|
-
const validate = () => {
|
|
8866
|
-
if (!existsSync11(contentRoot)) {
|
|
8867
|
-
throw new BlumeError({
|
|
8868
|
-
code: options.missingCode ?? "BLUME_CONTENT_ROOT_MISSING",
|
|
8869
|
-
file: contentRoot,
|
|
8870
|
-
message: `Content root not found: ${options.root}`,
|
|
8871
|
-
severity: "error",
|
|
8872
|
-
suggestion: `Create a "${options.root}" folder with at least one .md or .mdx file, or set content.root in blume.config.ts.`
|
|
8873
|
-
});
|
|
8874
|
-
}
|
|
8875
|
-
};
|
|
8876
|
-
const watch = (onChange) => {
|
|
8877
|
-
if (!existsSync11(contentRoot)) {
|
|
8878
|
-
return () => {};
|
|
8879
|
-
}
|
|
8880
|
-
const watcher = fsWatch(contentRoot, { recursive: true }, onChange);
|
|
8881
|
-
return () => watcher.close();
|
|
8882
|
-
};
|
|
8883
|
-
return {
|
|
8884
|
-
contentRoot,
|
|
8885
|
-
load: load2,
|
|
8886
|
-
name: options.name,
|
|
8887
|
-
prefix: options.prefix,
|
|
8888
|
-
read: (ref) => readFile10(join14(contentRoot, ref), "utf-8"),
|
|
8889
|
-
staged: false,
|
|
8890
|
-
validate,
|
|
8891
|
-
watch
|
|
8892
|
-
};
|
|
8893
|
-
};
|
|
8894
|
-
|
|
8895
|
-
// src/core/sources/cache.ts
|
|
8896
|
-
import { mkdir as mkdir3, readFile as readFile11, writeFile as writeFile3 } from "node:fs/promises";
|
|
8897
|
-
import { join as join15 } from "pathe";
|
|
8898
|
-
var hashText = (text) => {
|
|
8899
|
-
let hash = 5381;
|
|
8900
|
-
for (let i = 0;i < text.length; i += 1) {
|
|
8901
|
-
hash = (hash * 33 + (text.codePointAt(i) ?? 0)) % 2147483647;
|
|
8902
|
-
}
|
|
8903
|
-
return hash.toString(36);
|
|
8904
|
-
};
|
|
8905
|
-
var entriesDigest = (entries) => hashText(entries.map((entry) => `${entry.ref}:${entry.hash ?? hashText(entry.body.text)}`).join("|"));
|
|
8906
|
-
var pollingWatch = (load2, intervalSeconds) => (onChange) => {
|
|
8907
|
-
let last = "";
|
|
8908
|
-
const tick = async () => {
|
|
8909
|
-
try {
|
|
8910
|
-
const { entries } = await load2();
|
|
8911
|
-
const next = entriesDigest(entries);
|
|
8912
|
-
if (last && next !== last) {
|
|
8913
|
-
onChange();
|
|
8914
|
-
}
|
|
8915
|
-
last = next;
|
|
8916
|
-
} catch {}
|
|
8917
|
-
};
|
|
8918
|
-
const timer = setInterval(() => {
|
|
8919
|
-
tick();
|
|
8920
|
-
}, intervalSeconds * 1000);
|
|
8921
|
-
return () => clearInterval(timer);
|
|
8922
|
-
};
|
|
8923
|
-
var snapshotCache = (cacheDir) => {
|
|
8924
|
-
const file = join15(cacheDir, "entries.json");
|
|
8925
|
-
return {
|
|
8926
|
-
read: async () => {
|
|
8927
|
-
try {
|
|
8928
|
-
return JSON.parse(await readFile11(file, "utf-8"));
|
|
8929
|
-
} catch {
|
|
8930
|
-
return [];
|
|
8931
|
-
}
|
|
8932
|
-
},
|
|
8933
|
-
write: async (entries) => {
|
|
8934
|
-
try {
|
|
8935
|
-
await mkdir3(cacheDir, { recursive: true });
|
|
8936
|
-
await writeFile3(file, `${JSON.stringify(entries)}
|
|
8937
|
-
`, "utf-8");
|
|
8938
|
-
} catch {}
|
|
8939
|
-
}
|
|
9448
|
+
const parsed = frontmatter_default(source);
|
|
9449
|
+
return {
|
|
9450
|
+
body: { format, text: parsed.content },
|
|
9451
|
+
data: parsed.data,
|
|
9452
|
+
ref: relative10(contentRoot, file),
|
|
9453
|
+
sourcePath: file
|
|
9454
|
+
};
|
|
9455
|
+
}));
|
|
9456
|
+
return { diagnostics: [], entries };
|
|
8940
9457
|
};
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
9458
|
+
const validate = () => {
|
|
9459
|
+
if (!existsSync13(contentRoot)) {
|
|
9460
|
+
throw new BlumeError({
|
|
9461
|
+
code: options.missingCode ?? "BLUME_CONTENT_ROOT_MISSING",
|
|
9462
|
+
file: contentRoot,
|
|
9463
|
+
message: `Content root not found: ${options.root}`,
|
|
9464
|
+
severity: "error",
|
|
9465
|
+
suggestion: `Create a "${options.root}" folder with at least one .md or .mdx file, or set content.root in blume.config.ts.`
|
|
9466
|
+
});
|
|
8947
9467
|
}
|
|
8948
|
-
}
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
return { diagnostics: [], entries };
|
|
8953
|
-
} catch (error) {
|
|
8954
|
-
const fallback = await cache.read();
|
|
8955
|
-
if (fallback.length > 0) {
|
|
8956
|
-
const diagnostic = {
|
|
8957
|
-
code: "BLUME_SOURCE_OFFLINE",
|
|
8958
|
-
message: `Source "${name}" could not be fetched (${error.message}); served ${fallback.length} cached entries.`,
|
|
8959
|
-
severity: "warning"
|
|
8960
|
-
};
|
|
8961
|
-
return { diagnostics: [diagnostic], entries: fallback };
|
|
9468
|
+
};
|
|
9469
|
+
const watch = (onChange) => {
|
|
9470
|
+
if (!existsSync13(contentRoot)) {
|
|
9471
|
+
return () => {};
|
|
8962
9472
|
}
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
|
|
9473
|
+
const watcher = fsWatch(contentRoot, { recursive: true }, onChange);
|
|
9474
|
+
return () => watcher.close();
|
|
9475
|
+
};
|
|
9476
|
+
return {
|
|
9477
|
+
contentRoot,
|
|
9478
|
+
load: load2,
|
|
9479
|
+
name: options.name,
|
|
9480
|
+
prefix: options.prefix,
|
|
9481
|
+
read: (ref) => readFile13(join19(contentRoot, ref), "utf-8"),
|
|
9482
|
+
staged: false,
|
|
9483
|
+
validate,
|
|
9484
|
+
watch
|
|
9485
|
+
};
|
|
8969
9486
|
};
|
|
8970
9487
|
|
|
8971
9488
|
// src/core/sources/github-releases.ts
|
|
@@ -8973,9 +9490,9 @@ var DEFAULT_BASE_URL = "https://api.github.com";
|
|
|
8973
9490
|
var DEFAULT_LIMIT = 100;
|
|
8974
9491
|
var PER_PAGE = 100;
|
|
8975
9492
|
var LEADING_V = /^v/iu;
|
|
8976
|
-
var
|
|
9493
|
+
var NON_SLUG3 = /[^a-z0-9]+/gu;
|
|
8977
9494
|
var EDGE_DASHES = /^-+|-+$/gu;
|
|
8978
|
-
var slugifyTag = (tag) => tag.toLowerCase().replaceAll(
|
|
9495
|
+
var slugifyTag = (tag) => tag.toLowerCase().replaceAll(NON_SLUG3, "-").replaceAll(EDGE_DASHES, "");
|
|
8979
9496
|
var githubHeaders = () => {
|
|
8980
9497
|
const headers = new Headers({ Accept: "application/vnd.github+json" });
|
|
8981
9498
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -9135,11 +9652,12 @@ var enumerateGithub = async (github, include, doFetch) => {
|
|
|
9135
9652
|
}
|
|
9136
9653
|
const body = await res.json();
|
|
9137
9654
|
const prefix = base ? `${base}/` : "";
|
|
9138
|
-
|
|
9655
|
+
const refs = (body.tree ?? []).filter((node) => node.type === "blob" && node.path.startsWith(prefix)).map((node) => node.path.slice(prefix.length)).filter((rel) => matchesInclude(rel, include)).map((rel) => ({
|
|
9139
9656
|
editUrl: `https://github.com/${owner}/${repo}/edit/${ref}/${prefix}${rel}`,
|
|
9140
9657
|
fetchUrl: `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${prefix}${rel}`,
|
|
9141
9658
|
ref: rel
|
|
9142
9659
|
}));
|
|
9660
|
+
return { refs, truncated: body.truncated === true };
|
|
9143
9661
|
};
|
|
9144
9662
|
var mdxRemoteSource = (options, ctx) => {
|
|
9145
9663
|
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
@@ -9151,11 +9669,12 @@ var mdxRemoteSource = (options, ctx) => {
|
|
|
9151
9669
|
}
|
|
9152
9670
|
if (options.files && options.url) {
|
|
9153
9671
|
const base = options.url.replace(/\/$/u, "");
|
|
9154
|
-
|
|
9672
|
+
const refs = options.files.filter((ref) => matchesInclude(ref, options.include)).map((ref) => ({
|
|
9155
9673
|
editUrl: `${base}/${ref}`,
|
|
9156
9674
|
fetchUrl: `${base}/${ref}`,
|
|
9157
9675
|
ref
|
|
9158
9676
|
}));
|
|
9677
|
+
return { refs, truncated: false };
|
|
9159
9678
|
}
|
|
9160
9679
|
throw new BlumeError({
|
|
9161
9680
|
code: "BLUME_SOURCE_MISCONFIGURED",
|
|
@@ -9181,12 +9700,40 @@ var mdxRemoteSource = (options, ctx) => {
|
|
|
9181
9700
|
};
|
|
9182
9701
|
};
|
|
9183
9702
|
const load2 = async () => {
|
|
9703
|
+
const skipped = [];
|
|
9184
9704
|
const result = await loadWithCache(options.name, cache, async () => {
|
|
9185
|
-
const refs = await enumerate();
|
|
9186
|
-
|
|
9705
|
+
const { refs, truncated } = await enumerate();
|
|
9706
|
+
if (truncated) {
|
|
9707
|
+
skipped.push({
|
|
9708
|
+
code: "BLUME_SOURCE_TRUNCATED",
|
|
9709
|
+
message: `Source "${options.name}" hit GitHub's tree listing limit; some files were not enumerated. Narrow the source path or split the repo.`,
|
|
9710
|
+
severity: "warning"
|
|
9711
|
+
});
|
|
9712
|
+
}
|
|
9713
|
+
const settled = await Promise.all(refs.map(async (ref) => {
|
|
9714
|
+
try {
|
|
9715
|
+
return await fetchEntry(ref);
|
|
9716
|
+
} catch (error) {
|
|
9717
|
+
skipped.push({
|
|
9718
|
+
code: "BLUME_SOURCE_FETCH_FAILED",
|
|
9719
|
+
message: `Source "${options.name}" skipped "${ref.ref}" (${error.message}); the rest were imported.`,
|
|
9720
|
+
severity: "warning"
|
|
9721
|
+
});
|
|
9722
|
+
return null;
|
|
9723
|
+
}
|
|
9724
|
+
}));
|
|
9725
|
+
const entries = settled.filter((entry) => entry !== null);
|
|
9726
|
+
if (refs.length > 0 && entries.length === 0) {
|
|
9727
|
+
skipped.length = 0;
|
|
9728
|
+
throw new Error(`all ${refs.length} remote file(s) failed to fetch`);
|
|
9729
|
+
}
|
|
9730
|
+
return entries;
|
|
9187
9731
|
}, ctx.refresh ?? true);
|
|
9188
9732
|
snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
|
|
9189
|
-
return
|
|
9733
|
+
return {
|
|
9734
|
+
...result,
|
|
9735
|
+
diagnostics: [...result.diagnostics, ...skipped]
|
|
9736
|
+
};
|
|
9190
9737
|
};
|
|
9191
9738
|
const read = async (ref) => {
|
|
9192
9739
|
const cached3 = snapshot.get(ref);
|
|
@@ -9208,21 +9755,25 @@ var mdxRemoteSource = (options, ctx) => {
|
|
|
9208
9755
|
};
|
|
9209
9756
|
|
|
9210
9757
|
// src/core/sources/mintlify.ts
|
|
9211
|
-
import { existsSync as
|
|
9212
|
-
import { readFile as
|
|
9213
|
-
import { isAbsolute as
|
|
9758
|
+
import { existsSync as existsSync15, watch as fsWatch2 } from "node:fs";
|
|
9759
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
9760
|
+
import { isAbsolute as isAbsolute8, join as join22, relative as relative14, resolve as resolve8 } from "pathe";
|
|
9214
9761
|
import { glob as glob7 } from "tinyglobby";
|
|
9215
9762
|
|
|
9216
9763
|
// src/migrate/shared.ts
|
|
9217
|
-
import { existsSync as
|
|
9218
|
-
import { readFile as
|
|
9219
|
-
import { join as
|
|
9764
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
9765
|
+
import { readFile as readFile14, writeFile as writeFile5 } from "node:fs/promises";
|
|
9766
|
+
import { isAbsolute as isAbsolute7, join as join20, relative as relative11 } from "pathe";
|
|
9767
|
+
var isInsideRoot2 = (root, candidate) => {
|
|
9768
|
+
const rel = relative11(root, candidate);
|
|
9769
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute7(rel);
|
|
9770
|
+
};
|
|
9220
9771
|
var writeBlumeConfig = async (root, config) => {
|
|
9221
9772
|
const body = `import { defineConfig } from "blume";
|
|
9222
9773
|
|
|
9223
9774
|
export default defineConfig(${JSON.stringify(config, null, 2)});
|
|
9224
9775
|
`;
|
|
9225
|
-
await
|
|
9776
|
+
await writeFile5(join20(root, "blume.config.ts"), body, "utf-8");
|
|
9226
9777
|
};
|
|
9227
9778
|
var BLUME_SCRIPTS = {
|
|
9228
9779
|
build: "blume build",
|
|
@@ -9230,13 +9781,13 @@ var BLUME_SCRIPTS = {
|
|
|
9230
9781
|
start: "blume preview"
|
|
9231
9782
|
};
|
|
9232
9783
|
var rewriteFrameworkScripts = async (root, cli, remove) => {
|
|
9233
|
-
const pkgPath =
|
|
9234
|
-
if (!
|
|
9784
|
+
const pkgPath = join20(root, "package.json");
|
|
9785
|
+
if (!existsSync14(pkgPath)) {
|
|
9235
9786
|
return false;
|
|
9236
9787
|
}
|
|
9237
9788
|
let pkg;
|
|
9238
9789
|
try {
|
|
9239
|
-
pkg = JSON.parse(await
|
|
9790
|
+
pkg = JSON.parse(await readFile14(pkgPath, "utf-8"));
|
|
9240
9791
|
} catch {
|
|
9241
9792
|
return false;
|
|
9242
9793
|
}
|
|
@@ -9259,30 +9810,12 @@ var rewriteFrameworkScripts = async (root, cli, remove) => {
|
|
|
9259
9810
|
}
|
|
9260
9811
|
if (changed) {
|
|
9261
9812
|
pkg.scripts = next;
|
|
9262
|
-
await
|
|
9813
|
+
await writeFile5(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
9263
9814
|
`, "utf-8");
|
|
9264
9815
|
}
|
|
9265
9816
|
return changed;
|
|
9266
9817
|
};
|
|
9267
|
-
var
|
|
9268
|
-
var ensureGitignore = async (root, entries) => {
|
|
9269
|
-
const path = join16(root, ".gitignore");
|
|
9270
|
-
const existing = existsSync12(path) ? await readFile12(path, "utf-8") : "";
|
|
9271
|
-
const present = new Set(existing.split(`
|
|
9272
|
-
`).map(gitignoreKey).filter(Boolean));
|
|
9273
|
-
const added = entries.filter((entry) => !present.has(gitignoreKey(entry)));
|
|
9274
|
-
if (added.length === 0) {
|
|
9275
|
-
return [];
|
|
9276
|
-
}
|
|
9277
|
-
const gap = existing.length > 0 && !existing.endsWith(`
|
|
9278
|
-
`) ? `
|
|
9279
|
-
` : "";
|
|
9280
|
-
await writeFile4(path, `${existing}${gap}${added.join(`
|
|
9281
|
-
`)}
|
|
9282
|
-
`, "utf-8");
|
|
9283
|
-
return added;
|
|
9284
|
-
};
|
|
9285
|
-
var leftoverFiles = (root, candidates) => candidates.filter((candidate) => existsSync12(join16(root, candidate)));
|
|
9818
|
+
var leftoverFiles = (root, candidates) => candidates.filter((candidate) => existsSync14(join20(root, candidate)));
|
|
9286
9819
|
var attribute = (attrs, name) => {
|
|
9287
9820
|
const match = attrs.match(new RegExp(`\\b${name}=(?:"(?<dq>[^"]*)"|'(?<sq>[^']*)')`, "u"));
|
|
9288
9821
|
return match?.groups?.dq ?? match?.groups?.sq;
|
|
@@ -9679,7 +10212,7 @@ var isLiteralObject = (value) => typeof value === "object" && value !== null &&
|
|
|
9679
10212
|
var asLiteralArray = (value) => Array.isArray(value) ? value : undefined;
|
|
9680
10213
|
|
|
9681
10214
|
// src/migrate/mintlify/content.ts
|
|
9682
|
-
import { dirname as dirname10, join as
|
|
10215
|
+
import { dirname as dirname10, join as join21, relative as relative12 } from "pathe";
|
|
9683
10216
|
var CALLOUT_DIRECTIVES = {
|
|
9684
10217
|
Check: "success",
|
|
9685
10218
|
Danger: "danger",
|
|
@@ -9715,9 +10248,9 @@ var rewriteSnippetImports = (source, options) => {
|
|
|
9715
10248
|
if (/\.mdx?$/u.test(importSource)) {
|
|
9716
10249
|
return "";
|
|
9717
10250
|
}
|
|
9718
|
-
const target =
|
|
10251
|
+
const target = join21(options.root, importSource.replace(/^\/+/u, ""));
|
|
9719
10252
|
components.push(importSource.replace(/^\/+/u, ""));
|
|
9720
|
-
let rel =
|
|
10253
|
+
let rel = relative12(dirname10(options.filePath), target);
|
|
9721
10254
|
if (!rel.startsWith(".")) {
|
|
9722
10255
|
rel = `./${rel}`;
|
|
9723
10256
|
}
|
|
@@ -9725,7 +10258,7 @@ var rewriteSnippetImports = (source, options) => {
|
|
|
9725
10258
|
});
|
|
9726
10259
|
return { components, source: next };
|
|
9727
10260
|
};
|
|
9728
|
-
var UNSUPPORTED_COMPONENTS = ["
|
|
10261
|
+
var UNSUPPORTED_COMPONENTS = ["Update"];
|
|
9729
10262
|
var unsupportedMintlifyComponents = (source) => UNSUPPORTED_COMPONENTS.filter((name) => new RegExp(`<${name}\\b`, "u").test(source));
|
|
9730
10263
|
|
|
9731
10264
|
// src/migrate/mintlify/frontmatter.ts
|
|
@@ -9907,7 +10440,7 @@ var rewriteMintlifySvgIconProps = (source) => {
|
|
|
9907
10440
|
|
|
9908
10441
|
// src/migrate/mintlify/snippets.ts
|
|
9909
10442
|
import { readFile as readFileFromDisk } from "node:fs/promises";
|
|
9910
|
-
import { dirname as dirname11, relative as
|
|
10443
|
+
import { dirname as dirname11, relative as relative13, resolve as resolve7 } from "pathe";
|
|
9911
10444
|
var MARKDOWN_SNIPPET_IMPORT = /^import\s+(?<name>[$A-Z_a-z][$\w]*)\s+from\s+["'](?<source>[^"']+\.mdx?)["'];?\s*$/gmu;
|
|
9912
10445
|
var NAMED_SNIPPET_IMPORT = /^import\s+\{(?<names>[^}]+)\}\s+from\s+["'](?<source>[^"']+\.mdx?)["'];?\s*$/gmu;
|
|
9913
10446
|
var EXPORTED_STRING_CONST = /^export\s+const\s+(?<name>[$A-Z_a-z][$\w]*)\s*=\s*(?:"(?<double>(?:\\.|[^"\\])*)"|'(?<single>(?:\\.|[^'\\])*)'|`(?<template>(?:\\.|[^`\\])*)`)\s*;?\s*$/gmu;
|
|
@@ -9916,15 +10449,15 @@ var PLACEHOLDER = /\{(?<name>[$A-Z_a-z][$\w]*)\}/gu;
|
|
|
9916
10449
|
var GLOBAL_VARIABLE = /\{\{\s*(?<name>[A-Za-z0-9-]+)\s*\}\}/gu;
|
|
9917
10450
|
var FRONTMATTER_BLOCK = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/u;
|
|
9918
10451
|
var USER_EXPORT = /^(?:export\s+)?(?:const|let|var)\s+user\s*=|^import\s+\{\s*user\s*\}/mu;
|
|
9919
|
-
var
|
|
9920
|
-
const rel =
|
|
10452
|
+
var isInsideRoot3 = (root, candidate) => {
|
|
10453
|
+
const rel = relative13(root, candidate);
|
|
9921
10454
|
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/");
|
|
9922
10455
|
};
|
|
9923
10456
|
var escapeRegExp2 = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
9924
10457
|
var snippetSelfClosingTagPattern = (name) => new RegExp(`<${escapeRegExp2(name)}(?<attrs>[^>]*?)\\s*/>`, "gu");
|
|
9925
10458
|
var snippetPairedTagPattern = (name) => new RegExp(`<${escapeRegExp2(name)}(?<attrs>[^>]*?)>[\\s\\S]*?</${escapeRegExp2(name)}>`, "gu");
|
|
9926
10459
|
var rootRelativePath = (root, file) => {
|
|
9927
|
-
const rel =
|
|
10460
|
+
const rel = relative13(root, file);
|
|
9928
10461
|
return rel ? `/${rel}` : "/";
|
|
9929
10462
|
};
|
|
9930
10463
|
var snippetCycleMessage = (root, file, trail) => {
|
|
@@ -9934,7 +10467,7 @@ var snippetCycleMessage = (root, file, trail) => {
|
|
|
9934
10467
|
};
|
|
9935
10468
|
var resolveSnippetPath = (options) => {
|
|
9936
10469
|
const target = options.source.startsWith("/") ? resolve7(options.root, options.source.slice(1)) : resolve7(dirname11(options.filePath), options.source);
|
|
9937
|
-
return
|
|
10470
|
+
return isInsideRoot3(options.root, target) ? target : null;
|
|
9938
10471
|
};
|
|
9939
10472
|
var collectImports2 = (source) => [...source.matchAll(MARKDOWN_SNIPPET_IMPORT)].flatMap((match) => {
|
|
9940
10473
|
const name = match.groups?.name;
|
|
@@ -9996,9 +10529,9 @@ var rewriteMintlifyMarkdownSnippets = async (source, options) => {
|
|
|
9996
10529
|
throw new Error(snippetCycleMessage(options.root, file, options.trail ?? []));
|
|
9997
10530
|
}
|
|
9998
10531
|
seen.add(file);
|
|
9999
|
-
const
|
|
10532
|
+
const readFile15 = options.readFile ?? readFileFromDisk;
|
|
10000
10533
|
try {
|
|
10001
|
-
const raw = await
|
|
10534
|
+
const raw = await readFile15(file);
|
|
10002
10535
|
const content = frontmatter_default(raw).content.trim();
|
|
10003
10536
|
const transformed = await rewriteMintlifyMarkdownSnippets(content, {
|
|
10004
10537
|
...options,
|
|
@@ -10039,7 +10572,7 @@ var rewriteMintlifyMarkdownSnippets = async (source, options) => {
|
|
|
10039
10572
|
return await inlineAt(0, source);
|
|
10040
10573
|
};
|
|
10041
10574
|
var rewriteMintlifySnippetVariables = async (source, options) => {
|
|
10042
|
-
const
|
|
10575
|
+
const readFile15 = options.readFile ?? readFileFromDisk;
|
|
10043
10576
|
const inlineImport = async (current, variableImport) => {
|
|
10044
10577
|
const file = resolveSnippetPath({
|
|
10045
10578
|
filePath: options.filePath,
|
|
@@ -10049,7 +10582,7 @@ var rewriteMintlifySnippetVariables = async (source, options) => {
|
|
|
10049
10582
|
if (!file) {
|
|
10050
10583
|
return current;
|
|
10051
10584
|
}
|
|
10052
|
-
const exports = collectStringExports(frontmatter_default(await
|
|
10585
|
+
const exports = collectStringExports(frontmatter_default(await readFile15(file)).content);
|
|
10053
10586
|
let next = current;
|
|
10054
10587
|
for (const name of variableImport.names) {
|
|
10055
10588
|
const value = exports.get(name.imported);
|
|
@@ -10127,7 +10660,7 @@ var MINTLIFY_SOURCE_IGNORES = [
|
|
|
10127
10660
|
"snippets/**"
|
|
10128
10661
|
];
|
|
10129
10662
|
var mintlifySource = (options) => {
|
|
10130
|
-
const contentRoot =
|
|
10663
|
+
const contentRoot = isAbsolute8(options.root) ? options.root : join22(resolve8(options.projectRoot), options.root);
|
|
10131
10664
|
const ignore = [...new Set([...options.exclude, ...MINTLIFY_SOURCE_IGNORES])];
|
|
10132
10665
|
const transform = (raw, file) => transformMintlifyContent(raw, {
|
|
10133
10666
|
filePath: file,
|
|
@@ -10144,7 +10677,7 @@ var mintlifySource = (options) => {
|
|
|
10144
10677
|
files.sort();
|
|
10145
10678
|
const unsupported = new Set;
|
|
10146
10679
|
const entries = await Promise.all(files.map(async (file) => {
|
|
10147
|
-
const result = await transform(await
|
|
10680
|
+
const result = await transform(await readFile15(file, "utf-8"), file);
|
|
10148
10681
|
for (const name of result.unsupported) {
|
|
10149
10682
|
unsupported.add(name);
|
|
10150
10683
|
}
|
|
@@ -10153,21 +10686,21 @@ var mintlifySource = (options) => {
|
|
|
10153
10686
|
body: { format: "mdx", text: parsed.content },
|
|
10154
10687
|
data: parsed.data,
|
|
10155
10688
|
raw: result.content,
|
|
10156
|
-
ref:
|
|
10689
|
+
ref: relative14(contentRoot, file),
|
|
10157
10690
|
sourcePath: file
|
|
10158
10691
|
};
|
|
10159
10692
|
}));
|
|
10160
10693
|
const diagnostics = unsupported.size > 0 ? [
|
|
10161
10694
|
{
|
|
10162
10695
|
code: "BLUME_MINTLIFY_UNSUPPORTED",
|
|
10163
|
-
message: `Mintlify components without a Blume equivalent were left as-is: ${[...unsupported].toSorted().join(", ")}.
|
|
10696
|
+
message: `Mintlify components without a Blume equivalent were left as-is: ${[...unsupported].toSorted().join(", ")}. Replace them by hand or provide a matching component.`,
|
|
10164
10697
|
severity: "warning"
|
|
10165
10698
|
}
|
|
10166
10699
|
] : [];
|
|
10167
10700
|
return { diagnostics, entries };
|
|
10168
10701
|
};
|
|
10169
10702
|
const validate = () => {
|
|
10170
|
-
if (!
|
|
10703
|
+
if (!existsSync15(contentRoot)) {
|
|
10171
10704
|
throw new BlumeError({
|
|
10172
10705
|
code: "BLUME_CONTENT_ROOT_MISSING",
|
|
10173
10706
|
file: contentRoot,
|
|
@@ -10179,11 +10712,11 @@ var mintlifySource = (options) => {
|
|
|
10179
10712
|
};
|
|
10180
10713
|
const watch = (onChange) => {
|
|
10181
10714
|
const disposers = [];
|
|
10182
|
-
if (
|
|
10715
|
+
if (existsSync15(contentRoot)) {
|
|
10183
10716
|
const watcher = fsWatch2(contentRoot, { recursive: true }, onChange);
|
|
10184
10717
|
disposers.push(() => watcher.close());
|
|
10185
10718
|
}
|
|
10186
|
-
if (options.configFile &&
|
|
10719
|
+
if (options.configFile && existsSync15(options.configFile)) {
|
|
10187
10720
|
const watcher = fsWatch2(options.configFile, onChange);
|
|
10188
10721
|
disposers.push(() => watcher.close());
|
|
10189
10722
|
}
|
|
@@ -10194,8 +10727,8 @@ var mintlifySource = (options) => {
|
|
|
10194
10727
|
};
|
|
10195
10728
|
};
|
|
10196
10729
|
const read = async (ref) => {
|
|
10197
|
-
const file =
|
|
10198
|
-
const result = await transform(await
|
|
10730
|
+
const file = join22(contentRoot, ref);
|
|
10731
|
+
const result = await transform(await readFile15(file, "utf-8"), file);
|
|
10199
10732
|
return result.content;
|
|
10200
10733
|
};
|
|
10201
10734
|
return {
|
|
@@ -10211,11 +10744,12 @@ var mintlifySource = (options) => {
|
|
|
10211
10744
|
};
|
|
10212
10745
|
|
|
10213
10746
|
// src/core/sources/notion.ts
|
|
10214
|
-
import {
|
|
10747
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
10748
|
+
import { join as join24 } from "pathe";
|
|
10215
10749
|
|
|
10216
10750
|
// src/core/sources/assets.ts
|
|
10217
|
-
import { mkdir as mkdir4, writeFile as
|
|
10218
|
-
import { extname as extname6, join as
|
|
10751
|
+
import { mkdir as mkdir4, writeFile as writeFile6 } from "node:fs/promises";
|
|
10752
|
+
import { extname as extname6, join as join23 } from "pathe";
|
|
10219
10753
|
var MD_IMAGE = /!\[(?<alt>[^\]]*)\]\((?<url>[^)\s]+)\)/gu;
|
|
10220
10754
|
var REMOTE = /^https?:\/\//u;
|
|
10221
10755
|
var SAFE_EXT = /^\.[a-z0-9]+$/iu;
|
|
@@ -10244,7 +10778,7 @@ var materializeAssets = async (markdown, ctx) => {
|
|
|
10244
10778
|
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
10245
10779
|
const file = `${hashText(url)}${extFor(url)}`;
|
|
10246
10780
|
await mkdir4(ctx.assetsDir, { recursive: true });
|
|
10247
|
-
await
|
|
10781
|
+
await writeFile6(join23(ctx.assetsDir, file), bytes);
|
|
10248
10782
|
rewrites.set(url, `${ctx.assetsBaseUrl}/${file}`);
|
|
10249
10783
|
} catch (error) {
|
|
10250
10784
|
diagnostics.push({
|
|
@@ -10279,6 +10813,28 @@ var richToMarkdown = (rich = []) => rich.map((node) => {
|
|
|
10279
10813
|
return node.href ? `[${text}](${node.href})` : text;
|
|
10280
10814
|
}).join("");
|
|
10281
10815
|
var blockField = (block) => block[block.type]?.rich_text ?? [];
|
|
10816
|
+
var RATE_LIMITED = 429;
|
|
10817
|
+
var MAX_RETRIES = 4;
|
|
10818
|
+
var BASE_DELAY_MS = 500;
|
|
10819
|
+
var SECOND_MS = 1000;
|
|
10820
|
+
var withNotionRetry = async (call) => {
|
|
10821
|
+
let lastError;
|
|
10822
|
+
for (let attempt = 0;attempt <= MAX_RETRIES; attempt += 1) {
|
|
10823
|
+
try {
|
|
10824
|
+
return await call();
|
|
10825
|
+
} catch (error) {
|
|
10826
|
+
lastError = error;
|
|
10827
|
+
const { status } = error;
|
|
10828
|
+
if (status !== RATE_LIMITED || attempt === MAX_RETRIES) {
|
|
10829
|
+
throw error;
|
|
10830
|
+
}
|
|
10831
|
+
const retryAfter = Number(error.headers?.["retry-after"]);
|
|
10832
|
+
const wait = retryAfter > 0 ? retryAfter * SECOND_MS : BASE_DELAY_MS * 2 ** attempt;
|
|
10833
|
+
await sleep(wait);
|
|
10834
|
+
}
|
|
10835
|
+
}
|
|
10836
|
+
throw lastError instanceof Error ? lastError : new Error("Notion request failed after retries.");
|
|
10837
|
+
};
|
|
10282
10838
|
var collectAll = async (page, cursor, acc = []) => {
|
|
10283
10839
|
const res = await page(cursor);
|
|
10284
10840
|
const all = [...acc, ...res.results];
|
|
@@ -10338,8 +10894,8 @@ ${text}
|
|
|
10338
10894
|
};
|
|
10339
10895
|
var notionSource = (options, ctx) => {
|
|
10340
10896
|
const props = options.properties ?? {};
|
|
10341
|
-
const cache = snapshotCache(ctx?.cacheDir ??
|
|
10342
|
-
const assetsDir = ctx?.assetsDir ??
|
|
10897
|
+
const cache = snapshotCache(ctx?.cacheDir ?? join24(".blume", "cache", options.name));
|
|
10898
|
+
const assetsDir = ctx?.assetsDir ?? join24(".blume", "public", "blume-assets", options.name);
|
|
10343
10899
|
const assetsBaseUrl = ctx?.assetsBaseUrl ?? `/blume-assets/${options.name}`;
|
|
10344
10900
|
let snapshot = new Map;
|
|
10345
10901
|
const resolveClient = async () => {
|
|
@@ -10358,7 +10914,7 @@ var notionSource = (options, ctx) => {
|
|
|
10358
10914
|
}
|
|
10359
10915
|
return new Client({ auth: options.token ?? process.env.NOTION_TOKEN });
|
|
10360
10916
|
};
|
|
10361
|
-
const childrenOf = (client, blockId) => collectAll((cursor) => client.blocks.children.list({ block_id: blockId, start_cursor: cursor }));
|
|
10917
|
+
const childrenOf = (client, blockId) => collectAll((cursor) => withNotionRetry(() => client.blocks.children.list({ block_id: blockId, start_cursor: cursor })));
|
|
10362
10918
|
const renderContainer = async (client, block, render) => {
|
|
10363
10919
|
const children = async (target) => {
|
|
10364
10920
|
if (!target.has_children) {
|
|
@@ -10444,10 +11000,10 @@ ${rendered.join(`
|
|
|
10444
11000
|
data.sidebar = { order };
|
|
10445
11001
|
}
|
|
10446
11002
|
const slugProp = richToMarkdown(page.properties[props.slug ?? "Slug"]?.rich_text);
|
|
10447
|
-
const slug =
|
|
11003
|
+
const slug = slugify3(slugProp || title) || page.id;
|
|
10448
11004
|
return { data, slug };
|
|
10449
11005
|
};
|
|
10450
|
-
const
|
|
11006
|
+
const toEntry2 = async (client, page) => {
|
|
10451
11007
|
const { data, slug } = frontmatter(page);
|
|
10452
11008
|
const mdx = await renderBlocks(client, await childrenOf(client, page.id));
|
|
10453
11009
|
const assets = await materializeAssets(mdx, {
|
|
@@ -10472,11 +11028,11 @@ ${rendered.join(`
|
|
|
10472
11028
|
const assetDiagnostics = [];
|
|
10473
11029
|
const result = await loadWithCache(options.name, cache, async () => {
|
|
10474
11030
|
const client = await resolveClient();
|
|
10475
|
-
const pages = await collectAll((cursor) => client.databases.query({
|
|
11031
|
+
const pages = await collectAll((cursor) => withNotionRetry(() => client.databases.query({
|
|
10476
11032
|
database_id: options.database,
|
|
10477
11033
|
start_cursor: cursor
|
|
10478
|
-
}));
|
|
10479
|
-
const built = await Promise.all(pages.map((page) =>
|
|
11034
|
+
})));
|
|
11035
|
+
const built = await Promise.all(pages.map((page) => toEntry2(client, page)));
|
|
10480
11036
|
for (const item of built) {
|
|
10481
11037
|
assetDiagnostics.push(...item.diagnostics);
|
|
10482
11038
|
}
|
|
@@ -10507,7 +11063,7 @@ ${rendered.join(`
|
|
|
10507
11063
|
};
|
|
10508
11064
|
|
|
10509
11065
|
// src/core/sources/sanity.ts
|
|
10510
|
-
import { join as
|
|
11066
|
+
import { join as join25 } from "pathe";
|
|
10511
11067
|
|
|
10512
11068
|
// src/core/sources/portable-text.ts
|
|
10513
11069
|
var HEADING_STYLES = {
|
|
@@ -10646,11 +11202,11 @@ var resolveClient = async (options, preview) => {
|
|
|
10646
11202
|
};
|
|
10647
11203
|
var sanitySource = (options, ctx) => {
|
|
10648
11204
|
const fields = options.fields ?? {};
|
|
10649
|
-
const cache = snapshotCache(ctx?.cacheDir ??
|
|
11205
|
+
const cache = snapshotCache(ctx?.cacheDir ?? join25(".blume", "cache", options.name));
|
|
10650
11206
|
let snapshot = new Map;
|
|
10651
|
-
const
|
|
11207
|
+
const toEntry2 = (doc) => {
|
|
10652
11208
|
const slugValue = asString2(getPath(doc, fields.slug ?? "slug.current")) ?? asString2(doc._id) ?? "untitled";
|
|
10653
|
-
const slug =
|
|
11209
|
+
const slug = slugify3(slugValue) || slugify3(asString2(doc._id) ?? "") || "untitled";
|
|
10654
11210
|
const data = {};
|
|
10655
11211
|
const title = asString2(getPath(doc, fields.title ?? "title"));
|
|
10656
11212
|
const description = asString2(getPath(doc, fields.description ?? "description"));
|
|
@@ -10682,7 +11238,7 @@ var sanitySource = (options, ctx) => {
|
|
|
10682
11238
|
const result = await loadWithCache(options.name, cache, async () => {
|
|
10683
11239
|
const client = await resolveClient(options, ctx?.preview ?? false);
|
|
10684
11240
|
const docs = await client.fetch(options.query);
|
|
10685
|
-
return docs.map(
|
|
11241
|
+
return docs.map(toEntry2);
|
|
10686
11242
|
}, ctx?.refresh ?? true);
|
|
10687
11243
|
snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
|
|
10688
11244
|
return result;
|
|
@@ -10721,8 +11277,8 @@ var uniqueNamer = () => {
|
|
|
10721
11277
|
};
|
|
10722
11278
|
var sourceContext = (context, name, runtime) => ({
|
|
10723
11279
|
assetsBaseUrl: `/blume-assets/${name}`,
|
|
10724
|
-
assetsDir:
|
|
10725
|
-
cacheDir:
|
|
11280
|
+
assetsDir: join26(context.outDir, "public", "blume-assets", name),
|
|
11281
|
+
cacheDir: join26(context.outDir, "cache", name),
|
|
10726
11282
|
mode: runtime.mode,
|
|
10727
11283
|
preview: runtime.preview,
|
|
10728
11284
|
projectRoot: context.root,
|
|
@@ -10804,7 +11360,7 @@ var baseName = (def) => {
|
|
|
10804
11360
|
}
|
|
10805
11361
|
return def.prefix ?? def.type;
|
|
10806
11362
|
};
|
|
10807
|
-
var
|
|
11363
|
+
var contentSources = (config, context, runtime) => {
|
|
10808
11364
|
const defs = config.content.sources;
|
|
10809
11365
|
if (!defs || defs.length === 0) {
|
|
10810
11366
|
return [
|
|
@@ -10820,6 +11376,14 @@ var resolveSources = (config, context, runtime) => {
|
|
|
10820
11376
|
const nameFor = uniqueNamer();
|
|
10821
11377
|
return defs.map((def) => buildSource(def, nameFor(baseName(def)), context, runtime));
|
|
10822
11378
|
};
|
|
11379
|
+
var resolveSources = (config, context, runtime) => {
|
|
11380
|
+
const sources = contentSources(config, context, runtime);
|
|
11381
|
+
const references = blumeReferences(config);
|
|
11382
|
+
if (references.length > 0) {
|
|
11383
|
+
sources.push(openApiSource(references, sourceContext(context, "openapi", runtime)));
|
|
11384
|
+
}
|
|
11385
|
+
return sources;
|
|
11386
|
+
};
|
|
10823
11387
|
|
|
10824
11388
|
// src/core/project-graph.ts
|
|
10825
11389
|
var applyConfigOverrides = (config, overrides) => {
|
|
@@ -10848,7 +11412,9 @@ var scanProject = async (root, options = {}) => {
|
|
|
10848
11412
|
});
|
|
10849
11413
|
const { bridge } = configResult;
|
|
10850
11414
|
const config = applyConfigOverrides(configResult.config, options.overrides);
|
|
10851
|
-
const context = resolveProjectContext(root, config
|
|
11415
|
+
const context = resolveProjectContext(root, config, {
|
|
11416
|
+
runtimeDir: options.runtimeDir
|
|
11417
|
+
});
|
|
10852
11418
|
const sources = resolveSources(config, context, {
|
|
10853
11419
|
mode,
|
|
10854
11420
|
preview,
|
|
@@ -10916,8 +11482,8 @@ var scanProject = async (root, options = {}) => {
|
|
|
10916
11482
|
};
|
|
10917
11483
|
|
|
10918
11484
|
// src/cli/env.ts
|
|
10919
|
-
import { existsSync as
|
|
10920
|
-
import { dirname as dirname12, join as
|
|
11485
|
+
import { existsSync as existsSync16, readFileSync as readFileSync9 } from "node:fs";
|
|
11486
|
+
import { dirname as dirname12, join as join27, resolve as resolve9 } from "pathe";
|
|
10921
11487
|
var ENV_LINE = /^\s*(?:export\s+)?(?<key>[A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?<value>.*?)\s*$/u;
|
|
10922
11488
|
var DOUBLE_QUOTED2 = /^"(?<body>[\s\S]*)"$/u;
|
|
10923
11489
|
var SINGLE_QUOTED = /^'(?<body>[\s\S]*)'$/u;
|
|
@@ -10955,8 +11521,8 @@ var applyEnv = (parsed) => {
|
|
|
10955
11521
|
};
|
|
10956
11522
|
var loadFile = (path) => {
|
|
10957
11523
|
try {
|
|
10958
|
-
if (
|
|
10959
|
-
applyEnv(parseEnv(
|
|
11524
|
+
if (existsSync16(path)) {
|
|
11525
|
+
applyEnv(parseEnv(readFileSync9(path, "utf-8")));
|
|
10960
11526
|
}
|
|
10961
11527
|
} catch {}
|
|
10962
11528
|
};
|
|
@@ -10964,10 +11530,10 @@ var loadEnvFiles = (startDir) => {
|
|
|
10964
11530
|
let dir = resolve9(startDir);
|
|
10965
11531
|
let done = false;
|
|
10966
11532
|
while (!done) {
|
|
10967
|
-
loadFile(
|
|
10968
|
-
loadFile(
|
|
11533
|
+
loadFile(join27(dir, ".env.local"));
|
|
11534
|
+
loadFile(join27(dir, ".env"));
|
|
10969
11535
|
const parent = dirname12(dir);
|
|
10970
|
-
done =
|
|
11536
|
+
done = existsSync16(join27(dir, ".git")) || parent === dir;
|
|
10971
11537
|
dir = parent;
|
|
10972
11538
|
}
|
|
10973
11539
|
};
|
|
@@ -11040,7 +11606,8 @@ var prepareProject = async (options) => {
|
|
|
11040
11606
|
mode: options.mode,
|
|
11041
11607
|
overrides: options.overrides,
|
|
11042
11608
|
preview: options.preview,
|
|
11043
|
-
refresh: options.refresh
|
|
11609
|
+
refresh: options.refresh,
|
|
11610
|
+
runtimeDir: options.runtimeDir
|
|
11044
11611
|
});
|
|
11045
11612
|
} catch (error) {
|
|
11046
11613
|
if (error instanceof BlumeError) {
|
|
@@ -11082,29 +11649,38 @@ var prepareProject = async (options) => {
|
|
|
11082
11649
|
|
|
11083
11650
|
// src/cli/commands/build.ts
|
|
11084
11651
|
var ADAPTERS = ["vercel", "node", "netlify", "cloudflare"];
|
|
11652
|
+
var validateBudgetFlags = (args) => {
|
|
11653
|
+
for (const flag of ["budget-js", "budget-css"]) {
|
|
11654
|
+
const value = args[flag];
|
|
11655
|
+
if (value !== undefined && !(Number(value) > 0)) {
|
|
11656
|
+
logger.error(`Invalid --${flag} "${value}" (expected a positive number of kB).`);
|
|
11657
|
+
process.exit(1);
|
|
11658
|
+
}
|
|
11659
|
+
}
|
|
11660
|
+
};
|
|
11085
11661
|
var emitRedirectFiles = async (config, distDir) => {
|
|
11086
11662
|
const { redirects } = config;
|
|
11087
11663
|
if (redirects.length === 0 || config.deployment.output !== "static") {
|
|
11088
11664
|
return;
|
|
11089
11665
|
}
|
|
11090
|
-
await
|
|
11666
|
+
await writeFile7(join28(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
|
|
11091
11667
|
const platformFiles = [
|
|
11092
11668
|
{ content: buildNetlifyRedirects(redirects), name: "_redirects" },
|
|
11093
11669
|
{ content: buildVercelConfig(redirects), name: "vercel.json" }
|
|
11094
11670
|
];
|
|
11095
|
-
await Promise.all(platformFiles.map((file) =>
|
|
11671
|
+
await Promise.all(platformFiles.map((file) => existsSync17(join28(distDir, file.name)) ? Promise.resolve() : writeFile7(join28(distDir, file.name), file.content, "utf-8")));
|
|
11096
11672
|
logger.success(`Emitted redirect files for ${redirects.length} redirect(s)`);
|
|
11097
11673
|
};
|
|
11098
11674
|
var formatBytes = (bytes) => bytes < 1024 ? `${bytes} B` : `${(bytes / 1024).toFixed(bytes < 1024 * 100 ? 1 : 0)} kB`;
|
|
11099
11675
|
var astroAssets = async (distDir, ext) => {
|
|
11100
|
-
const astroDir =
|
|
11101
|
-
if (!
|
|
11676
|
+
const astroDir = join28(distDir, "_astro");
|
|
11677
|
+
if (!existsSync17(astroDir)) {
|
|
11102
11678
|
return [];
|
|
11103
11679
|
}
|
|
11104
11680
|
const entries = await readdir(astroDir);
|
|
11105
11681
|
const files = entries.filter((name) => name.endsWith(`.${ext}`));
|
|
11106
11682
|
const sized = await Promise.all(files.map(async (name) => {
|
|
11107
|
-
const info = await stat(
|
|
11683
|
+
const info = await stat(join28(astroDir, name));
|
|
11108
11684
|
return { name, size: info.size };
|
|
11109
11685
|
}));
|
|
11110
11686
|
return sized.toSorted((a, b) => b.size - a.size);
|
|
@@ -11146,6 +11722,58 @@ var enforceBudget = async (distDir, args) => {
|
|
|
11146
11722
|
}
|
|
11147
11723
|
return passed ? "pass" : "fail";
|
|
11148
11724
|
};
|
|
11725
|
+
var publishBuildArtifacts = async (project, distDir, args) => {
|
|
11726
|
+
if (project.config.search.provider === "pagefind") {
|
|
11727
|
+
logger.start("Building search index");
|
|
11728
|
+
const indexed = await buildSearchIndex(distDir);
|
|
11729
|
+
logger.success(`Indexed ${indexed} page(s) for search`);
|
|
11730
|
+
}
|
|
11731
|
+
await syncSearchProvider(project, {
|
|
11732
|
+
start: (message) => logger.start(message),
|
|
11733
|
+
success: (message) => logger.success(message),
|
|
11734
|
+
warn: (message) => logger.warn(message)
|
|
11735
|
+
});
|
|
11736
|
+
if (project.config.ai.llmsTxt) {
|
|
11737
|
+
const { index, full } = await buildLlmsFiles(project);
|
|
11738
|
+
await Promise.all([
|
|
11739
|
+
writeFile7(join28(distDir, "llms.txt"), index, "utf-8"),
|
|
11740
|
+
writeFile7(join28(distDir, "llms-full.txt"), full, "utf-8")
|
|
11741
|
+
]);
|
|
11742
|
+
logger.success("Generated llms.txt and llms-full.txt");
|
|
11743
|
+
}
|
|
11744
|
+
const sitemap = buildSitemap(project);
|
|
11745
|
+
if (sitemap && !existsSync17(join28(distDir, "sitemap.xml"))) {
|
|
11746
|
+
await writeFile7(join28(distDir, "sitemap.xml"), sitemap, "utf-8");
|
|
11747
|
+
logger.success("Generated sitemap.xml");
|
|
11748
|
+
}
|
|
11749
|
+
const robots = buildRobots(project);
|
|
11750
|
+
if (robots && !existsSync17(join28(distDir, "robots.txt"))) {
|
|
11751
|
+
await writeFile7(join28(distDir, "robots.txt"), robots, "utf-8");
|
|
11752
|
+
logger.success("Generated robots.txt");
|
|
11753
|
+
}
|
|
11754
|
+
await emitRedirectFiles(project.config, distDir);
|
|
11755
|
+
const { config } = project;
|
|
11756
|
+
const features = serverFeatures(config);
|
|
11757
|
+
logger.box([
|
|
11758
|
+
`Output ${config.deployment.output}`,
|
|
11759
|
+
`Adapter ${config.deployment.adapter ?? "none"}`,
|
|
11760
|
+
`Site ${config.deployment.site ?? "not set"}`,
|
|
11761
|
+
`Search ${config.search.provider}`,
|
|
11762
|
+
`Redirects ${config.redirects.length}`,
|
|
11763
|
+
`Sitemap ${sitemap ? "yes" : "no (set deployment.site)"}`,
|
|
11764
|
+
`Robots ${robots ? "yes" : "no"}`,
|
|
11765
|
+
`LLM files ${config.ai.llmsTxt ? "yes" : "no"}`,
|
|
11766
|
+
`Server features ${features.length > 0 ? features.join(", ") : "none"}`
|
|
11767
|
+
].join(`
|
|
11768
|
+
`));
|
|
11769
|
+
if (args.analyze) {
|
|
11770
|
+
await reportBundleSizes(distDir);
|
|
11771
|
+
}
|
|
11772
|
+
if (await enforceBudget(distDir, args) === "fail") {
|
|
11773
|
+
process.exit(1);
|
|
11774
|
+
}
|
|
11775
|
+
logger.success(`Built to ${distDir}`);
|
|
11776
|
+
};
|
|
11149
11777
|
var buildCommand = defineCommand2({
|
|
11150
11778
|
args: {
|
|
11151
11779
|
adapter: {
|
|
@@ -11168,6 +11796,10 @@ var buildCommand = defineCommand2({
|
|
|
11168
11796
|
description: "Fail if total client JavaScript exceeds this many kB.",
|
|
11169
11797
|
type: "string"
|
|
11170
11798
|
},
|
|
11799
|
+
isolated: {
|
|
11800
|
+
description: "Build into an isolated .blume-verify runtime (and its own dist) so a running dev server and the real dist/ are untouched. For verifying changes while `blume dev` runs.",
|
|
11801
|
+
type: "boolean"
|
|
11802
|
+
},
|
|
11171
11803
|
output: {
|
|
11172
11804
|
description: "Output mode: static | server.",
|
|
11173
11805
|
type: "string"
|
|
@@ -11184,6 +11816,11 @@ var buildCommand = defineCommand2({
|
|
|
11184
11816
|
},
|
|
11185
11817
|
async run({ args }) {
|
|
11186
11818
|
const root = process.cwd();
|
|
11819
|
+
const runtimeDir = args.isolated ? ".blume-verify" : process.env.BLUME_RUNTIME_DIR;
|
|
11820
|
+
refuseIfDevRunning(root, "building", runtimeDir);
|
|
11821
|
+
if (args.isolated) {
|
|
11822
|
+
await ensureGitignore(root, [".blume-verify/"]);
|
|
11823
|
+
}
|
|
11187
11824
|
if (args.output && args.output !== "static" && args.output !== "server") {
|
|
11188
11825
|
logger.error(`Invalid --output "${args.output}" (use static | server).`);
|
|
11189
11826
|
process.exit(1);
|
|
@@ -11192,6 +11829,7 @@ var buildCommand = defineCommand2({
|
|
|
11192
11829
|
logger.error(`Invalid --adapter "${args.adapter}" (use ${ADAPTERS.join(" | ")}).`);
|
|
11193
11830
|
process.exit(1);
|
|
11194
11831
|
}
|
|
11832
|
+
validateBudgetFlags(args);
|
|
11195
11833
|
const project = await prepareProject({
|
|
11196
11834
|
mode: "build",
|
|
11197
11835
|
overrides: {
|
|
@@ -11201,6 +11839,7 @@ var buildCommand = defineCommand2({
|
|
|
11201
11839
|
},
|
|
11202
11840
|
preview: args.preview,
|
|
11203
11841
|
root,
|
|
11842
|
+
runtimeDir,
|
|
11204
11843
|
strict: args.strict
|
|
11205
11844
|
});
|
|
11206
11845
|
logger.start(`Building ${project.graph.pages.length} page(s) (${project.config.deployment.output} output)`);
|
|
@@ -11208,68 +11847,27 @@ var buildCommand = defineCommand2({
|
|
|
11208
11847
|
logLevel: "info",
|
|
11209
11848
|
root: project.context.outDir
|
|
11210
11849
|
});
|
|
11211
|
-
const distDir =
|
|
11212
|
-
if (
|
|
11213
|
-
logger.
|
|
11214
|
-
|
|
11215
|
-
logger.success(`Indexed ${indexed} page(s) for search`);
|
|
11216
|
-
}
|
|
11217
|
-
await syncSearchProvider(project, {
|
|
11218
|
-
start: (message) => logger.start(message),
|
|
11219
|
-
success: (message) => logger.success(message),
|
|
11220
|
-
warn: (message) => logger.warn(message)
|
|
11221
|
-
});
|
|
11222
|
-
if (project.config.ai.llmsTxt) {
|
|
11223
|
-
const { index, full } = await buildLlmsFiles(project);
|
|
11224
|
-
await Promise.all([
|
|
11225
|
-
writeFile6(join24(distDir, "llms.txt"), index, "utf-8"),
|
|
11226
|
-
writeFile6(join24(distDir, "llms-full.txt"), full, "utf-8")
|
|
11227
|
-
]);
|
|
11228
|
-
logger.success("Generated llms.txt and llms-full.txt");
|
|
11229
|
-
}
|
|
11230
|
-
const sitemap = buildSitemap(project);
|
|
11231
|
-
if (sitemap && !existsSync15(join24(distDir, "sitemap.xml"))) {
|
|
11232
|
-
await writeFile6(join24(distDir, "sitemap.xml"), sitemap, "utf-8");
|
|
11233
|
-
logger.success("Generated sitemap.xml");
|
|
11234
|
-
}
|
|
11235
|
-
const robots = buildRobots(project);
|
|
11236
|
-
if (robots && !existsSync15(join24(distDir, "robots.txt"))) {
|
|
11237
|
-
await writeFile6(join24(distDir, "robots.txt"), robots, "utf-8");
|
|
11238
|
-
logger.success("Generated robots.txt");
|
|
11239
|
-
}
|
|
11240
|
-
await emitRedirectFiles(project.config, distDir);
|
|
11241
|
-
const { config } = project;
|
|
11242
|
-
const features = serverFeatures(config);
|
|
11243
|
-
logger.box([
|
|
11244
|
-
`Output ${config.deployment.output}`,
|
|
11245
|
-
`Adapter ${config.deployment.adapter ?? "none"}`,
|
|
11246
|
-
`Site ${config.deployment.site ?? "not set"}`,
|
|
11247
|
-
`Search ${config.search.provider}`,
|
|
11248
|
-
`Redirects ${config.redirects.length}`,
|
|
11249
|
-
`Sitemap ${sitemap ? "yes" : "no (set deployment.site)"}`,
|
|
11250
|
-
`Robots ${robots ? "yes" : "no"}`,
|
|
11251
|
-
`LLM files ${config.ai.llmsTxt ? "yes" : "no"}`,
|
|
11252
|
-
`Server features ${features.length > 0 ? features.join(", ") : "none"}`
|
|
11253
|
-
].join(`
|
|
11254
|
-
`));
|
|
11255
|
-
if (args.analyze) {
|
|
11256
|
-
await reportBundleSizes(distDir);
|
|
11257
|
-
}
|
|
11258
|
-
if (await enforceBudget(distDir, args) === "fail") {
|
|
11259
|
-
process.exit(1);
|
|
11850
|
+
const distDir = project.context.distDir ?? join28(root, "dist");
|
|
11851
|
+
if (runtimeDir) {
|
|
11852
|
+
logger.success(`Isolated build OK — output at ${distDir} (not published).`);
|
|
11853
|
+
return;
|
|
11260
11854
|
}
|
|
11261
|
-
|
|
11855
|
+
await publishBuildArtifacts(project, distDir, args);
|
|
11262
11856
|
}
|
|
11263
11857
|
});
|
|
11264
11858
|
|
|
11265
11859
|
// src/cli/commands/check.ts
|
|
11266
|
-
import { existsSync as
|
|
11860
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
11267
11861
|
import { check } from "@astrojs/check";
|
|
11268
11862
|
import { sync } from "astro";
|
|
11269
11863
|
import { defineCommand as defineCommand3 } from "citty";
|
|
11270
|
-
import { join as
|
|
11864
|
+
import { join as join29 } from "pathe";
|
|
11271
11865
|
var checkCommand = defineCommand3({
|
|
11272
11866
|
args: {
|
|
11867
|
+
isolated: {
|
|
11868
|
+
description: "Type-check in an isolated .blume-verify runtime so a running dev server is untouched. For verifying changes while `blume dev` runs.",
|
|
11869
|
+
type: "boolean"
|
|
11870
|
+
},
|
|
11273
11871
|
preview: {
|
|
11274
11872
|
description: "Include drafts and unpublished CMS content.",
|
|
11275
11873
|
type: "boolean"
|
|
@@ -11285,21 +11883,27 @@ var checkCommand = defineCommand3({
|
|
|
11285
11883
|
},
|
|
11286
11884
|
async run({ args }) {
|
|
11287
11885
|
const root = process.cwd();
|
|
11886
|
+
const runtimeDir = args.isolated ? ".blume-verify" : process.env.BLUME_RUNTIME_DIR;
|
|
11887
|
+
refuseIfDevRunning(root, "checking", runtimeDir);
|
|
11888
|
+
if (args.isolated) {
|
|
11889
|
+
await ensureGitignore(root, [".blume-verify/"]);
|
|
11890
|
+
}
|
|
11288
11891
|
const project = await prepareProject({
|
|
11289
11892
|
mode: "build",
|
|
11290
11893
|
preview: args.preview,
|
|
11291
11894
|
root,
|
|
11895
|
+
runtimeDir,
|
|
11292
11896
|
strict: args.strict
|
|
11293
11897
|
});
|
|
11294
11898
|
const { outDir } = project.context;
|
|
11295
11899
|
await sync({ logLevel: "warn", root: outDir });
|
|
11296
|
-
const tsconfig =
|
|
11900
|
+
const tsconfig = join29(root, "tsconfig.json");
|
|
11297
11901
|
logger.start(`Type-checking ${project.graph.pages.length} page(s)`);
|
|
11298
11902
|
const failed = await check({
|
|
11299
11903
|
minimumFailingSeverity: "error",
|
|
11300
11904
|
minimumSeverity: "hint",
|
|
11301
11905
|
root: outDir,
|
|
11302
|
-
tsconfig:
|
|
11906
|
+
tsconfig: existsSync18(tsconfig) ? tsconfig : undefined,
|
|
11303
11907
|
watch: false
|
|
11304
11908
|
});
|
|
11305
11909
|
if (failed) {
|
|
@@ -11315,6 +11919,9 @@ import { watch } from "node:fs";
|
|
|
11315
11919
|
import { dev } from "astro";
|
|
11316
11920
|
import { defineCommand as defineCommand4 } from "citty";
|
|
11317
11921
|
|
|
11922
|
+
// src/astro/static-assets.ts
|
|
11923
|
+
import { extname as extname7, join as join30, relative as relative15, resolve as resolve10, sep } from "pathe";
|
|
11924
|
+
|
|
11318
11925
|
// src/astro/integration.ts
|
|
11319
11926
|
var overlayServer = null;
|
|
11320
11927
|
var overlayChannel = () => overlayServer?.ws ?? overlayServer?.hot;
|
|
@@ -11348,6 +11955,20 @@ ${body}`,
|
|
|
11348
11955
|
});
|
|
11349
11956
|
};
|
|
11350
11957
|
|
|
11958
|
+
// src/cli/args.ts
|
|
11959
|
+
var MAX_PORT = 65535;
|
|
11960
|
+
var parsePort = (value) => {
|
|
11961
|
+
if (value === undefined) {
|
|
11962
|
+
return;
|
|
11963
|
+
}
|
|
11964
|
+
const port = Number(value);
|
|
11965
|
+
if (!(Number.isInteger(port) && port >= 1 && port <= MAX_PORT)) {
|
|
11966
|
+
logger.error(`Invalid --port "${value}" (expected an integer 1-${MAX_PORT}).`);
|
|
11967
|
+
process.exit(1);
|
|
11968
|
+
}
|
|
11969
|
+
return port;
|
|
11970
|
+
};
|
|
11971
|
+
|
|
11351
11972
|
// src/cli/commands/dev.ts
|
|
11352
11973
|
var devCommand = defineCommand4({
|
|
11353
11974
|
args: {
|
|
@@ -11376,7 +11997,8 @@ var devCommand = defineCommand4({
|
|
|
11376
11997
|
const root = process.cwd();
|
|
11377
11998
|
const preview = args.preview ?? false;
|
|
11378
11999
|
const overrides = args["content-dir"] ? { contentRoot: args["content-dir"] } : undefined;
|
|
11379
|
-
const
|
|
12000
|
+
const explicitPort = parsePort(args.port);
|
|
12001
|
+
const port = explicitPort ?? 4321;
|
|
11380
12002
|
const devServerUrl = `http://localhost:${port}`;
|
|
11381
12003
|
const project = await prepareProject({
|
|
11382
12004
|
devServerUrl,
|
|
@@ -11389,13 +12011,15 @@ var devCommand = defineCommand4({
|
|
|
11389
12011
|
if (project.bridge) {
|
|
11390
12012
|
logger.info('Detected docs.json — running in Mintlify bridge mode (no migration). Run "blume migrate mintlify" to convert permanently.');
|
|
11391
12013
|
}
|
|
12014
|
+
const releaseLock = acquireDevLock(project.context.outDir);
|
|
12015
|
+
process.on("exit", releaseLock);
|
|
11392
12016
|
const server = await dev({
|
|
11393
12017
|
logLevel: args.debug ? "debug" : "info",
|
|
11394
12018
|
root: project.context.outDir,
|
|
11395
12019
|
server: {
|
|
11396
12020
|
host: args.host ?? false,
|
|
11397
12021
|
open: args.open ?? false,
|
|
11398
|
-
port:
|
|
12022
|
+
port: explicitPort
|
|
11399
12023
|
}
|
|
11400
12024
|
});
|
|
11401
12025
|
showBlumeErrorOverlay(project.diagnostics);
|
|
@@ -11436,6 +12060,7 @@ var devCommand = defineCommand4({
|
|
|
11436
12060
|
for (const dispose of disposers) {
|
|
11437
12061
|
dispose();
|
|
11438
12062
|
}
|
|
12063
|
+
releaseLock();
|
|
11439
12064
|
await server.stop();
|
|
11440
12065
|
process.exit(0);
|
|
11441
12066
|
};
|
|
@@ -11505,6 +12130,7 @@ var doctorCommand = defineCommand5({
|
|
|
11505
12130
|
}
|
|
11506
12131
|
if (args.json) {
|
|
11507
12132
|
if (reportDiagnosticsJson(diagnostics, root)) {
|
|
12133
|
+
await flushStdout();
|
|
11508
12134
|
process.exit(1);
|
|
11509
12135
|
}
|
|
11510
12136
|
return;
|
|
@@ -11520,15 +12146,19 @@ var doctorCommand = defineCommand5({
|
|
|
11520
12146
|
});
|
|
11521
12147
|
|
|
11522
12148
|
// src/cli/commands/eject.ts
|
|
11523
|
-
import { readFile as
|
|
12149
|
+
import { readFile as readFile17, writeFile as writeFile9 } from "node:fs/promises";
|
|
11524
12150
|
import { defineCommand as defineCommand6 } from "citty";
|
|
11525
|
-
import { join as
|
|
12151
|
+
import { join as join32, relative as relative17 } from "pathe";
|
|
11526
12152
|
|
|
11527
12153
|
// src/registry/eject.ts
|
|
11528
|
-
import { existsSync as
|
|
11529
|
-
import { cp, mkdir as mkdir5, readFile as
|
|
11530
|
-
import { join as
|
|
12154
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
12155
|
+
import { cp, mkdir as mkdir5, readFile as readFile16, rm as rm2, writeFile as writeFile8 } from "node:fs/promises";
|
|
12156
|
+
import { join as join31, relative as relative16 } from "pathe";
|
|
11531
12157
|
var POSIX = (path) => path.split("\\").join("/");
|
|
12158
|
+
var ejectOpenApiData = (project) => {
|
|
12159
|
+
const source = project.sources.find(isOpenApiSource);
|
|
12160
|
+
return source ? source.openApiData() : {};
|
|
12161
|
+
};
|
|
11532
12162
|
var askFiles = async (project, srcDir, genDir) => {
|
|
11533
12163
|
const { ask } = project.config.ai;
|
|
11534
12164
|
if (!ask?.enabled) {
|
|
@@ -11538,14 +12168,14 @@ var askFiles = async (project, srcDir, genDir) => {
|
|
|
11538
12168
|
const files = [
|
|
11539
12169
|
{
|
|
11540
12170
|
content: askEndpointTemplate(resolveAskBackend(ask), grounded),
|
|
11541
|
-
path:
|
|
12171
|
+
path: join31(srcDir, "pages", "api", "ask.ts")
|
|
11542
12172
|
}
|
|
11543
12173
|
];
|
|
11544
12174
|
if (grounded) {
|
|
11545
12175
|
files.push({
|
|
11546
12176
|
content: `${JSON.stringify(await buildAskData(project))}
|
|
11547
12177
|
`,
|
|
11548
|
-
path:
|
|
12178
|
+
path: join31(genDir, "ask-data.json")
|
|
11549
12179
|
});
|
|
11550
12180
|
}
|
|
11551
12181
|
return files;
|
|
@@ -11553,15 +12183,15 @@ var askFiles = async (project, srcDir, genDir) => {
|
|
|
11553
12183
|
var eject = async (root) => {
|
|
11554
12184
|
const project = await scanProject(root, { mode: "build" });
|
|
11555
12185
|
const { context, config } = project;
|
|
11556
|
-
const srcDir =
|
|
11557
|
-
const genDir =
|
|
12186
|
+
const srcDir = join31(root, "src");
|
|
12187
|
+
const genDir = join31(srcDir, "generated");
|
|
11558
12188
|
const askEnabled = config.ai.ask?.enabled ?? false;
|
|
11559
12189
|
const exportPdf = config.export.pdf;
|
|
11560
12190
|
const exportEpub = config.export.epub;
|
|
11561
12191
|
const [pages, needsReactRaw, userTheme, rawMarkdown, islands, examples] = await Promise.all([
|
|
11562
12192
|
context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),
|
|
11563
12193
|
detectNeedsReact(root),
|
|
11564
|
-
context.themeFile ?
|
|
12194
|
+
context.themeFile ? readFile16(context.themeFile, "utf-8") : Promise.resolve(""),
|
|
11565
12195
|
buildRawMarkdown(project),
|
|
11566
12196
|
discoverIslands(root),
|
|
11567
12197
|
discoverExamples(root, config.examples)
|
|
@@ -11575,13 +12205,13 @@ var eject = async (root) => {
|
|
|
11575
12205
|
const needsSvelte = frameworks.has("svelte");
|
|
11576
12206
|
const relContext = {
|
|
11577
12207
|
...context,
|
|
11578
|
-
contentRoot: POSIX(
|
|
12208
|
+
contentRoot: POSIX(relative16(root, context.contentRoot)),
|
|
11579
12209
|
outDir: ".",
|
|
11580
12210
|
root: "."
|
|
11581
12211
|
};
|
|
11582
|
-
const componentsImport = context.componentsFile ? `../../${POSIX(
|
|
12212
|
+
const componentsImport = context.componentsFile ? `../../${POSIX(relative16(root, context.componentsFile))}` : null;
|
|
11583
12213
|
const relPages = pages.map((page) => ({
|
|
11584
|
-
entrypoint: POSIX(
|
|
12214
|
+
entrypoint: POSIX(relative16(root, page.entrypoint)),
|
|
11585
12215
|
pattern: page.pattern
|
|
11586
12216
|
}));
|
|
11587
12217
|
const staged = collectStaged(project);
|
|
@@ -11598,17 +12228,19 @@ var eject = async (root) => {
|
|
|
11598
12228
|
needsReact,
|
|
11599
12229
|
needsSvelte,
|
|
11600
12230
|
needsVue,
|
|
12231
|
+
openapiPath: "./src/generated/openapi.json",
|
|
11601
12232
|
pages: relPages,
|
|
11602
12233
|
searchClientPath: "./src/generated/search-client.ts",
|
|
11603
12234
|
themePath: "./src/generated/app.css"
|
|
11604
12235
|
}),
|
|
11605
|
-
path:
|
|
12236
|
+
path: join31(root, "astro.config.mjs")
|
|
11606
12237
|
},
|
|
11607
12238
|
{
|
|
11608
12239
|
content: runtimeTsconfigTemplate(),
|
|
11609
|
-
path:
|
|
12240
|
+
path: join31(root, "tsconfig.json"),
|
|
12241
|
+
skipIfExists: true
|
|
11610
12242
|
},
|
|
11611
|
-
{ content: envTemplate(), path:
|
|
12243
|
+
{ content: envTemplate(), path: join31(srcDir, "env.d.ts") },
|
|
11612
12244
|
{
|
|
11613
12245
|
content: contentConfigTemplate({
|
|
11614
12246
|
config,
|
|
@@ -11616,7 +12248,7 @@ var eject = async (root) => {
|
|
|
11616
12248
|
staged: hasStaged,
|
|
11617
12249
|
stagedBase: stagedDir
|
|
11618
12250
|
}),
|
|
11619
|
-
path:
|
|
12251
|
+
path: join31(srcDir, "content.config.ts")
|
|
11620
12252
|
},
|
|
11621
12253
|
{
|
|
11622
12254
|
content: catchAllPageTemplate({
|
|
@@ -11626,19 +12258,19 @@ var eject = async (root) => {
|
|
|
11626
12258
|
mathEnabled: config.markdown.math,
|
|
11627
12259
|
needsReact
|
|
11628
12260
|
}),
|
|
11629
|
-
path:
|
|
12261
|
+
path: join31(srcDir, "pages", "[...slug].astro")
|
|
11630
12262
|
},
|
|
11631
12263
|
{
|
|
11632
12264
|
content: planComponentSlots(componentsImport, null).module,
|
|
11633
|
-
path:
|
|
12265
|
+
path: join31(genDir, "components.ts")
|
|
11634
12266
|
},
|
|
11635
12267
|
{
|
|
11636
12268
|
content: islandMapTemplate(islands.islands),
|
|
11637
|
-
path:
|
|
12269
|
+
path: join31(genDir, "islands.ts")
|
|
11638
12270
|
},
|
|
11639
12271
|
{
|
|
11640
12272
|
content: exampleMapTemplate(examples.examples),
|
|
11641
|
-
path:
|
|
12273
|
+
path: join31(genDir, "examples.ts")
|
|
11642
12274
|
},
|
|
11643
12275
|
{
|
|
11644
12276
|
content: tailwindEntryTemplate({
|
|
@@ -11650,21 +12282,26 @@ var eject = async (root) => {
|
|
|
11650
12282
|
twoslashCss: twoslashCss(),
|
|
11651
12283
|
userTheme
|
|
11652
12284
|
}),
|
|
11653
|
-
path:
|
|
12285
|
+
path: join31(genDir, "app.css")
|
|
12286
|
+
},
|
|
12287
|
+
{ content: buildRuntimeData(project), path: join31(genDir, "data.json") },
|
|
12288
|
+
{
|
|
12289
|
+
content: `${JSON.stringify(ejectOpenApiData(project))}
|
|
12290
|
+
`,
|
|
12291
|
+
path: join31(genDir, "openapi.json")
|
|
11654
12292
|
},
|
|
11655
|
-
{ content: buildRuntimeData(project), path: join26(genDir, "data.json") },
|
|
11656
12293
|
{
|
|
11657
12294
|
content: `${JSON.stringify(rawMarkdown)}
|
|
11658
12295
|
`,
|
|
11659
|
-
path:
|
|
12296
|
+
path: join31(genDir, "raw-markdown.json")
|
|
11660
12297
|
},
|
|
11661
12298
|
{
|
|
11662
12299
|
content: rawMarkdownEndpointTemplate(),
|
|
11663
|
-
path:
|
|
12300
|
+
path: join31(srcDir, "pages", "[...slug].md.ts")
|
|
11664
12301
|
},
|
|
11665
12302
|
{
|
|
11666
12303
|
content: rawMarkdownEndpointTemplate(),
|
|
11667
|
-
path:
|
|
12304
|
+
path: join31(srcDir, "pages", "[...slug].mdx.ts")
|
|
11668
12305
|
}
|
|
11669
12306
|
];
|
|
11670
12307
|
if (askEnabled) {
|
|
@@ -11673,34 +12310,34 @@ var eject = async (root) => {
|
|
|
11673
12310
|
if (config.seo.og.enabled) {
|
|
11674
12311
|
files.push({
|
|
11675
12312
|
content: ogEndpointTemplate(customOgRoutes(pages, config.title)),
|
|
11676
|
-
path:
|
|
12313
|
+
path: join31(srcDir, "pages", "og", "[...slug].png.ts")
|
|
11677
12314
|
});
|
|
11678
12315
|
}
|
|
11679
12316
|
if (!routeIsTaken(pages, project.graph.pages, "/404")) {
|
|
11680
12317
|
files.push({
|
|
11681
12318
|
content: notFoundPageTemplate(),
|
|
11682
|
-
path:
|
|
12319
|
+
path: join31(srcDir, "pages", "404.astro")
|
|
11683
12320
|
});
|
|
11684
12321
|
}
|
|
11685
12322
|
files.push({
|
|
11686
12323
|
content: searchClientTemplate(config),
|
|
11687
|
-
path:
|
|
12324
|
+
path: join31(genDir, "search-client.ts")
|
|
11688
12325
|
});
|
|
11689
12326
|
if (servesStaticIndex(config.search.provider)) {
|
|
11690
12327
|
const documents = await buildSearchDocuments(project);
|
|
11691
12328
|
files.push({
|
|
11692
12329
|
content: `${JSON.stringify(documents)}
|
|
11693
12330
|
`,
|
|
11694
|
-
path:
|
|
12331
|
+
path: join31(genDir, "search.json")
|
|
11695
12332
|
}, {
|
|
11696
12333
|
content: searchEndpointTemplate(),
|
|
11697
|
-
path:
|
|
12334
|
+
path: join31(srcDir, "pages", "blume-search.json.ts")
|
|
11698
12335
|
});
|
|
11699
12336
|
}
|
|
11700
12337
|
if (config.search.provider === "mixedbread") {
|
|
11701
12338
|
files.push({
|
|
11702
12339
|
content: mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""),
|
|
11703
|
-
path:
|
|
12340
|
+
path: join31(srcDir, "pages", "api", "search.ts")
|
|
11704
12341
|
});
|
|
11705
12342
|
}
|
|
11706
12343
|
const feeds = buildRssFeeds(project);
|
|
@@ -11709,13 +12346,13 @@ var eject = async (root) => {
|
|
|
11709
12346
|
files.push({
|
|
11710
12347
|
content: `${JSON.stringify(feedXml)}
|
|
11711
12348
|
`,
|
|
11712
|
-
path:
|
|
12349
|
+
path: join31(genDir, "rss.json")
|
|
11713
12350
|
}, {
|
|
11714
12351
|
content: rssEndpointTemplate(),
|
|
11715
|
-
path:
|
|
12352
|
+
path: join31(srcDir, "pages", "[section]", "rss.xml.ts")
|
|
11716
12353
|
});
|
|
11717
12354
|
}
|
|
11718
|
-
if (
|
|
12355
|
+
if (hasScalarReferences(config)) {
|
|
11719
12356
|
const references = await buildReferenceFiles({
|
|
11720
12357
|
config,
|
|
11721
12358
|
contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
|
|
@@ -11724,40 +12361,41 @@ var eject = async (root) => {
|
|
|
11724
12361
|
for (const file of references.files) {
|
|
11725
12362
|
files.push({
|
|
11726
12363
|
content: file.content,
|
|
11727
|
-
path:
|
|
12364
|
+
path: join31(srcDir, "pages", file.pagePath)
|
|
11728
12365
|
});
|
|
11729
12366
|
}
|
|
11730
12367
|
}
|
|
11731
12368
|
files.push(...islands.islands.map((island) => ({
|
|
11732
12369
|
content: islandWrapperTemplate(island),
|
|
11733
|
-
path:
|
|
12370
|
+
path: join31(genDir, "islands", `${island.name}.astro`)
|
|
11734
12371
|
})), ...examples.examples.map((example) => ({
|
|
11735
12372
|
content: exampleWrapperTemplate(example),
|
|
11736
|
-
path:
|
|
12373
|
+
path: join31(genDir, "examples", `${exampleSlug(example.path)}.astro`)
|
|
11737
12374
|
})));
|
|
11738
12375
|
for (const [entryId, content] of staged) {
|
|
11739
|
-
files.push({ content, path:
|
|
12376
|
+
files.push({ content, path: join31(root, stagedDir, entryId) });
|
|
11740
12377
|
}
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
await
|
|
12378
|
+
const written = files.filter((file) => !(file.skipIfExists && existsSync19(file.path)));
|
|
12379
|
+
await Promise.all(written.map(async (file) => {
|
|
12380
|
+
await mkdir5(join31(file.path, ".."), { recursive: true });
|
|
12381
|
+
await writeFile8(file.path, file.content, "utf-8");
|
|
11744
12382
|
}));
|
|
11745
|
-
const assetsSrc =
|
|
11746
|
-
if (
|
|
11747
|
-
await cp(assetsSrc,
|
|
12383
|
+
const assetsSrc = join31(context.outDir, "public", "blume-assets");
|
|
12384
|
+
if (existsSync19(assetsSrc)) {
|
|
12385
|
+
await cp(assetsSrc, join31(root, "public", "blume-assets"), {
|
|
11748
12386
|
recursive: true
|
|
11749
12387
|
});
|
|
11750
12388
|
}
|
|
11751
12389
|
await rm2(context.outDir, { force: true, recursive: true });
|
|
11752
|
-
return
|
|
12390
|
+
return written.map((file) => file.path);
|
|
11753
12391
|
};
|
|
11754
12392
|
|
|
11755
12393
|
// src/cli/commands/eject.ts
|
|
11756
12394
|
var updatePackageScripts = async (root) => {
|
|
11757
|
-
const pkgPath =
|
|
12395
|
+
const pkgPath = join32(root, "package.json");
|
|
11758
12396
|
let pkg;
|
|
11759
12397
|
try {
|
|
11760
|
-
pkg = JSON.parse(await
|
|
12398
|
+
pkg = JSON.parse(await readFile17(pkgPath, "utf-8"));
|
|
11761
12399
|
} catch {
|
|
11762
12400
|
return;
|
|
11763
12401
|
}
|
|
@@ -11768,7 +12406,7 @@ var updatePackageScripts = async (root) => {
|
|
|
11768
12406
|
dev: "astro dev",
|
|
11769
12407
|
preview: "astro preview"
|
|
11770
12408
|
};
|
|
11771
|
-
await
|
|
12409
|
+
await writeFile9(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
11772
12410
|
`, "utf-8");
|
|
11773
12411
|
};
|
|
11774
12412
|
var ejectCommand = defineCommand6({
|
|
@@ -11781,8 +12419,9 @@ var ejectCommand = defineCommand6({
|
|
|
11781
12419
|
},
|
|
11782
12420
|
async run({ args }) {
|
|
11783
12421
|
const root = process.cwd();
|
|
12422
|
+
refuseIfDevRunning(root, "ejecting");
|
|
11784
12423
|
if (!args.yes) {
|
|
11785
|
-
logger.warn("Eject is one-way: it writes astro.config.mjs and
|
|
12424
|
+
logger.warn("Eject is one-way: it writes astro.config.mjs, src/, and (if absent) tsconfig.json, rewrites your package.json scripts, and removes .blume. An existing tsconfig.json is left untouched.");
|
|
11786
12425
|
logger.info("Re-run with --yes to proceed.");
|
|
11787
12426
|
return;
|
|
11788
12427
|
}
|
|
@@ -11790,7 +12429,7 @@ var ejectCommand = defineCommand6({
|
|
|
11790
12429
|
await updatePackageScripts(root);
|
|
11791
12430
|
logger.success(`Ejected ${files.length} file(s):`);
|
|
11792
12431
|
for (const file of files) {
|
|
11793
|
-
process.stdout.write(` ${
|
|
12432
|
+
process.stdout.write(` ${relative17(root, file)}
|
|
11794
12433
|
`);
|
|
11795
12434
|
}
|
|
11796
12435
|
logger.box(`Your project is now a standalone Astro app.
|
|
@@ -11803,10 +12442,10 @@ The blume package remains importable.`);
|
|
|
11803
12442
|
});
|
|
11804
12443
|
|
|
11805
12444
|
// src/cli/commands/init.ts
|
|
11806
|
-
import { existsSync as
|
|
11807
|
-
import { mkdir as mkdir6, writeFile as
|
|
12445
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
12446
|
+
import { mkdir as mkdir6, writeFile as writeFile10 } from "node:fs/promises";
|
|
11808
12447
|
import { defineCommand as defineCommand7 } from "citty";
|
|
11809
|
-
import { basename as basename4, dirname as dirname13, join as
|
|
12448
|
+
import { basename as basename4, dirname as dirname13, isAbsolute as isAbsolute9, join as join33, relative as relative18 } from "pathe";
|
|
11810
12449
|
var toPackageName = (raw) => raw.toLowerCase().replaceAll(/[^a-z0-9._-]+/gu, "-").replaceAll(/^[-_.]+|[-_.]+$/gu, "") || "docs";
|
|
11811
12450
|
var packageTemplate = (name, version) => `{
|
|
11812
12451
|
"name": ${JSON.stringify(name)},
|
|
@@ -11854,7 +12493,7 @@ var STARTERS = {
|
|
|
11854
12493
|
files: (dir) => [
|
|
11855
12494
|
{
|
|
11856
12495
|
content: page("API Reference", "Explore the API.", "# API Reference\n\nYour OpenAPI spec renders at [`/api`](/api). Point `openapi.sources` at your own spec in `blume.config.ts`."),
|
|
11857
|
-
path:
|
|
12496
|
+
path: join33(dir, "index.mdx")
|
|
11858
12497
|
}
|
|
11859
12498
|
]
|
|
11860
12499
|
},
|
|
@@ -11869,7 +12508,7 @@ var STARTERS = {
|
|
|
11869
12508
|
files: (dir) => [
|
|
11870
12509
|
{
|
|
11871
12510
|
content: page("Introduction", "Welcome to your new Blume docs.", "# Introduction\n\nWrite your docs here, and log releases under `changelog/`."),
|
|
11872
|
-
path:
|
|
12511
|
+
path: join33(dir, "index.mdx")
|
|
11873
12512
|
},
|
|
11874
12513
|
{
|
|
11875
12514
|
content: `---
|
|
@@ -11880,7 +12519,7 @@ date: 2026-01-01
|
|
|
11880
12519
|
|
|
11881
12520
|
The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries beside it.
|
|
11882
12521
|
`,
|
|
11883
|
-
path:
|
|
12522
|
+
path: join33(dir, "changelog", "v1-0-0.mdx")
|
|
11884
12523
|
}
|
|
11885
12524
|
]
|
|
11886
12525
|
},
|
|
@@ -11893,7 +12532,7 @@ The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries besid
|
|
|
11893
12532
|
Welcome to **Blume** — markdown-first docs powered by Astro and Vite.
|
|
11894
12533
|
|
|
11895
12534
|
Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
|
|
11896
|
-
path:
|
|
12535
|
+
path: join33(dir, "index.mdx")
|
|
11897
12536
|
}
|
|
11898
12537
|
]
|
|
11899
12538
|
},
|
|
@@ -11904,11 +12543,11 @@ Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
|
|
|
11904
12543
|
content: page("Introduction", "Get started with the SDK.", `# Introduction
|
|
11905
12544
|
|
|
11906
12545
|
Install the SDK and make your first call. See [Installation](/installation).`),
|
|
11907
|
-
path:
|
|
12546
|
+
path: join33(dir, "index.mdx")
|
|
11908
12547
|
},
|
|
11909
12548
|
{
|
|
11910
12549
|
content: page("Installation", "Install the SDK.", "# Installation\n\n```package-install\nyour-sdk\n```"),
|
|
11911
|
-
path:
|
|
12550
|
+
path: join33(dir, "installation.mdx")
|
|
11912
12551
|
}
|
|
11913
12552
|
]
|
|
11914
12553
|
}
|
|
@@ -11918,12 +12557,12 @@ var commandsFor = (pm) => ({
|
|
|
11918
12557
|
install: `${pm} install`
|
|
11919
12558
|
});
|
|
11920
12559
|
var writeFileSafe = async (path, content) => {
|
|
11921
|
-
if (
|
|
12560
|
+
if (existsSync20(path)) {
|
|
11922
12561
|
logger.info(`Skipped existing ${path}`);
|
|
11923
12562
|
return false;
|
|
11924
12563
|
}
|
|
11925
12564
|
await mkdir6(dirname13(path), { recursive: true });
|
|
11926
|
-
await
|
|
12565
|
+
await writeFile10(path, content, "utf-8");
|
|
11927
12566
|
logger.success(`Created ${path}`);
|
|
11928
12567
|
return true;
|
|
11929
12568
|
};
|
|
@@ -11955,6 +12594,10 @@ var initCommand = defineCommand7({
|
|
|
11955
12594
|
async run({ args }) {
|
|
11956
12595
|
const root = process.cwd();
|
|
11957
12596
|
const contentDir = args["content-dir"] ?? "docs";
|
|
12597
|
+
if (isAbsolute9(contentDir) || relative18(root, join33(root, contentDir)).startsWith("..")) {
|
|
12598
|
+
logger.error(`Invalid --content-dir "${contentDir}" (must be a path inside the project).`);
|
|
12599
|
+
process.exit(1);
|
|
12600
|
+
}
|
|
11958
12601
|
const template = args.template ?? "docs";
|
|
11959
12602
|
if (!TEMPLATES.includes(template)) {
|
|
11960
12603
|
logger.error(`Unknown template "${args.template}" (use ${TEMPLATES.join(" | ")}).`);
|
|
@@ -11966,9 +12609,13 @@ var initCommand = defineCommand7({
|
|
|
11966
12609
|
process.exit(1);
|
|
11967
12610
|
}
|
|
11968
12611
|
const starter = STARTERS[template];
|
|
11969
|
-
const createdPackage = await writeFileSafe(
|
|
11970
|
-
await writeFileSafe(
|
|
11971
|
-
await Promise.all(starter.files(contentDir).map((file) => writeFileSafe(
|
|
12612
|
+
const createdPackage = await writeFileSafe(join33(root, "package.json"), packageTemplate(toPackageName(basename4(root)), getBlumeVersion()));
|
|
12613
|
+
await writeFileSafe(join33(root, "blume.config.ts"), starter.config);
|
|
12614
|
+
await Promise.all(starter.files(contentDir).map((file) => writeFileSafe(join33(root, file.path), file.content)));
|
|
12615
|
+
const ignored = await ensureGitignore(root, [".blume/", "dist/"]);
|
|
12616
|
+
if (ignored.length > 0) {
|
|
12617
|
+
logger.success(`Added ${ignored.join(", ")} to .gitignore`);
|
|
12618
|
+
}
|
|
11972
12619
|
const commands = commandsFor(pm);
|
|
11973
12620
|
if (args.eject) {
|
|
11974
12621
|
try {
|
|
@@ -12005,15 +12652,15 @@ var initCommand = defineCommand7({
|
|
|
12005
12652
|
import { defineCommand as defineCommand8 } from "citty";
|
|
12006
12653
|
|
|
12007
12654
|
// src/migrate/fumadocs/index.ts
|
|
12008
|
-
import { existsSync as
|
|
12009
|
-
import { mkdir as mkdir8, readFile as
|
|
12010
|
-
import { dirname as dirname16, join as
|
|
12655
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
12656
|
+
import { mkdir as mkdir8, readFile as readFile19, rename as rename3, rm as rm3, writeFile as writeFile12 } from "node:fs/promises";
|
|
12657
|
+
import { dirname as dirname16, join as join36, relative as relative19 } from "pathe";
|
|
12011
12658
|
import { glob as glob8 } from "tinyglobby";
|
|
12012
12659
|
|
|
12013
12660
|
// src/migrate/fumadocs/config.ts
|
|
12014
|
-
import { existsSync as
|
|
12015
|
-
import { readFile as
|
|
12016
|
-
import { basename as basename5, dirname as dirname14, join as
|
|
12661
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
12662
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
12663
|
+
import { basename as basename5, dirname as dirname14, join as join34 } from "pathe";
|
|
12017
12664
|
var SOURCE_FILES = [
|
|
12018
12665
|
"lib/source.ts",
|
|
12019
12666
|
"app/source.ts",
|
|
@@ -12043,7 +12690,7 @@ var GENERIC_NAMES = new Set([
|
|
|
12043
12690
|
var gitRepoRoot = (start) => {
|
|
12044
12691
|
let dir = start;
|
|
12045
12692
|
for (;; ) {
|
|
12046
|
-
if (
|
|
12693
|
+
if (existsSync21(join34(dir, ".git"))) {
|
|
12047
12694
|
return dir;
|
|
12048
12695
|
}
|
|
12049
12696
|
const parent = dirname14(dir);
|
|
@@ -12055,12 +12702,12 @@ var gitRepoRoot = (start) => {
|
|
|
12055
12702
|
};
|
|
12056
12703
|
var bareName = (name) => name.includes("/") ? name.slice(name.lastIndexOf("/") + 1) : name;
|
|
12057
12704
|
var readTitle = async (root) => {
|
|
12058
|
-
const packageJson =
|
|
12059
|
-
if (!
|
|
12705
|
+
const packageJson = join34(root, "package.json");
|
|
12706
|
+
if (!existsSync21(packageJson)) {
|
|
12060
12707
|
return "Documentation";
|
|
12061
12708
|
}
|
|
12062
12709
|
try {
|
|
12063
|
-
const parsed = JSON.parse(await
|
|
12710
|
+
const parsed = JSON.parse(await readFile18(packageJson, "utf-8"));
|
|
12064
12711
|
const { name } = parsed;
|
|
12065
12712
|
if (typeof name !== "string" || !name.trim()) {
|
|
12066
12713
|
return "Documentation";
|
|
@@ -12081,11 +12728,11 @@ var readTitle = async (root) => {
|
|
|
12081
12728
|
};
|
|
12082
12729
|
var scrapeBaseUrl = async (root) => {
|
|
12083
12730
|
for (const candidate of SOURCE_FILES) {
|
|
12084
|
-
const file =
|
|
12085
|
-
if (!
|
|
12731
|
+
const file = join34(root, candidate);
|
|
12732
|
+
if (!existsSync21(file)) {
|
|
12086
12733
|
continue;
|
|
12087
12734
|
}
|
|
12088
|
-
const base = BASE_URL.exec(await
|
|
12735
|
+
const base = BASE_URL.exec(await readFile18(file, "utf-8"))?.groups?.base;
|
|
12089
12736
|
if (base) {
|
|
12090
12737
|
return base;
|
|
12091
12738
|
}
|
|
@@ -12114,9 +12761,9 @@ var loadFumadocsConfig = async (root) => {
|
|
|
12114
12761
|
};
|
|
12115
12762
|
|
|
12116
12763
|
// src/migrate/fumadocs/content.ts
|
|
12117
|
-
import { existsSync as
|
|
12764
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
12118
12765
|
import { readFile as readFileFromDisk2 } from "node:fs/promises";
|
|
12119
|
-
import { dirname as dirname15, resolve as
|
|
12766
|
+
import { dirname as dirname15, resolve as resolve11 } from "pathe";
|
|
12120
12767
|
var FUMADOCS_IMPORT = /^import\s+[\s\S]*?\s+from\s+["']fumadocs-(?:ui|core|mdx)(?:\/[^"']*)?["'];?[ \t]*\n?/gmu;
|
|
12121
12768
|
var stripFumadocsImports = (source) => {
|
|
12122
12769
|
const stripped = source.replace(FUMADOCS_IMPORT, "");
|
|
@@ -12317,12 +12964,16 @@ var inlineFumadocsIncludes = async (source, options) => {
|
|
|
12317
12964
|
if (!rawPath) {
|
|
12318
12965
|
continue;
|
|
12319
12966
|
}
|
|
12320
|
-
const target =
|
|
12967
|
+
const target = resolve11(dirname15(options.filePath), rawPath);
|
|
12968
|
+
if (!isInsideRoot2(options.root, target)) {
|
|
12969
|
+
warnings.push(`<include> target "${rawPath}" is outside the docs tree — left as-is.`);
|
|
12970
|
+
continue;
|
|
12971
|
+
}
|
|
12321
12972
|
if (seen.has(target)) {
|
|
12322
12973
|
warnings.push(`Circular <include> "${rawPath}" — left as-is.`);
|
|
12323
12974
|
continue;
|
|
12324
12975
|
}
|
|
12325
|
-
if (!
|
|
12976
|
+
if (!existsSync22(target)) {
|
|
12326
12977
|
warnings.push(`<include> target "${rawPath}" not found — left as-is.`);
|
|
12327
12978
|
continue;
|
|
12328
12979
|
}
|
|
@@ -12354,9 +13005,9 @@ var normalizeFumadocsPageMeta = (value) => {
|
|
|
12354
13005
|
};
|
|
12355
13006
|
|
|
12356
13007
|
// src/migrate/fumadocs/groups.ts
|
|
12357
|
-
import { existsSync as
|
|
12358
|
-
import { mkdir as mkdir7, rename as rename2, writeFile as
|
|
12359
|
-
import { basename as basename6, join as
|
|
13008
|
+
import { existsSync as existsSync23, statSync as statSync2 } from "node:fs";
|
|
13009
|
+
import { mkdir as mkdir7, rename as rename2, writeFile as writeFile11 } from "node:fs/promises";
|
|
13010
|
+
import { basename as basename6, join as join35 } from "pathe";
|
|
12360
13011
|
|
|
12361
13012
|
// src/migrate/fumadocs/meta.ts
|
|
12362
13013
|
var SEPARATOR = /^---(?<label>.*)---$/u;
|
|
@@ -12501,13 +13152,16 @@ var isDirectory = (path) => {
|
|
|
12501
13152
|
}
|
|
12502
13153
|
};
|
|
12503
13154
|
var resolveEntry = (docsDir, name) => {
|
|
13155
|
+
if (!isInsideRoot2(docsDir, join35(docsDir, name))) {
|
|
13156
|
+
return null;
|
|
13157
|
+
}
|
|
12504
13158
|
for (const ext of PAGE_EXTS) {
|
|
12505
|
-
const file =
|
|
12506
|
-
if (
|
|
13159
|
+
const file = join35(docsDir, `${name}${ext}`);
|
|
13160
|
+
if (existsSync23(file)) {
|
|
12507
13161
|
return { kind: "file", path: file };
|
|
12508
13162
|
}
|
|
12509
13163
|
}
|
|
12510
|
-
const folder =
|
|
13164
|
+
const folder = join35(docsDir, name);
|
|
12511
13165
|
return isDirectory(folder) ? { kind: "folder", path: folder } : null;
|
|
12512
13166
|
};
|
|
12513
13167
|
var humanize2 = (name) => name.split(WORD_SPLIT3).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
@@ -12542,8 +13196,8 @@ var moveItemIntoGroup = async (item, docsDir, groupDir, label, warnings) => {
|
|
|
12542
13196
|
warnings.push(`Sidebar entry "${item.name}" in section "${label}" matched no page or folder; skipped.`);
|
|
12543
13197
|
return null;
|
|
12544
13198
|
}
|
|
12545
|
-
const dest =
|
|
12546
|
-
if (
|
|
13199
|
+
const dest = join35(groupDir, basename6(resolved.path));
|
|
13200
|
+
if (existsSync23(dest)) {
|
|
12547
13201
|
warnings.push(`Skipped moving "${item.name}" into section "${label}" (target already exists).`);
|
|
12548
13202
|
return null;
|
|
12549
13203
|
}
|
|
@@ -12570,7 +13224,7 @@ var reshapeSection = async (section, docsDir, order, warnings) => {
|
|
|
12570
13224
|
}
|
|
12571
13225
|
return;
|
|
12572
13226
|
}
|
|
12573
|
-
const groupDir =
|
|
13227
|
+
const groupDir = join35(docsDir, `(${section.label})`);
|
|
12574
13228
|
const sectionKeys = [];
|
|
12575
13229
|
for (const item of section.items) {
|
|
12576
13230
|
const key = await moveItemIntoGroup(item, docsDir, groupDir, section.label, warnings);
|
|
@@ -12583,7 +13237,7 @@ var reshapeSection = async (section, docsDir, order, warnings) => {
|
|
|
12583
13237
|
}
|
|
12584
13238
|
order.push(section.label);
|
|
12585
13239
|
if (sectionKeys.length > 1) {
|
|
12586
|
-
await
|
|
13240
|
+
await writeFile11(join35(groupDir, "meta.ts"), renderMetaModule({ pages: sectionKeys }), "utf-8");
|
|
12587
13241
|
}
|
|
12588
13242
|
};
|
|
12589
13243
|
var reshapeFumadocsGroups = async (structure, docsDir) => {
|
|
@@ -12615,9 +13269,9 @@ var FUMADOCS_LEFTOVERS = [
|
|
|
12615
13269
|
"app"
|
|
12616
13270
|
];
|
|
12617
13271
|
var movePage = async (abs, base, root) => {
|
|
12618
|
-
const rel =
|
|
12619
|
-
const dest =
|
|
12620
|
-
if (
|
|
13272
|
+
const rel = relative19(base, abs);
|
|
13273
|
+
const dest = join36(root, "docs", rel);
|
|
13274
|
+
if (existsSync24(dest)) {
|
|
12621
13275
|
return {
|
|
12622
13276
|
includeWarnings: [],
|
|
12623
13277
|
moved: 0,
|
|
@@ -12626,8 +13280,11 @@ var movePage = async (abs, base, root) => {
|
|
|
12626
13280
|
unsupported: []
|
|
12627
13281
|
};
|
|
12628
13282
|
}
|
|
12629
|
-
const raw = await
|
|
12630
|
-
const included = await inlineFumadocsIncludes(raw, {
|
|
13283
|
+
const raw = await readFile19(abs, "utf-8");
|
|
13284
|
+
const included = await inlineFumadocsIncludes(raw, {
|
|
13285
|
+
filePath: abs,
|
|
13286
|
+
root: base
|
|
13287
|
+
});
|
|
12631
13288
|
let text = stripFumadocsImports(included.content);
|
|
12632
13289
|
text = rewriteFumadocsCallouts(text);
|
|
12633
13290
|
text = rewriteFumadocsContainers(text);
|
|
@@ -12637,7 +13294,7 @@ var movePage = async (abs, base, root) => {
|
|
|
12637
13294
|
const { data, removed } = normalizeFumadocsPageMeta(parsed.data);
|
|
12638
13295
|
const content = Object.keys(data).length > 0 ? frontmatter_default.stringify(parsed.content, data) : parsed.content;
|
|
12639
13296
|
await mkdir8(dirname16(dest), { recursive: true });
|
|
12640
|
-
await
|
|
13297
|
+
await writeFile12(dest, content, "utf-8");
|
|
12641
13298
|
await rm3(abs, { force: true });
|
|
12642
13299
|
return {
|
|
12643
13300
|
includeWarnings: included.warnings,
|
|
@@ -12651,22 +13308,22 @@ var writeMeta = async (dest, meta, rel, warnings) => {
|
|
|
12651
13308
|
if (Object.keys(meta).length === 0) {
|
|
12652
13309
|
return warnings;
|
|
12653
13310
|
}
|
|
12654
|
-
if (
|
|
13311
|
+
if (existsSync24(dest)) {
|
|
12655
13312
|
return [...warnings, `Skipped ${rel} (target already exists)`];
|
|
12656
13313
|
}
|
|
12657
13314
|
await mkdir8(dirname16(dest), { recursive: true });
|
|
12658
|
-
await
|
|
13315
|
+
await writeFile12(dest, renderMetaModule(meta), "utf-8");
|
|
12659
13316
|
return warnings;
|
|
12660
13317
|
};
|
|
12661
13318
|
var convertMeta = async (abs, base, root) => {
|
|
12662
|
-
const rel =
|
|
12663
|
-
const raw = await
|
|
13319
|
+
const rel = relative19(base, abs);
|
|
13320
|
+
const raw = await readFile19(abs, "utf-8");
|
|
12664
13321
|
let parsed;
|
|
12665
13322
|
try {
|
|
12666
13323
|
parsed = JSON.parse(raw);
|
|
12667
13324
|
} catch {
|
|
12668
|
-
const dest2 =
|
|
12669
|
-
if (
|
|
13325
|
+
const dest2 = join36(root, "docs", rel);
|
|
13326
|
+
if (existsSync24(dest2)) {
|
|
12670
13327
|
return [`Skipped ${rel} (target already exists)`];
|
|
12671
13328
|
}
|
|
12672
13329
|
await mkdir8(dirname16(dest2), { recursive: true });
|
|
@@ -12676,10 +13333,10 @@ var convertMeta = async (abs, base, root) => {
|
|
|
12676
13333
|
];
|
|
12677
13334
|
}
|
|
12678
13335
|
const dir = dirname16(rel) === "." ? "" : dirname16(rel);
|
|
12679
|
-
const docsDir =
|
|
12680
|
-
const dest =
|
|
13336
|
+
const docsDir = join36(root, "docs", dir);
|
|
13337
|
+
const dest = join36(docsDir, "meta.ts");
|
|
12681
13338
|
const structure = parseFumadocsPages(parsed.pages);
|
|
12682
|
-
if (structure.hasSections && !
|
|
13339
|
+
if (structure.hasSections && !existsSync24(dest)) {
|
|
12683
13340
|
const self = translateFumadocsSelfMeta(parsed);
|
|
12684
13341
|
const reshape = await reshapeFumadocsGroups(structure, docsDir);
|
|
12685
13342
|
const meta2 = { ...self.meta };
|
|
@@ -12726,8 +13383,8 @@ var summarizePages = (results) => {
|
|
|
12726
13383
|
};
|
|
12727
13384
|
};
|
|
12728
13385
|
var cleanupSourceDirs = async (root) => {
|
|
12729
|
-
const docs =
|
|
12730
|
-
if (
|
|
13386
|
+
const docs = join36(root, "content", "docs");
|
|
13387
|
+
if (existsSync24(docs)) {
|
|
12731
13388
|
const remaining = await glob8(["**/*"], { cwd: docs, dot: true });
|
|
12732
13389
|
if (remaining.length > 0) {
|
|
12733
13390
|
return [
|
|
@@ -12736,8 +13393,8 @@ var cleanupSourceDirs = async (root) => {
|
|
|
12736
13393
|
}
|
|
12737
13394
|
await rm3(docs, { force: true, recursive: true });
|
|
12738
13395
|
}
|
|
12739
|
-
const content =
|
|
12740
|
-
if (
|
|
13396
|
+
const content = join36(root, "content");
|
|
13397
|
+
if (existsSync24(content)) {
|
|
12741
13398
|
const remaining = await glob8(["**/*"], { cwd: content, dot: true });
|
|
12742
13399
|
if (remaining.length === 0) {
|
|
12743
13400
|
await rm3(content, { force: true, recursive: true });
|
|
@@ -12747,8 +13404,8 @@ var cleanupSourceDirs = async (root) => {
|
|
|
12747
13404
|
};
|
|
12748
13405
|
var migrateFumadocsProject = async (root) => {
|
|
12749
13406
|
const { config, warnings: configWarnings } = await loadFumadocsConfig(root);
|
|
12750
|
-
const base =
|
|
12751
|
-
if (!
|
|
13407
|
+
const base = join36(root, SOURCE_DIR);
|
|
13408
|
+
if (!existsSync24(base)) {
|
|
12752
13409
|
await writeBlumeConfig(root, config);
|
|
12753
13410
|
return {
|
|
12754
13411
|
moved: 0,
|
|
@@ -12806,10 +13463,33 @@ var migrateFumadocsProject = async (root) => {
|
|
|
12806
13463
|
};
|
|
12807
13464
|
|
|
12808
13465
|
// src/migrate/mintlify/index.ts
|
|
12809
|
-
import { existsSync as
|
|
12810
|
-
import { mkdir as mkdir9, readFile as
|
|
12811
|
-
import { dirname as dirname17, join as
|
|
13466
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
13467
|
+
import { mkdir as mkdir9, readFile as readFile20, rename as rename4, rm as rm4, stat as stat2, writeFile as writeFile13 } from "node:fs/promises";
|
|
13468
|
+
import { dirname as dirname17, join as join37 } from "pathe";
|
|
12812
13469
|
import { glob as glob9 } from "tinyglobby";
|
|
13470
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
13471
|
+
var hasFontFamily = (value) => {
|
|
13472
|
+
const object = asRecord(value);
|
|
13473
|
+
if (!object) {
|
|
13474
|
+
return false;
|
|
13475
|
+
}
|
|
13476
|
+
const named = (child) => typeof asRecord(child)?.family === "string";
|
|
13477
|
+
return typeof object.family === "string" || named(object.heading) || named(object.body);
|
|
13478
|
+
};
|
|
13479
|
+
var droppedChromeWarnings = (spec, config) => {
|
|
13480
|
+
const warnings = [];
|
|
13481
|
+
const navbar = asRecord(spec.navbar);
|
|
13482
|
+
if (navbar && (navbar.links || navbar.primary)) {
|
|
13483
|
+
warnings.push("Header links (navbar.links/navbar.primary) have no blume.config equivalent and were dropped; re-add them with navigation.tabs or a Header layout override.");
|
|
13484
|
+
}
|
|
13485
|
+
if (asRecord(spec.footer)?.socials) {
|
|
13486
|
+
warnings.push("Footer social links (footer.socials) have no blume.config equivalent and were dropped; add them with a Footer layout override.");
|
|
13487
|
+
}
|
|
13488
|
+
if (hasFontFamily(spec.fonts ?? spec.font) && !config.theme?.fonts) {
|
|
13489
|
+
warnings.push("docs.json font family isn't in Blume's curated Google Fonts set; set theme.fonts to a supported slug or add @font-face rules in theme.css.");
|
|
13490
|
+
}
|
|
13491
|
+
return warnings;
|
|
13492
|
+
};
|
|
12813
13493
|
var prune = (value) => {
|
|
12814
13494
|
if (Array.isArray(value)) {
|
|
12815
13495
|
return value.map(prune);
|
|
@@ -12838,38 +13518,48 @@ var writeBlumeConfig2 = async (root, config) => {
|
|
|
12838
13518
|
|
|
12839
13519
|
export default defineConfig(${JSON.stringify(prune(config), null, 2)});
|
|
12840
13520
|
`;
|
|
12841
|
-
await
|
|
13521
|
+
await writeFile13(join37(root, "blume.config.ts"), body, "utf-8");
|
|
12842
13522
|
};
|
|
12843
|
-
var relocateAssets = async (root,
|
|
12844
|
-
const
|
|
12845
|
-
for (const ref of refs) {
|
|
12846
|
-
if (typeof ref !== "string" || !ref.startsWith("/")) {
|
|
12847
|
-
continue;
|
|
12848
|
-
}
|
|
12849
|
-
const [segment] = ref.replace(/^\/+/u, "").split("/");
|
|
12850
|
-
if (segment) {
|
|
12851
|
-
segments.add(segment);
|
|
12852
|
-
}
|
|
12853
|
-
}
|
|
13523
|
+
var relocateAssets = async (root, segments) => {
|
|
13524
|
+
const served = [];
|
|
12854
13525
|
const moved = [];
|
|
12855
13526
|
for (const segment of segments) {
|
|
12856
|
-
const source =
|
|
12857
|
-
if (!
|
|
13527
|
+
const source = join37(root, segment);
|
|
13528
|
+
if (!existsSync25(source) || segment === "public") {
|
|
12858
13529
|
continue;
|
|
12859
13530
|
}
|
|
12860
|
-
const
|
|
12861
|
-
if (
|
|
13531
|
+
const stats = await stat2(source);
|
|
13532
|
+
if (stats.isDirectory()) {
|
|
13533
|
+
served.push(segment);
|
|
12862
13534
|
continue;
|
|
12863
13535
|
}
|
|
12864
|
-
|
|
13536
|
+
const dest = join37(root, "public", segment);
|
|
13537
|
+
if (existsSync25(dest)) {
|
|
13538
|
+
continue;
|
|
13539
|
+
}
|
|
13540
|
+
await mkdir9(join37(root, "public"), { recursive: true });
|
|
12865
13541
|
await rename4(source, dest);
|
|
12866
13542
|
moved.push(segment);
|
|
12867
13543
|
}
|
|
12868
|
-
return moved;
|
|
13544
|
+
return { moved, served };
|
|
13545
|
+
};
|
|
13546
|
+
var applyRelocatedAssets = (config, assets, warnings) => {
|
|
13547
|
+
if (assets.served.length > 0) {
|
|
13548
|
+
config.content = {
|
|
13549
|
+
...config.content,
|
|
13550
|
+
assets: [
|
|
13551
|
+
...new Set([...config.content?.assets ?? [], ...assets.served])
|
|
13552
|
+
]
|
|
13553
|
+
};
|
|
13554
|
+
warnings.push(`Kept asset dir(s) in place, served via content.assets: ${assets.served.join(", ")}.`);
|
|
13555
|
+
}
|
|
13556
|
+
if (assets.moved.length > 0) {
|
|
13557
|
+
warnings.push(`Moved assets into public/: ${assets.moved.join(", ")}.`);
|
|
13558
|
+
}
|
|
12869
13559
|
};
|
|
12870
13560
|
var cleanupSnippets = async (root, kept, warnings) => {
|
|
12871
|
-
const dir =
|
|
12872
|
-
if (!
|
|
13561
|
+
const dir = join37(root, "snippets");
|
|
13562
|
+
if (!existsSync25(dir)) {
|
|
12873
13563
|
return;
|
|
12874
13564
|
}
|
|
12875
13565
|
const markdown = await glob9(["**/*.{md,mdx}"], { absolute: true, cwd: dir });
|
|
@@ -12887,30 +13577,13 @@ var cleanupSnippets = async (root, kept, warnings) => {
|
|
|
12887
13577
|
warnings.push(`Rewrote ${kept.size} component snippet import(s) to relative paths; verify they resolve.`);
|
|
12888
13578
|
}
|
|
12889
13579
|
};
|
|
12890
|
-
var assetRefs = (config) => {
|
|
12891
|
-
const refs = ["/images"];
|
|
12892
|
-
const logo = config.logo;
|
|
12893
|
-
if (typeof logo === "string") {
|
|
12894
|
-
refs.push(logo);
|
|
12895
|
-
} else if (logo) {
|
|
12896
|
-
refs.push(logo.light, logo.dark);
|
|
12897
|
-
}
|
|
12898
|
-
const favicon = config.favicon;
|
|
12899
|
-
if (typeof favicon === "string") {
|
|
12900
|
-
refs.push(favicon);
|
|
12901
|
-
} else if (favicon) {
|
|
12902
|
-
refs.push(favicon.light, favicon.dark);
|
|
12903
|
-
}
|
|
12904
|
-
refs.push(config.theme?.backgroundImage, config.theme?.backgroundImageDark);
|
|
12905
|
-
return refs;
|
|
12906
|
-
};
|
|
12907
13580
|
var migrateMintlifyProject = async (root) => {
|
|
12908
13581
|
const warnings = [];
|
|
12909
|
-
const configFile =
|
|
13582
|
+
const configFile = existsSync25(join37(root, "docs.json")) ? join37(root, "docs.json") : join37(root, "mint.json");
|
|
12910
13583
|
let config;
|
|
12911
|
-
if (
|
|
13584
|
+
if (existsSync25(configFile)) {
|
|
12912
13585
|
config = await loadMintlifyConfig(root, configFile);
|
|
12913
|
-
const spec = JSON.parse(await
|
|
13586
|
+
const spec = JSON.parse(await readFile20(configFile, "utf-8"));
|
|
12914
13587
|
const i18n = mintlifyI18n(spec);
|
|
12915
13588
|
if (i18n) {
|
|
12916
13589
|
config.i18n = i18n;
|
|
@@ -12919,6 +13592,11 @@ var migrateMintlifyProject = async (root) => {
|
|
|
12919
13592
|
}
|
|
12920
13593
|
warnings.push(`Mapped ${i18n.locales.length} languages to i18n.locales (default: ${i18n.defaultLocale}); review the locale labels.`);
|
|
12921
13594
|
}
|
|
13595
|
+
const openapiSources = config.openapi?.sources ?? [];
|
|
13596
|
+
if (openapiSources.length > 0) {
|
|
13597
|
+
warnings.push(`Mapped ${openapiSources.length} OpenAPI spec source(s) to openapi.sources (native reference renderer); verify each spec path or URL resolves.`);
|
|
13598
|
+
}
|
|
13599
|
+
warnings.push(...droppedChromeWarnings(spec, config));
|
|
12922
13600
|
} else {
|
|
12923
13601
|
warnings.push("No docs.json or mint.json found; writing a default config.");
|
|
12924
13602
|
config = { content: { root: "." }, title: "Documentation" };
|
|
@@ -12941,7 +13619,7 @@ var migrateMintlifyProject = async (root) => {
|
|
|
12941
13619
|
const unsupported = new Set;
|
|
12942
13620
|
const keptComponents = new Set;
|
|
12943
13621
|
for (const file of files) {
|
|
12944
|
-
const raw = await
|
|
13622
|
+
const raw = await readFile20(file, "utf-8");
|
|
12945
13623
|
const result = await transformMintlifyContent(raw, {
|
|
12946
13624
|
filePath: file,
|
|
12947
13625
|
root,
|
|
@@ -12949,7 +13627,7 @@ var migrateMintlifyProject = async (root) => {
|
|
|
12949
13627
|
});
|
|
12950
13628
|
if (result.content !== raw) {
|
|
12951
13629
|
await mkdir9(dirname17(file), { recursive: true });
|
|
12952
|
-
await
|
|
13630
|
+
await writeFile13(file, result.content, "utf-8");
|
|
12953
13631
|
}
|
|
12954
13632
|
for (const key of result.removed) {
|
|
12955
13633
|
removedKeys.add(key);
|
|
@@ -12962,32 +13640,30 @@ var migrateMintlifyProject = async (root) => {
|
|
|
12962
13640
|
}
|
|
12963
13641
|
moved += 1;
|
|
12964
13642
|
}
|
|
12965
|
-
const
|
|
13643
|
+
const assets = await relocateAssets(root, assetSegments(config));
|
|
12966
13644
|
await cleanupSnippets(root, keptComponents, warnings);
|
|
12967
13645
|
if (config.content?.exclude) {
|
|
12968
13646
|
config.content.exclude = [...new Set(config.content.exclude)];
|
|
12969
13647
|
}
|
|
13648
|
+
applyRelocatedAssets(config, assets, warnings);
|
|
12970
13649
|
await writeBlumeConfig2(root, config);
|
|
12971
13650
|
if (Object.keys(variables).length > 0) {
|
|
12972
13651
|
warnings.push(`Inlined ${Object.keys(variables).length} docs.json variable(s) into content; Blume has no runtime variable substitution.`);
|
|
12973
13652
|
}
|
|
12974
|
-
if (movedAssets.length > 0) {
|
|
12975
|
-
warnings.push(`Moved assets into public/: ${movedAssets.join(", ")}.`);
|
|
12976
|
-
}
|
|
12977
13653
|
if (removedKeys.size > 0) {
|
|
12978
13654
|
warnings.push(`Dropped unsupported page frontmatter keys: ${[...removedKeys].join(", ")}.`);
|
|
12979
13655
|
}
|
|
12980
13656
|
if (unsupported.size > 0) {
|
|
12981
|
-
warnings.push(`Components without a Blume equivalent need manual review
|
|
13657
|
+
warnings.push(`Components without a Blume equivalent need manual review: ${[...unsupported].join(", ")}.`);
|
|
12982
13658
|
}
|
|
12983
13659
|
warnings.push("Review blume.config.ts; navigation, theme, and chrome were mapped from docs.json.");
|
|
12984
13660
|
return { moved, warnings };
|
|
12985
13661
|
};
|
|
12986
13662
|
|
|
12987
13663
|
// src/migrate/nextra/index.ts
|
|
12988
|
-
import { existsSync as
|
|
12989
|
-
import { mkdir as mkdir10, readFile as
|
|
12990
|
-
import { basename as basename7, dirname as dirname18, extname as
|
|
13664
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
13665
|
+
import { mkdir as mkdir10, readFile as readFile21, rename as rename5, rm as rm5, writeFile as writeFile14 } from "node:fs/promises";
|
|
13666
|
+
import { basename as basename7, dirname as dirname18, extname as extname8, join as join38, relative as relative20 } from "pathe";
|
|
12991
13667
|
import { glob as glob10 } from "tinyglobby";
|
|
12992
13668
|
|
|
12993
13669
|
// src/migrate/nextra/content.ts
|
|
@@ -13222,7 +13898,7 @@ var indexFolders = (base, pageFiles, metaFiles) => {
|
|
|
13222
13898
|
}
|
|
13223
13899
|
};
|
|
13224
13900
|
for (const abs of pageFiles) {
|
|
13225
|
-
const rel =
|
|
13901
|
+
const rel = relative20(base, abs);
|
|
13226
13902
|
const dir = normalizeDir(dirname18(rel));
|
|
13227
13903
|
const slug = basename7(rel).replace(/\.mdx?$/u, "");
|
|
13228
13904
|
const folder = pagesByDir.get(dir) ?? new Map;
|
|
@@ -13231,7 +13907,7 @@ var indexFolders = (base, pageFiles, metaFiles) => {
|
|
|
13231
13907
|
registerDir(dir);
|
|
13232
13908
|
}
|
|
13233
13909
|
for (const abs of metaFiles) {
|
|
13234
|
-
registerDir(normalizeDir(dirname18(
|
|
13910
|
+
registerDir(normalizeDir(dirname18(relative20(base, abs))));
|
|
13235
13911
|
}
|
|
13236
13912
|
const childDirs = new Map;
|
|
13237
13913
|
for (const dir of allDirs) {
|
|
@@ -13269,7 +13945,7 @@ var planMetas = (metas, index) => {
|
|
|
13269
13945
|
plan.metaByDir.set(dir, conversion.folderMeta);
|
|
13270
13946
|
plan.consumedMetas.push(meta.abs);
|
|
13271
13947
|
for (const [slug, title] of Object.entries(conversion.folderTitles)) {
|
|
13272
|
-
plan.folderTitleByDir.set(normalizeDir(
|
|
13948
|
+
plan.folderTitleByDir.set(normalizeDir(join38(dir, slug)), title);
|
|
13273
13949
|
}
|
|
13274
13950
|
for (const [slug, label] of Object.entries(conversion.pageLabels)) {
|
|
13275
13951
|
const pageAbs = index.pagesByDir.get(dir)?.get(slug);
|
|
@@ -13293,19 +13969,19 @@ var planMetas = (metas, index) => {
|
|
|
13293
13969
|
return plan;
|
|
13294
13970
|
};
|
|
13295
13971
|
var movePage2 = async (abs, options) => {
|
|
13296
|
-
const rel =
|
|
13297
|
-
const dest =
|
|
13298
|
-
if (
|
|
13972
|
+
const rel = relative20(options.base, abs);
|
|
13973
|
+
const dest = join38(options.root, "docs", rel);
|
|
13974
|
+
if (existsSync26(dest)) {
|
|
13299
13975
|
return { moved: 0, removed: [], skipped: rel, unsupported: [] };
|
|
13300
13976
|
}
|
|
13301
|
-
const raw = await
|
|
13977
|
+
const raw = await readFile21(abs, "utf-8");
|
|
13302
13978
|
const text = rewriteNextraCallouts(stripNextraImports(raw));
|
|
13303
13979
|
const unsupported = unsupportedNextraComponents(text);
|
|
13304
13980
|
const parsed = frontmatter_default(text);
|
|
13305
13981
|
const { data, removed } = normalizeNextraPageMeta(parsed.data, options.overrides.get(abs) ?? {});
|
|
13306
13982
|
const content = Object.keys(data).length > 0 ? frontmatter_default.stringify(parsed.content, data) : parsed.content;
|
|
13307
13983
|
await mkdir10(dirname18(dest), { recursive: true });
|
|
13308
|
-
await
|
|
13984
|
+
await writeFile14(dest, content, "utf-8");
|
|
13309
13985
|
await rm5(abs, { force: true });
|
|
13310
13986
|
return { moved: 1, removed, skipped: null, unsupported };
|
|
13311
13987
|
};
|
|
@@ -13347,9 +14023,9 @@ var writeFolderMetas = async (root, plan) => {
|
|
|
13347
14023
|
if (Object.keys(finalMeta).length === 0) {
|
|
13348
14024
|
return;
|
|
13349
14025
|
}
|
|
13350
|
-
const dest =
|
|
14026
|
+
const dest = join38(root, "docs", dir, "meta.ts");
|
|
13351
14027
|
await mkdir10(dirname18(dest), { recursive: true });
|
|
13352
|
-
await
|
|
14028
|
+
await writeFile14(dest, `import { defineMeta } from "blume";
|
|
13353
14029
|
|
|
13354
14030
|
export default defineMeta(${JSON.stringify(finalMeta, null, 2)});
|
|
13355
14031
|
`, "utf-8");
|
|
@@ -13358,8 +14034,8 @@ export default defineMeta(${JSON.stringify(finalMeta, null, 2)});
|
|
|
13358
14034
|
var relocateUnparseableMetas = async (root, metas) => {
|
|
13359
14035
|
const warnings = [];
|
|
13360
14036
|
await Promise.all(metas.map(async ({ abs, rel }) => {
|
|
13361
|
-
const dest =
|
|
13362
|
-
if (
|
|
14037
|
+
const dest = join38(root, "docs", rel);
|
|
14038
|
+
if (existsSync26(dest)) {
|
|
13363
14039
|
warnings.push(`Skipped ${rel} (target already exists)`);
|
|
13364
14040
|
return;
|
|
13365
14041
|
}
|
|
@@ -13371,7 +14047,7 @@ var relocateUnparseableMetas = async (root, metas) => {
|
|
|
13371
14047
|
};
|
|
13372
14048
|
var buildConfig = (tabs) => tabs.length > 0 ? { navigation: { tabs }, title: "Documentation" } : { title: "Documentation" };
|
|
13373
14049
|
var migrateNextraProject = async (root) => {
|
|
13374
|
-
const sourceDir = SOURCE_DIRS.find((dir) =>
|
|
14050
|
+
const sourceDir = SOURCE_DIRS.find((dir) => existsSync26(join38(root, dir)));
|
|
13375
14051
|
if (!sourceDir) {
|
|
13376
14052
|
await writeBlumeConfig(root, { title: "Documentation" });
|
|
13377
14053
|
return {
|
|
@@ -13381,7 +14057,7 @@ var migrateNextraProject = async (root) => {
|
|
|
13381
14057
|
]
|
|
13382
14058
|
};
|
|
13383
14059
|
}
|
|
13384
|
-
const base =
|
|
14060
|
+
const base = join38(root, sourceDir);
|
|
13385
14061
|
const pageFiles = await glob10([PAGE_GLOB2], {
|
|
13386
14062
|
absolute: true,
|
|
13387
14063
|
cwd: base,
|
|
@@ -13395,9 +14071,9 @@ var migrateNextraProject = async (root) => {
|
|
|
13395
14071
|
const index = indexFolders(base, pageFiles, metaFiles);
|
|
13396
14072
|
const metas = await Promise.all(metaFiles.map(async (abs) => ({
|
|
13397
14073
|
abs,
|
|
13398
|
-
ext:
|
|
13399
|
-
raw: await
|
|
13400
|
-
rel:
|
|
14074
|
+
ext: extname8(abs),
|
|
14075
|
+
raw: await readFile21(abs, "utf-8"),
|
|
14076
|
+
rel: relative20(base, abs)
|
|
13401
14077
|
})));
|
|
13402
14078
|
const plan = planMetas(metas, index);
|
|
13403
14079
|
const moves = await Promise.all(pageFiles.map((abs) => movePage2(abs, { base, overrides: plan.pageOverrides, root })));
|
|
@@ -13422,15 +14098,15 @@ var migrateNextraProject = async (root) => {
|
|
|
13422
14098
|
};
|
|
13423
14099
|
|
|
13424
14100
|
// src/migrate/starlight/index.ts
|
|
13425
|
-
import { existsSync as
|
|
13426
|
-
import { readFile as
|
|
13427
|
-
import { join as
|
|
14101
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
14102
|
+
import { readFile as readFile23, writeFile as writeFile15 } from "node:fs/promises";
|
|
14103
|
+
import { join as join40 } from "pathe";
|
|
13428
14104
|
import { glob as glob11 } from "tinyglobby";
|
|
13429
14105
|
|
|
13430
14106
|
// src/migrate/starlight/config.ts
|
|
13431
|
-
import { existsSync as
|
|
13432
|
-
import { readFile as
|
|
13433
|
-
import { join as
|
|
14107
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
14108
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
14109
|
+
import { join as join39 } from "pathe";
|
|
13434
14110
|
var CONFIG_FILES = [
|
|
13435
14111
|
"astro.config.mjs",
|
|
13436
14112
|
"astro.config.mts",
|
|
@@ -13461,7 +14137,7 @@ var extractStarlightOptions = (source) => {
|
|
|
13461
14137
|
return isLiteralObject(parsed) ? parsed : "unparseable";
|
|
13462
14138
|
};
|
|
13463
14139
|
var loadStarlightConfig = async (root) => {
|
|
13464
|
-
const file = CONFIG_FILES.map((name) =>
|
|
14140
|
+
const file = CONFIG_FILES.map((name) => join39(root, name)).find((path) => existsSync27(path));
|
|
13465
14141
|
if (!file) {
|
|
13466
14142
|
return {
|
|
13467
14143
|
options: {},
|
|
@@ -13470,7 +14146,7 @@ var loadStarlightConfig = async (root) => {
|
|
|
13470
14146
|
]
|
|
13471
14147
|
};
|
|
13472
14148
|
}
|
|
13473
|
-
const source = await
|
|
14149
|
+
const source = await readFile22(file, "utf-8");
|
|
13474
14150
|
const result = extractStarlightOptions(source);
|
|
13475
14151
|
if (result === "missing") {
|
|
13476
14152
|
return {
|
|
@@ -13868,7 +14544,7 @@ var starlightI18n = (options) => {
|
|
|
13868
14544
|
// src/migrate/starlight/index.ts
|
|
13869
14545
|
var CONTENT_DIR = "src/content/docs";
|
|
13870
14546
|
var transformPage = async (file) => {
|
|
13871
|
-
const raw = await
|
|
14547
|
+
const raw = await readFile23(file, "utf-8");
|
|
13872
14548
|
let text = stripStarlightImports(raw);
|
|
13873
14549
|
text = rewriteStarlightAsides(text);
|
|
13874
14550
|
text = rewriteStarlightComponents(text);
|
|
@@ -13878,7 +14554,7 @@ var transformPage = async (file) => {
|
|
|
13878
14554
|
const { data, removed } = normalizeStarlightPageMeta(parsed.data);
|
|
13879
14555
|
const content = Object.keys(data).length > 0 ? frontmatter_default.stringify(parsed.content, data) : parsed.content;
|
|
13880
14556
|
if (content !== raw) {
|
|
13881
|
-
await
|
|
14557
|
+
await writeFile15(file, content, "utf-8");
|
|
13882
14558
|
}
|
|
13883
14559
|
return { aliased, removed, unsupported };
|
|
13884
14560
|
};
|
|
@@ -13890,8 +14566,8 @@ var migrateStarlightProject = async (root) => {
|
|
|
13890
14566
|
config.i18n = i18n;
|
|
13891
14567
|
warnings.push(`Mapped ${i18n.locales.length} locale(s) to i18n (default: ${i18n.defaultLocale}); review the locale labels.`);
|
|
13892
14568
|
}
|
|
13893
|
-
const base =
|
|
13894
|
-
if (!
|
|
14569
|
+
const base = join40(root, CONTENT_DIR);
|
|
14570
|
+
if (!existsSync28(base)) {
|
|
13895
14571
|
await writeBlumeConfig(root, config);
|
|
13896
14572
|
return {
|
|
13897
14573
|
moved: 0,
|
|
@@ -13979,10 +14655,10 @@ var migrateCommand = defineCommand8({
|
|
|
13979
14655
|
});
|
|
13980
14656
|
|
|
13981
14657
|
// src/cli/commands/preview.ts
|
|
13982
|
-
import { existsSync as
|
|
14658
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
13983
14659
|
import { preview } from "astro";
|
|
13984
14660
|
import { defineCommand as defineCommand9 } from "citty";
|
|
13985
|
-
import { join as
|
|
14661
|
+
import { join as join41 } from "pathe";
|
|
13986
14662
|
var previewCommand = defineCommand9({
|
|
13987
14663
|
args: {
|
|
13988
14664
|
host: { description: "Network host to bind.", type: "string" },
|
|
@@ -13996,7 +14672,7 @@ var previewCommand = defineCommand9({
|
|
|
13996
14672
|
const root = process.cwd();
|
|
13997
14673
|
const { config } = await loadConfig(root);
|
|
13998
14674
|
const context = resolveProjectContext(root, config);
|
|
13999
|
-
if (!
|
|
14675
|
+
if (!existsSync29(join41(context.outDir, "astro.config.mjs"))) {
|
|
14000
14676
|
logger.error("No build found. Run `blume build` first.");
|
|
14001
14677
|
process.exit(1);
|
|
14002
14678
|
}
|
|
@@ -14005,7 +14681,7 @@ var previewCommand = defineCommand9({
|
|
|
14005
14681
|
root: context.outDir,
|
|
14006
14682
|
server: {
|
|
14007
14683
|
host: args.host ?? false,
|
|
14008
|
-
port:
|
|
14684
|
+
port: parsePort(args.port)
|
|
14009
14685
|
}
|
|
14010
14686
|
});
|
|
14011
14687
|
}
|
|
@@ -14014,7 +14690,7 @@ var previewCommand = defineCommand9({
|
|
|
14014
14690
|
// src/cli/commands/sync.ts
|
|
14015
14691
|
import { rm as rm6 } from "node:fs/promises";
|
|
14016
14692
|
import { defineCommand as defineCommand10 } from "citty";
|
|
14017
|
-
import { join as
|
|
14693
|
+
import { join as join42 } from "pathe";
|
|
14018
14694
|
var syncCommand = defineCommand10({
|
|
14019
14695
|
args: {
|
|
14020
14696
|
force: {
|
|
@@ -14036,7 +14712,7 @@ var syncCommand = defineCommand10({
|
|
|
14036
14712
|
if (args.force) {
|
|
14037
14713
|
const { config } = await loadConfig(root);
|
|
14038
14714
|
const context = resolveProjectContext(root, config);
|
|
14039
|
-
await rm6(
|
|
14715
|
+
await rm6(join42(context.outDir, "cache"), { force: true, recursive: true });
|
|
14040
14716
|
logger.info("Cleared source cache.");
|
|
14041
14717
|
}
|
|
14042
14718
|
await prepareProject({
|
|
@@ -14051,13 +14727,13 @@ var syncCommand = defineCommand10({
|
|
|
14051
14727
|
});
|
|
14052
14728
|
|
|
14053
14729
|
// src/cli/commands/validate.ts
|
|
14054
|
-
import { existsSync as
|
|
14730
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
14055
14731
|
import { defineCommand as defineCommand11 } from "citty";
|
|
14056
|
-
import { join as
|
|
14732
|
+
import { join as join44 } from "pathe";
|
|
14057
14733
|
|
|
14058
14734
|
// src/core/links.ts
|
|
14059
|
-
import { existsSync as
|
|
14060
|
-
import { join as
|
|
14735
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
14736
|
+
import { basename as basename8, join as join43 } from "pathe";
|
|
14061
14737
|
var HTTP = /^https?:\/\//iu;
|
|
14062
14738
|
var PROTOCOL_RELATIVE = /^\/\//u;
|
|
14063
14739
|
var SCHEME = /^[a-z][a-z0-9+.-]*:/iu;
|
|
@@ -14069,9 +14745,21 @@ var STATUS_NOT_FOUND = 404;
|
|
|
14069
14745
|
var STATUS_GONE = 410;
|
|
14070
14746
|
var STATUS_METHOD_NOT_ALLOWED = 405;
|
|
14071
14747
|
var STATUS_NOT_IMPLEMENTED = 501;
|
|
14072
|
-
var
|
|
14748
|
+
var assetIsPresent = (resolved, ctx) => {
|
|
14749
|
+
if (ctx.publicDir && existsSync30(join43(ctx.publicDir, resolved))) {
|
|
14750
|
+
return true;
|
|
14751
|
+
}
|
|
14752
|
+
return ctx.assetMounts.some((mount) => (resolved === mount.url || resolved.startsWith(`${mount.url}/`)) && existsSync30(join43(mount.dir, resolved.slice(mount.url.length))));
|
|
14753
|
+
};
|
|
14754
|
+
var isIndexPage = (page2) => {
|
|
14755
|
+
const ref = page2.source?.ref ?? page2.sourcePath ?? "";
|
|
14756
|
+
return /^index\.(?:md|mdx)$/iu.test(basename8(ref));
|
|
14757
|
+
};
|
|
14758
|
+
var resolveRelative = (pageRoute, target, isIndex) => {
|
|
14073
14759
|
const segments = pageRoute.split("/").filter(Boolean);
|
|
14074
|
-
|
|
14760
|
+
if (!isIndex) {
|
|
14761
|
+
segments.pop();
|
|
14762
|
+
}
|
|
14075
14763
|
for (const part of target.split("/")) {
|
|
14076
14764
|
if (part === "" || part === ".") {
|
|
14077
14765
|
continue;
|
|
@@ -14113,13 +14801,20 @@ var checkAnchor = (route, fragment, site, ctx) => {
|
|
|
14113
14801
|
};
|
|
14114
14802
|
};
|
|
14115
14803
|
var checkPathLink = (resolved, fragment, target, site, ctx) => {
|
|
14804
|
+
const route = toRoute(resolved);
|
|
14805
|
+
if (ctx.routes.has(route)) {
|
|
14806
|
+
return fragment ? checkAnchor(route, fragment, site, ctx) : null;
|
|
14807
|
+
}
|
|
14808
|
+
if (ctx.redirects.has(route)) {
|
|
14809
|
+
return null;
|
|
14810
|
+
}
|
|
14116
14811
|
if (FILE_EXT.test(resolved) && !DOC_EXT.test(resolved)) {
|
|
14117
|
-
if (ctx
|
|
14118
|
-
return "asset-unchecked";
|
|
14119
|
-
}
|
|
14120
|
-
if (existsSync28(join38(ctx.publicDir, resolved))) {
|
|
14812
|
+
if (assetIsPresent(resolved, ctx)) {
|
|
14121
14813
|
return null;
|
|
14122
14814
|
}
|
|
14815
|
+
if (ctx.publicDir === null && ctx.assetMounts.length === 0) {
|
|
14816
|
+
return "asset-unchecked";
|
|
14817
|
+
}
|
|
14123
14818
|
return {
|
|
14124
14819
|
...site,
|
|
14125
14820
|
code: "BLUME_BROKEN_ASSET",
|
|
@@ -14128,13 +14823,6 @@ var checkPathLink = (resolved, fragment, target, site, ctx) => {
|
|
|
14128
14823
|
suggestion: `Add the file at public${resolved} or fix the link.`
|
|
14129
14824
|
};
|
|
14130
14825
|
}
|
|
14131
|
-
const route = toRoute(resolved);
|
|
14132
|
-
if (ctx.routes.has(route)) {
|
|
14133
|
-
return fragment ? checkAnchor(route, fragment, site, ctx) : null;
|
|
14134
|
-
}
|
|
14135
|
-
if (ctx.redirects.has(route)) {
|
|
14136
|
-
return null;
|
|
14137
|
-
}
|
|
14138
14826
|
return {
|
|
14139
14827
|
...site,
|
|
14140
14828
|
code: "BLUME_BROKEN_LINK",
|
|
@@ -14243,12 +14931,13 @@ var classifyLink = (page2, link, ctx, onExternal) => {
|
|
|
14243
14931
|
if (rawPath === "") {
|
|
14244
14932
|
return fragment ? checkAnchor(page2.route, fragment, site, ctx) : null;
|
|
14245
14933
|
}
|
|
14246
|
-
const resolved = rawPath.startsWith("/") ? rawPath : resolveRelative(page2.route, rawPath);
|
|
14934
|
+
const resolved = rawPath.startsWith("/") ? rawPath : resolveRelative(page2.route, rawPath, isIndexPage(page2));
|
|
14247
14935
|
return checkPathLink(resolved, fragment, target, site, ctx);
|
|
14248
14936
|
};
|
|
14249
14937
|
var validateLinks = async (graph, options) => {
|
|
14250
14938
|
const ctx = {
|
|
14251
14939
|
anchors: buildAnchorIndex(graph.pages),
|
|
14940
|
+
assetMounts: options.assetMounts ?? [],
|
|
14252
14941
|
publicDir: options.publicDir,
|
|
14253
14942
|
redirects: new Set((options.redirects ?? []).map((redirect) => toRoute(redirect.from))),
|
|
14254
14943
|
routes: new Set(graph.routes.keys())
|
|
@@ -14305,10 +14994,11 @@ var validateCommand = defineCommand11({
|
|
|
14305
14994
|
try {
|
|
14306
14995
|
const project = await scanProject(root, { mode: "build" });
|
|
14307
14996
|
diagnostics.push(...project.diagnostics);
|
|
14308
|
-
const publicDir =
|
|
14997
|
+
const publicDir = join44(root, "public");
|
|
14309
14998
|
diagnostics.push(...await validateLinks(project.graph, {
|
|
14999
|
+
assetMounts: resolveAssetMounts(root, project.config.content.assets),
|
|
14310
15000
|
checkExternal: Boolean(args.external),
|
|
14311
|
-
publicDir:
|
|
15001
|
+
publicDir: existsSync31(publicDir) ? publicDir : null,
|
|
14312
15002
|
redirects: project.config.redirects
|
|
14313
15003
|
}));
|
|
14314
15004
|
} catch (error) {
|
|
@@ -14322,6 +15012,7 @@ var validateCommand = defineCommand11({
|
|
|
14322
15012
|
if (args.json) {
|
|
14323
15013
|
const hadErrors2 = reportDiagnosticsJson(diagnostics, root);
|
|
14324
15014
|
if (hadErrors2 || Boolean(args.strict) && diagnostics.length > 0) {
|
|
15015
|
+
await flushStdout();
|
|
14325
15016
|
process.exit(1);
|
|
14326
15017
|
}
|
|
14327
15018
|
return;
|
|
@@ -14368,5 +15059,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
14368
15059
|
});
|
|
14369
15060
|
runMain(main);
|
|
14370
15061
|
|
|
14371
|
-
//# debugId=
|
|
15062
|
+
//# debugId=432F59E1AFBFB0A064756E2164756E21
|
|
14372
15063
|
//# sourceMappingURL=index.js.map
|