blume 1.6.5 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/bin/blume.mjs +3 -2
- package/dist/cli/chunk-0qhq7b8q.js +111 -0
- package/dist/cli/chunk-0qhq7b8q.js.map +11 -0
- package/dist/cli/chunk-18tjv4f7.js +96 -0
- package/dist/cli/chunk-18tjv4f7.js.map +10 -0
- package/dist/cli/chunk-27gtm2ym.js +69 -0
- package/dist/cli/chunk-27gtm2ym.js.map +11 -0
- package/dist/cli/chunk-2aj8ddew.js +72 -0
- package/dist/cli/chunk-2aj8ddew.js.map +10 -0
- package/dist/cli/chunk-3r94j3tc.js +221 -0
- package/dist/cli/chunk-3r94j3tc.js.map +10 -0
- package/dist/cli/chunk-4trphnvy.js +102 -0
- package/dist/cli/chunk-4trphnvy.js.map +11 -0
- package/dist/cli/chunk-4xyggvgf.js +21 -0
- package/dist/cli/chunk-4xyggvgf.js.map +10 -0
- package/dist/cli/chunk-5d4q7121.js +4064 -0
- package/dist/cli/chunk-5d4q7121.js.map +40 -0
- package/dist/cli/chunk-5hs6gb7n.js +32 -0
- package/dist/cli/chunk-5hs6gb7n.js.map +10 -0
- package/dist/cli/chunk-6kzzpsx8.js +26 -0
- package/dist/cli/chunk-6kzzpsx8.js.map +10 -0
- package/dist/cli/chunk-8gnpdsn1.js +952 -0
- package/dist/cli/chunk-8gnpdsn1.js.map +12 -0
- package/dist/cli/chunk-9qs6acpw.js +176 -0
- package/dist/cli/chunk-9qs6acpw.js.map +10 -0
- package/dist/cli/chunk-agy5rzxy.js +2453 -0
- package/dist/cli/chunk-agy5rzxy.js.map +15 -0
- package/dist/cli/chunk-bcy492zc.js +16 -0
- package/dist/cli/chunk-bcy492zc.js.map +10 -0
- package/dist/cli/chunk-btfr9yvw.js +41 -0
- package/dist/cli/chunk-btfr9yvw.js.map +10 -0
- package/dist/cli/chunk-cbjnx4s8.js +73 -0
- package/dist/cli/chunk-cbjnx4s8.js.map +10 -0
- package/dist/cli/chunk-cfw6x4rm.js +1967 -0
- package/dist/cli/chunk-cfw6x4rm.js.map +34 -0
- package/dist/cli/chunk-ckh3a410.js +277 -0
- package/dist/cli/chunk-ckh3a410.js.map +11 -0
- package/dist/cli/chunk-drke6t0h.js +259 -0
- package/dist/cli/chunk-drke6t0h.js.map +11 -0
- package/dist/cli/chunk-ev67ycx0.js +15 -0
- package/dist/cli/chunk-ev67ycx0.js.map +10 -0
- package/dist/cli/chunk-ey89bjj1.js +209 -0
- package/dist/cli/chunk-ey89bjj1.js.map +11 -0
- package/dist/cli/chunk-j6pxe0dt.js +69 -0
- package/dist/cli/chunk-j6pxe0dt.js.map +11 -0
- package/dist/cli/chunk-jk1zwka1.js +387 -0
- package/dist/cli/chunk-jk1zwka1.js.map +12 -0
- package/dist/cli/chunk-jtb45atp.js +467 -0
- package/dist/cli/chunk-jtb45atp.js.map +14 -0
- package/dist/cli/chunk-jxkxjsc1.js +76 -0
- package/dist/cli/chunk-jxkxjsc1.js.map +10 -0
- package/dist/cli/chunk-kwx90v78.js +81 -0
- package/dist/cli/chunk-kwx90v78.js.map +10 -0
- package/dist/cli/chunk-n0nyat6g.js +30 -0
- package/dist/cli/chunk-n0nyat6g.js.map +10 -0
- package/dist/cli/chunk-pxj10x8y.js +35 -0
- package/dist/cli/chunk-pxj10x8y.js.map +10 -0
- package/dist/cli/chunk-qq9nm3qd.js +1141 -0
- package/dist/cli/chunk-qq9nm3qd.js.map +19 -0
- package/dist/cli/chunk-s102bysw.js +5170 -0
- package/dist/cli/chunk-s102bysw.js.map +47 -0
- package/dist/cli/chunk-s5dsk8bj.js +769 -0
- package/dist/cli/chunk-s5dsk8bj.js.map +13 -0
- package/dist/cli/chunk-s5e5jt53.js +227 -0
- package/dist/cli/chunk-s5e5jt53.js.map +11 -0
- package/dist/cli/chunk-sbdqrjbb.js +81 -0
- package/dist/cli/chunk-sbdqrjbb.js.map +10 -0
- package/dist/cli/chunk-tnskyrej.js +117 -0
- package/dist/cli/chunk-tnskyrej.js.map +10 -0
- package/dist/cli/chunk-v2ymm99c.js +1016 -0
- package/dist/cli/chunk-v2ymm99c.js.map +13 -0
- package/dist/cli/chunk-v5mm027v.js +185 -0
- package/dist/cli/chunk-v5mm027v.js.map +11 -0
- package/dist/cli/chunk-vt8fgygt.js +23 -0
- package/dist/cli/chunk-vt8fgygt.js.map +10 -0
- package/dist/cli/chunk-vxv4x1n8.js +17 -0
- package/dist/cli/chunk-vxv4x1n8.js.map +10 -0
- package/dist/cli/chunk-wd27zjcz.js +60 -0
- package/dist/cli/chunk-wd27zjcz.js.map +10 -0
- package/dist/cli/chunk-x66c5yjn.js +23 -0
- package/dist/cli/chunk-x66c5yjn.js.map +10 -0
- package/dist/cli/chunk-xv91q4nm.js +5314 -0
- package/dist/cli/chunk-xv91q4nm.js.map +58 -0
- package/dist/cli/chunk-y3g15rvv.js +679 -0
- package/dist/cli/chunk-y3g15rvv.js.map +15 -0
- package/dist/cli/chunk-ye9zdkgv.js +136 -0
- package/dist/cli/chunk-ye9zdkgv.js.map +10 -0
- package/dist/cli/chunk-ynacq3ev.js +1062 -0
- package/dist/cli/chunk-ynacq3ev.js.map +25 -0
- package/dist/cli/chunk-zr3ygrq3.js +54 -0
- package/dist/cli/chunk-zr3ygrq3.js.map +10 -0
- package/dist/cli/index.js +55 -27597
- package/dist/cli/index.js.map +5 -243
- package/dist/types/ai/ask-context.d.ts +26 -0
- package/dist/types/components/layout/nav-utils.d.ts +33 -1
- package/dist/types/core/code-fences.d.ts +11 -0
- package/dist/types/core/package-root.d.ts +1 -1
- package/dist/types/core/schema.d.ts +70 -0
- package/dist/types/theme/fonts.d.ts +22 -22
- package/docs/02-deployment.mdx +22 -1
- package/docs/configuration/ask-ai.mdx +1 -1
- package/docs/configuration/customization.mdx +2 -9
- package/docs/content/navigation.mdx +2 -0
- package/docs/content/syntax.mdx +1 -1
- package/docs/discoverability/open-graph.mdx +4 -0
- package/docs/reference/cli.mdx +1 -1
- package/package.json +4 -2
- package/src/ai/api/handlers.ts +4 -7
- package/src/ai/api/paths.ts +8 -0
- package/src/ai/api/spec.ts +2 -1
- package/src/ai/ask-context.ts +378 -22
- package/src/astro/generate.ts +161 -28
- package/src/astro/include-hmr.ts +10 -13
- package/src/astro/include-refresh.ts +0 -0
- package/src/astro/index.ts +6 -1
- package/src/astro/integration.ts +280 -53
- package/src/astro/module-types.ts +83 -0
- package/src/astro/templates.ts +256 -108
- package/src/audit/image-size.ts +10 -8
- package/src/cli/command-meta.ts +77 -0
- package/src/cli/commands/add.ts +2 -4
- package/src/cli/commands/audit.ts +2 -4
- package/src/cli/commands/build.ts +70 -346
- package/src/cli/commands/check.ts +2 -4
- package/src/cli/commands/dev.ts +31 -42
- package/src/cli/commands/doctor.ts +2 -4
- package/src/cli/commands/eject.ts +3 -41
- package/src/cli/commands/eval.ts +2 -5
- package/src/cli/commands/init.ts +2 -4
- package/src/cli/commands/mcp-stdio.ts +2 -5
- package/src/cli/commands/preview.ts +3 -5
- package/src/cli/commands/sync.ts +2 -4
- package/src/cli/commands/translate.ts +2 -5
- package/src/cli/commands/validate.ts +2 -4
- package/src/cli/commands/version.ts +2 -4
- package/src/cli/eject-scripts.ts +0 -45
- package/src/cli/host-args.ts +16 -0
- package/src/cli/index.ts +84 -35
- package/src/cli/lazy-command.ts +47 -0
- package/src/components/Icon.astro +24 -0
- package/src/components/content/GithubInfo.astro +4 -1
- package/src/components/icon-sprite-middleware.ts +41 -0
- package/src/components/icon-sprite.ts +93 -0
- package/src/components/layout/IconSprite.astro +11 -0
- package/src/components/layout/NavTree.astro +156 -188
- package/src/components/layout/NavTreeCache.astro +45 -0
- package/src/components/layout/NavTreeScript.astro +256 -0
- package/src/components/layout/PageActions.astro +11 -5
- package/src/components/layout/PageLayout.astro +21 -3
- package/src/components/layout/ReferenceLayout.astro +21 -4
- package/src/components/layout/RootLayout.astro +44 -6
- package/src/components/layout/nav-cache.ts +49 -0
- package/src/components/layout/nav-utils.ts +69 -1
- package/src/components/layout/page-locale.ts +29 -0
- package/src/core/api-name.ts +18 -0
- package/src/core/code-fences.ts +48 -0
- package/src/core/content-assets.ts +3 -7
- package/src/core/includes.ts +3 -7
- package/src/core/package-root.ts +1 -1
- package/src/core/schema.ts +19 -0
- package/src/core/sources/normalize.ts +2 -37
- package/src/core/sources/obsidian.ts +3 -2
- package/src/core/svg-dimensions.ts +97 -0
- package/src/core/version-cut.ts +2 -2
- package/src/deploy/artifacts.ts +370 -0
- package/src/deploy/cloudflare-negotiation.ts +97 -32
- package/src/deploy/function-bundle.ts +66 -20
- package/src/deploy/sitemap.ts +6 -0
- package/src/deploy/vercel-negotiation.ts +8 -30
- package/src/markdown/language-icon.ts +64 -20
- package/src/markdown/mermaid.ts +11 -0
- package/src/og/cache.ts +236 -0
- package/src/og/card.ts +18 -16
- package/src/og/index.ts +8 -1
- package/src/openapi/render-mdx.ts +9 -5
- package/src/registry/eject.ts +23 -10
- package/src/theme/entry.ts +41 -7
- package/src/theme/fonts.ts +30 -23
package/src/astro/generate.ts
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
import { createRequire } from "node:module";
|
|
13
13
|
import { pathToFileURL } from "node:url";
|
|
14
14
|
|
|
15
|
-
import { imageSize } from "image-size";
|
|
16
15
|
import pMap from "p-map";
|
|
17
16
|
import {
|
|
18
17
|
basename,
|
|
@@ -29,10 +28,16 @@ import { OPENAPI_PATH } from "../ai/api/paths.ts";
|
|
|
29
28
|
import { buildApiSpec } from "../ai/api/spec.ts";
|
|
30
29
|
import { buildAskData } from "../ai/ask-data.ts";
|
|
31
30
|
import { askBackendRuntimeDep, resolveAskBackend } from "../ai/ask.ts";
|
|
31
|
+
import { buildHomeLinkHeader } from "../ai/link-headers.ts";
|
|
32
32
|
import { buildRawMarkdown, markdownRoutePaths } from "../ai/markdown.ts";
|
|
33
|
+
import type { RawMarkdownEntry } from "../ai/markdown.ts";
|
|
33
34
|
import { buildMcpData } from "../ai/mcp/data.ts";
|
|
34
35
|
import type { McpData } from "../ai/mcp/data.ts";
|
|
35
36
|
import { buildMcpDiscovery, buildMcpServerCard } from "../ai/mcp/discovery.ts";
|
|
37
|
+
import {
|
|
38
|
+
hasDeferrableGroups,
|
|
39
|
+
navVariants,
|
|
40
|
+
} from "../components/layout/nav-utils.ts";
|
|
36
41
|
import { normalizeBasePath } from "../core/base-path.ts";
|
|
37
42
|
import { validateUsedComponents } from "../core/component-diagnostics.ts";
|
|
38
43
|
import { analyzeComponentOverrides } from "../core/component-overrides.ts";
|
|
@@ -64,10 +69,18 @@ import { packageRoot } from "../core/package-root.ts";
|
|
|
64
69
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
65
70
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
66
71
|
import { resolveDocsCollection } from "../core/sources/resolve.ts";
|
|
72
|
+
import { svgDimensions } from "../core/svg-dimensions.ts";
|
|
67
73
|
import { trimChar } from "../core/trim.ts";
|
|
68
74
|
import { resolveTsconfigAliases } from "../core/tsconfig-aliases.ts";
|
|
69
75
|
import type { Diagnostic, Navigation } from "../core/types.ts";
|
|
76
|
+
import { getBlumeVersion } from "../core/version.ts";
|
|
70
77
|
import { buildRssFeeds, renderRssFeed } from "../deploy/rss.ts";
|
|
78
|
+
import {
|
|
79
|
+
languageIconCss,
|
|
80
|
+
languageIconSlugsIn,
|
|
81
|
+
} from "../markdown/language-icon.ts";
|
|
82
|
+
import { hasMermaidFence } from "../markdown/mermaid.ts";
|
|
83
|
+
import { ogCacheDir } from "../og/cache.ts";
|
|
71
84
|
import { missingFontFiles, resolveOgFonts } from "../og/derive.ts";
|
|
72
85
|
import type { DerivedOgFonts } from "../og/derive.ts";
|
|
73
86
|
import { resolveOgLogo } from "../og/logo.ts";
|
|
@@ -104,6 +117,7 @@ import {
|
|
|
104
117
|
exampleMarkdownLookup,
|
|
105
118
|
exampleScanRoots,
|
|
106
119
|
} from "./examples.ts";
|
|
120
|
+
import { publishDevNegotiation } from "./integration.ts";
|
|
107
121
|
import { discoverIslands } from "./islands.ts";
|
|
108
122
|
import {
|
|
109
123
|
customOgRoutes,
|
|
@@ -121,7 +135,6 @@ import {
|
|
|
121
135
|
changelogIndexTemplate,
|
|
122
136
|
contentAssetsEndpointTemplate,
|
|
123
137
|
contentConfigTemplate,
|
|
124
|
-
envTemplate,
|
|
125
138
|
exampleMapTemplate,
|
|
126
139
|
exampleWrapperTemplate,
|
|
127
140
|
examplesPageTemplate,
|
|
@@ -147,10 +160,13 @@ import {
|
|
|
147
160
|
runtimeDependencies,
|
|
148
161
|
runtimePackageTemplate,
|
|
149
162
|
runtimeTsconfigTemplate,
|
|
163
|
+
featuresTemplate,
|
|
164
|
+
navFragmentTemplate,
|
|
150
165
|
searchClientTemplate,
|
|
151
166
|
searchEndpointTemplate,
|
|
152
167
|
stagedContentDir,
|
|
153
168
|
} from "./templates.ts";
|
|
169
|
+
import type { ClientFeatures } from "./templates.ts";
|
|
154
170
|
|
|
155
171
|
/** Absolute path to the Blume package `src` directory. */
|
|
156
172
|
const BLUME_SRC = join(packageRoot(), "src");
|
|
@@ -321,10 +337,35 @@ export const blumeDepsDir = (pkgDir: string = packageRoot()): string | null => {
|
|
|
321
337
|
};
|
|
322
338
|
|
|
323
339
|
/**
|
|
324
|
-
*
|
|
325
|
-
*
|
|
340
|
+
* Create `link` as a directory symlink to `target`, falling back to a junction.
|
|
341
|
+
*
|
|
342
|
+
* The type only matters on Windows, and there a junction is the wrong first
|
|
343
|
+
* choice: Windows can't follow a *relative* symlink reached through a
|
|
344
|
+
* junction (it resolves the relative target against the junction-side path,
|
|
345
|
+
* so it lands outside the store and every lookup is ENOENT), and Bun's
|
|
346
|
+
* isolated linker writes exactly those into Blume's dependency directory
|
|
347
|
+
* whenever it holds symlink privilege. A directory symlink traverses them
|
|
348
|
+
* fine. Without that privilege (no Developer Mode, non-admin shell) creating
|
|
349
|
+
* one fails, so fall back to a junction — and in that session the installer
|
|
350
|
+
* had no privilege either, so the deps are absolute junctions a junction can
|
|
351
|
+
* follow. Exported for testing.
|
|
352
|
+
*/
|
|
353
|
+
export const symlinkDir = async (
|
|
354
|
+
target: string,
|
|
355
|
+
link: string
|
|
356
|
+
): Promise<void> => {
|
|
357
|
+
try {
|
|
358
|
+
await symlink(target, link, "dir");
|
|
359
|
+
} catch {
|
|
360
|
+
await symlink(target, link, "junction");
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Point `link` at Blume's dependency directory via a `node_modules` link,
|
|
366
|
+
* replacing a stale link we own and leaving a real directory untouched.
|
|
326
367
|
*
|
|
327
|
-
* `lstat`, not `existsSync`, so a broken
|
|
368
|
+
* `lstat`, not `existsSync`, so a broken link (target since moved) is still
|
|
328
369
|
* detected — `existsSync` follows the link and reports a dangling one as absent.
|
|
329
370
|
*/
|
|
330
371
|
const linkDepsJunction = async (
|
|
@@ -355,7 +396,7 @@ const linkDepsJunction = async (
|
|
|
355
396
|
await rm(link, { force: true });
|
|
356
397
|
}
|
|
357
398
|
await mkdir(dirname(link), { recursive: true });
|
|
358
|
-
await
|
|
399
|
+
await symlinkDir(depsDir, link);
|
|
359
400
|
};
|
|
360
401
|
|
|
361
402
|
/** Read the `version` field of a `package.json`, or null when unreadable. */
|
|
@@ -906,24 +947,13 @@ interface LogoDimensions {
|
|
|
906
947
|
}
|
|
907
948
|
|
|
908
949
|
/**
|
|
909
|
-
* Read dimensions from an SVG's explicit size or its view box
|
|
910
|
-
*
|
|
911
|
-
*
|
|
912
|
-
*
|
|
913
|
-
* `>` inside another attribute). An SVG with no usable size returns partial
|
|
914
|
-
* dimensions or throws; both collapse to undefined.
|
|
950
|
+
* Read dimensions from an SVG's explicit size or its view box, with the same
|
|
951
|
+
* root-tag parser og/card.ts uses for the OG brand mark so the header and the
|
|
952
|
+
* card can't disagree about one logo. An SVG with no usable size collapses to
|
|
953
|
+
* undefined.
|
|
915
954
|
*/
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
return;
|
|
919
|
-
}
|
|
920
|
-
try {
|
|
921
|
-
const { height, width } = imageSize(Buffer.from(svg));
|
|
922
|
-
return height && width ? { height, width } : undefined;
|
|
923
|
-
} catch {
|
|
924
|
-
return undefined;
|
|
925
|
-
}
|
|
926
|
-
};
|
|
955
|
+
const logoDimensions = (svg: string | undefined): LogoDimensions | undefined =>
|
|
956
|
+
svg ? (svgDimensions(svg) ?? undefined) : undefined;
|
|
927
957
|
|
|
928
958
|
/** Read a local SVG logo from the project root or public directory. */
|
|
929
959
|
const readLogoSvg = (
|
|
@@ -988,8 +1018,8 @@ const resolveLogo = (project: BlumeProject): BlumeLogo | null => {
|
|
|
988
1018
|
return { alt, href: brandHref, svg: lightSvg, text };
|
|
989
1019
|
}
|
|
990
1020
|
|
|
991
|
-
const lightDimensions =
|
|
992
|
-
const darkDimensions =
|
|
1021
|
+
const lightDimensions = logoDimensions(lightSvg);
|
|
1022
|
+
const darkDimensions = logoDimensions(darkSvg);
|
|
993
1023
|
const dimensions =
|
|
994
1024
|
lightDimensions || darkDimensions
|
|
995
1025
|
? { dark: darkDimensions, light: lightDimensions }
|
|
@@ -1904,6 +1934,84 @@ const assertFontFilesExist = (project: BlumeProject): void => {
|
|
|
1904
1934
|
}
|
|
1905
1935
|
};
|
|
1906
1936
|
|
|
1937
|
+
/**
|
|
1938
|
+
* The client libraries a site needs (see `featuresTemplate`): the EPUB
|
|
1939
|
+
* generator when `export.epub` is on, and the Mermaid element when any page's
|
|
1940
|
+
* source has a mermaid fence — read from the raw-Markdown mirrors (every
|
|
1941
|
+
* route's verbatim source) and, for sources that carry their text on the
|
|
1942
|
+
* page record, the record itself.
|
|
1943
|
+
*/
|
|
1944
|
+
export const clientFeaturesFrom = (
|
|
1945
|
+
project: BlumeProject,
|
|
1946
|
+
rawMarkdown: Record<string, RawMarkdownEntry>
|
|
1947
|
+
): ClientFeatures => ({
|
|
1948
|
+
epub: project.config.export.epub,
|
|
1949
|
+
mermaid:
|
|
1950
|
+
Object.values(rawMarkdown).some((entry) =>
|
|
1951
|
+
hasMermaidFence(entry.mdx ?? entry.md ?? "")
|
|
1952
|
+
) ||
|
|
1953
|
+
project.graph.pages.some(
|
|
1954
|
+
(page) => page.body !== undefined && hasMermaidFence(page.body.text)
|
|
1955
|
+
),
|
|
1956
|
+
});
|
|
1957
|
+
|
|
1958
|
+
/**
|
|
1959
|
+
* The theme's code-block icon rules for the languages the site's Markdown
|
|
1960
|
+
* uses (see `languageIconCss`), read from the raw-Markdown mirrors and the
|
|
1961
|
+
* page records that carry their text.
|
|
1962
|
+
*/
|
|
1963
|
+
export const languageIconCssFrom = (
|
|
1964
|
+
project: BlumeProject,
|
|
1965
|
+
rawMarkdown: Record<string, RawMarkdownEntry>
|
|
1966
|
+
): string =>
|
|
1967
|
+
languageIconCss(
|
|
1968
|
+
languageIconSlugsIn(
|
|
1969
|
+
[
|
|
1970
|
+
...Object.values(rawMarkdown).map(
|
|
1971
|
+
(entry) => entry.mdx ?? entry.md ?? ""
|
|
1972
|
+
),
|
|
1973
|
+
...project.graph.pages.map((page) => page.body?.text ?? ""),
|
|
1974
|
+
].join("\n")
|
|
1975
|
+
)
|
|
1976
|
+
);
|
|
1977
|
+
|
|
1978
|
+
/** {@link languageIconCssFrom} over a fresh read of the raw Markdown (eject). */
|
|
1979
|
+
export const languageIconCssFor = async (
|
|
1980
|
+
project: BlumeProject
|
|
1981
|
+
): Promise<string> =>
|
|
1982
|
+
languageIconCssFrom(project, await buildRawMarkdown(project));
|
|
1983
|
+
|
|
1984
|
+
/**
|
|
1985
|
+
* The deferred sidebar fragments page, only when some group is a disclosure
|
|
1986
|
+
* or a drill-in panel — a flat sidebar renders every row on every page, so
|
|
1987
|
+
* there is nothing to fetch. A previous pass's page is an orphan the
|
|
1988
|
+
* generator removes when the sidebar goes flat again.
|
|
1989
|
+
*/
|
|
1990
|
+
const writeNavFragments = (
|
|
1991
|
+
write: (path: string, content: string) => Promise<boolean>,
|
|
1992
|
+
srcDir: string,
|
|
1993
|
+
navFragments: boolean
|
|
1994
|
+
): Promise<boolean> =>
|
|
1995
|
+
navFragments
|
|
1996
|
+
? write(
|
|
1997
|
+
join(
|
|
1998
|
+
srcDir,
|
|
1999
|
+
"pages",
|
|
2000
|
+
"blume-nav",
|
|
2001
|
+
"[version]",
|
|
2002
|
+
"[locale]",
|
|
2003
|
+
"[id].astro"
|
|
2004
|
+
),
|
|
2005
|
+
navFragmentTemplate()
|
|
2006
|
+
)
|
|
2007
|
+
: Promise.resolve(false);
|
|
2008
|
+
|
|
2009
|
+
/** {@link clientFeaturesFrom} over a fresh read of the raw Markdown (eject). */
|
|
2010
|
+
export const clientFeaturesFor = async (
|
|
2011
|
+
project: BlumeProject
|
|
2012
|
+
): Promise<ClientFeatures> =>
|
|
2013
|
+
clientFeaturesFrom(project, await buildRawMarkdown(project));
|
|
2014
|
+
|
|
1907
2015
|
/**
|
|
1908
2016
|
* Write (or update) the generated `.blume/` Astro runtime for a project.
|
|
1909
2017
|
* Only files whose content changed are rewritten so Vite HMR stays fast.
|
|
@@ -1919,6 +2027,7 @@ export const generateRuntime = async (
|
|
|
1919
2027
|
const askPath = join(srcDir, "generated", "Ask.astro");
|
|
1920
2028
|
const themePath = join(srcDir, "generated", "app.css");
|
|
1921
2029
|
const searchClientPath = join(srcDir, "generated", "search-client.ts");
|
|
2030
|
+
const featuresPath = join(srcDir, "generated", "features.ts");
|
|
1922
2031
|
const examplesPath = join(srcDir, "generated", "examples.ts");
|
|
1923
2032
|
const examplesThemePath = join(srcDir, "generated", "examples.css");
|
|
1924
2033
|
|
|
@@ -1937,9 +2046,24 @@ export const generateRuntime = async (
|
|
|
1937
2046
|
|
|
1938
2047
|
const depsLinkWarning = await ensureDepsLink(out);
|
|
1939
2048
|
|
|
2049
|
+
// The dev negotiation inputs. Published in memory (below) rather than baked
|
|
2050
|
+
// into the generated config, so a content-route change never rewrites
|
|
2051
|
+
// `astro.config.mjs` — which would restart the dev server in place.
|
|
2052
|
+
const contentRoutes = markdownRoutePaths(project);
|
|
2053
|
+
const homeLinkHeader =
|
|
2054
|
+
buildHomeLinkHeader(config, contentRoutes) ?? undefined;
|
|
2055
|
+
|
|
1940
2056
|
const askEnabled = config.ai.ask?.enabled ?? false;
|
|
1941
2057
|
const exportPdf = config.export.pdf;
|
|
1942
2058
|
const exportEpub = config.export.epub;
|
|
2059
|
+
// Every route's source Markdown: published as `blume:raw-markdown` below,
|
|
2060
|
+
// and inspected here for the client features the site needs.
|
|
2061
|
+
const rawMarkdown = await buildRawMarkdown(project);
|
|
2062
|
+
const clientFeatures = clientFeaturesFrom(project, rawMarkdown);
|
|
2063
|
+
const navFragments = navVariants(project.graph).some(({ navigation }) =>
|
|
2064
|
+
hasDeferrableGroups(navigation.sidebar)
|
|
2065
|
+
);
|
|
2066
|
+
const languageIcons = languageIconCssFrom(project, rawMarkdown);
|
|
1943
2067
|
// Staged (non-filesystem) sources materialize into `.blume/content`; keyed by
|
|
1944
2068
|
// entryId so i18n duplicates of one entry write a single file. Collected here
|
|
1945
2069
|
// so math detection also sees staged bodies (they never live under root).
|
|
@@ -2059,10 +2183,12 @@ export const generateRuntime = async (
|
|
|
2059
2183
|
askPath,
|
|
2060
2184
|
config,
|
|
2061
2185
|
contentRoot: docsCollection.base,
|
|
2062
|
-
contentRoutes
|
|
2186
|
+
contentRoutes,
|
|
2063
2187
|
context,
|
|
2064
2188
|
examplesPath,
|
|
2065
2189
|
examplesThemePath,
|
|
2190
|
+
features: clientFeatures,
|
|
2191
|
+
featuresPath,
|
|
2066
2192
|
integrationBridge,
|
|
2067
2193
|
needsReact,
|
|
2068
2194
|
needsSvelte,
|
|
@@ -2080,7 +2206,6 @@ export const generateRuntime = async (
|
|
|
2080
2206
|
)
|
|
2081
2207
|
),
|
|
2082
2208
|
write(join(out, "tsconfig.json"), runtimeTsconfigTemplate()),
|
|
2083
|
-
write(join(srcDir, "env.d.ts"), envTemplate()),
|
|
2084
2209
|
write(
|
|
2085
2210
|
join(srcDir, "content.config.ts"),
|
|
2086
2211
|
contentConfigTemplate({
|
|
@@ -2097,9 +2222,11 @@ export const generateRuntime = async (
|
|
|
2097
2222
|
exportEpub,
|
|
2098
2223
|
exportPdf,
|
|
2099
2224
|
mathEnabled: usesMath,
|
|
2225
|
+
navFragments,
|
|
2100
2226
|
needsReact,
|
|
2101
2227
|
})
|
|
2102
2228
|
),
|
|
2229
|
+
writeNavFragments(write, srcDir, navFragments),
|
|
2103
2230
|
// The header's Ask trigger, behind the `blume:ask` alias. Always written
|
|
2104
2231
|
// (even when Ask is off, as a component that renders nothing) so the alias
|
|
2105
2232
|
// resolves — the same contract as `blume:search-client`.
|
|
@@ -2131,6 +2258,7 @@ export const generateRuntime = async (
|
|
|
2131
2258
|
themePath,
|
|
2132
2259
|
tailwindEntryTemplate({
|
|
2133
2260
|
configTokens: `${buildThemeCss(config.theme)}${buildFontsCss(config.theme.fonts)}`,
|
|
2261
|
+
languageIcons,
|
|
2134
2262
|
sources: [
|
|
2135
2263
|
`${BLUME_SRC}/**/*.{astro,ts,tsx}`,
|
|
2136
2264
|
`${context.root}/**/*.{astro,mdx,ts,tsx}`,
|
|
@@ -2193,6 +2321,10 @@ export const generateRuntime = async (
|
|
|
2193
2321
|
ogRoutes,
|
|
2194
2322
|
{
|
|
2195
2323
|
...projectOgFonts(project),
|
|
2324
|
+
cache: {
|
|
2325
|
+
dir: ogCacheDir(project.context),
|
|
2326
|
+
version: getBlumeVersion(),
|
|
2327
|
+
},
|
|
2196
2328
|
pageDescriptions: config.seo.og.description !== false,
|
|
2197
2329
|
},
|
|
2198
2330
|
changelogIndex
|
|
@@ -2229,6 +2361,7 @@ export const generateRuntime = async (
|
|
|
2229
2361
|
}),
|
|
2230
2362
|
writeNotFoundPage(write, srcDir, pages, project.graph.pages),
|
|
2231
2363
|
write(searchClientPath, searchClientTemplate(config)),
|
|
2364
|
+
write(featuresPath, featuresTemplate(clientFeatures)),
|
|
2232
2365
|
]);
|
|
2233
2366
|
|
|
2234
2367
|
// Client-loaded providers (orama, flexsearch) ship a static index + endpoint.
|
|
@@ -2257,7 +2390,6 @@ export const generateRuntime = async (
|
|
|
2257
2390
|
`${JSON.stringify(buildIncludeGraph(project.graph.pages))}\n`
|
|
2258
2391
|
);
|
|
2259
2392
|
|
|
2260
|
-
const rawMarkdown = await buildRawMarkdown(project);
|
|
2261
2393
|
modules.set("blume:raw-markdown", JSON.stringify(rawMarkdown));
|
|
2262
2394
|
// The originals behind the rewritten `/blume-assets/content/…` references in
|
|
2263
2395
|
// the agent-facing Markdown, plus the endpoint that serves them (and the
|
|
@@ -2391,6 +2523,7 @@ export const generateRuntime = async (
|
|
|
2391
2523
|
// Publish last, once every page that imports a module is on disk: a live
|
|
2392
2524
|
// dev server invalidates the changed modules and reloads the browser against
|
|
2393
2525
|
// the finished tree, never a half-written one.
|
|
2526
|
+
publishDevNegotiation({ contentRoutes, homeLinkHeader });
|
|
2394
2527
|
publishRuntimeModules(modules);
|
|
2395
2528
|
|
|
2396
2529
|
return { structuralChange: structural.some(Boolean), warnings };
|
package/src/astro/include-hmr.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { readFile
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
import { refreshBlumeContent } from "./integration.ts";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Dev-server invalidation for `<include>` partials. A partial is not an Astro
|
|
@@ -54,25 +56,20 @@ export const includeHmrPlugin = (graphPath: string): IncludeHmrPlugin => ({
|
|
|
54
56
|
return;
|
|
55
57
|
}
|
|
56
58
|
const { moduleGraph, ws } = ctx.server;
|
|
57
|
-
const now = new Date();
|
|
58
59
|
for (const includer of includers) {
|
|
59
60
|
for (const mod of moduleGraph.getModulesByFile(includer) ?? []) {
|
|
60
61
|
// SAFETY: the module came out of this module graph; `never` only
|
|
61
62
|
// reflects that the structural slice doesn't model the node type.
|
|
62
63
|
moduleGraph.invalidateModule(mod as never);
|
|
63
64
|
}
|
|
64
|
-
// Plain `.md` pages have no Vite module: their HTML lives in the
|
|
65
|
-
// content-layer store, rendered at sync time. Bump the page's mtime so
|
|
66
|
-
// Astro's content watcher re-syncs it — the include-aware digest
|
|
67
|
-
// (`withIncludeRefresh`) then forces a fresh render that re-reads the
|
|
68
|
-
// edited partial.
|
|
69
|
-
try {
|
|
70
|
-
// oxlint-disable-next-line no-await-in-loop -- ordered per-page touch
|
|
71
|
-
await utimes(includer, now, now);
|
|
72
|
-
} catch {
|
|
73
|
-
// The page may have been deleted since the graph was written.
|
|
74
|
-
}
|
|
75
65
|
}
|
|
66
|
+
// Plain `.md` pages have no Vite module: their HTML lives in the
|
|
67
|
+
// content-layer store, rendered at sync time. Ask Astro to re-run the
|
|
68
|
+
// loaders — `withIncludeRefresh` then evicts the includers whose partials
|
|
69
|
+
// changed, so the glob loader renders them afresh. `false` only before
|
|
70
|
+
// the server's `astro:server:setup` has run, when there is no store to go
|
|
71
|
+
// stale yet.
|
|
72
|
+
await refreshBlumeContent();
|
|
76
73
|
ws.send({ type: "full-reload" });
|
|
77
74
|
// The partial itself is not a module; suppress Vite's default handling.
|
|
78
75
|
return [];
|
|
Binary file
|
package/src/astro/index.ts
CHANGED
|
@@ -3,7 +3,12 @@ export { generateRuntime, prerenderDepsPlugin } from "./generate.ts";
|
|
|
3
3
|
export { includeHmrPlugin } from "./include-hmr.ts";
|
|
4
4
|
export { withIncludeRefresh } from "./include-refresh.ts";
|
|
5
5
|
export type { GenerateResult } from "./generate.ts";
|
|
6
|
-
export {
|
|
6
|
+
export {
|
|
7
|
+
blumeIntegration,
|
|
8
|
+
publishBuildProject,
|
|
9
|
+
publishDevNegotiation,
|
|
10
|
+
refreshBlumeContent,
|
|
11
|
+
} from "./integration.ts";
|
|
7
12
|
export type { BlumeIntegrationOptions, BlumePageRoute } from "./integration.ts";
|
|
8
13
|
export {
|
|
9
14
|
publishRuntimeModules,
|