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.
Files changed (179) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/bin/blume.mjs +3 -2
  3. package/dist/cli/chunk-0qhq7b8q.js +111 -0
  4. package/dist/cli/chunk-0qhq7b8q.js.map +11 -0
  5. package/dist/cli/chunk-18tjv4f7.js +96 -0
  6. package/dist/cli/chunk-18tjv4f7.js.map +10 -0
  7. package/dist/cli/chunk-27gtm2ym.js +69 -0
  8. package/dist/cli/chunk-27gtm2ym.js.map +11 -0
  9. package/dist/cli/chunk-2aj8ddew.js +72 -0
  10. package/dist/cli/chunk-2aj8ddew.js.map +10 -0
  11. package/dist/cli/chunk-3r94j3tc.js +221 -0
  12. package/dist/cli/chunk-3r94j3tc.js.map +10 -0
  13. package/dist/cli/chunk-4trphnvy.js +102 -0
  14. package/dist/cli/chunk-4trphnvy.js.map +11 -0
  15. package/dist/cli/chunk-4xyggvgf.js +21 -0
  16. package/dist/cli/chunk-4xyggvgf.js.map +10 -0
  17. package/dist/cli/chunk-5d4q7121.js +4064 -0
  18. package/dist/cli/chunk-5d4q7121.js.map +40 -0
  19. package/dist/cli/chunk-5hs6gb7n.js +32 -0
  20. package/dist/cli/chunk-5hs6gb7n.js.map +10 -0
  21. package/dist/cli/chunk-6kzzpsx8.js +26 -0
  22. package/dist/cli/chunk-6kzzpsx8.js.map +10 -0
  23. package/dist/cli/chunk-8gnpdsn1.js +952 -0
  24. package/dist/cli/chunk-8gnpdsn1.js.map +12 -0
  25. package/dist/cli/chunk-9qs6acpw.js +176 -0
  26. package/dist/cli/chunk-9qs6acpw.js.map +10 -0
  27. package/dist/cli/chunk-agy5rzxy.js +2453 -0
  28. package/dist/cli/chunk-agy5rzxy.js.map +15 -0
  29. package/dist/cli/chunk-bcy492zc.js +16 -0
  30. package/dist/cli/chunk-bcy492zc.js.map +10 -0
  31. package/dist/cli/chunk-btfr9yvw.js +41 -0
  32. package/dist/cli/chunk-btfr9yvw.js.map +10 -0
  33. package/dist/cli/chunk-cbjnx4s8.js +73 -0
  34. package/dist/cli/chunk-cbjnx4s8.js.map +10 -0
  35. package/dist/cli/chunk-cfw6x4rm.js +1967 -0
  36. package/dist/cli/chunk-cfw6x4rm.js.map +34 -0
  37. package/dist/cli/chunk-ckh3a410.js +277 -0
  38. package/dist/cli/chunk-ckh3a410.js.map +11 -0
  39. package/dist/cli/chunk-drke6t0h.js +259 -0
  40. package/dist/cli/chunk-drke6t0h.js.map +11 -0
  41. package/dist/cli/chunk-ev67ycx0.js +15 -0
  42. package/dist/cli/chunk-ev67ycx0.js.map +10 -0
  43. package/dist/cli/chunk-ey89bjj1.js +209 -0
  44. package/dist/cli/chunk-ey89bjj1.js.map +11 -0
  45. package/dist/cli/chunk-j6pxe0dt.js +69 -0
  46. package/dist/cli/chunk-j6pxe0dt.js.map +11 -0
  47. package/dist/cli/chunk-jk1zwka1.js +387 -0
  48. package/dist/cli/chunk-jk1zwka1.js.map +12 -0
  49. package/dist/cli/chunk-jtb45atp.js +467 -0
  50. package/dist/cli/chunk-jtb45atp.js.map +14 -0
  51. package/dist/cli/chunk-jxkxjsc1.js +76 -0
  52. package/dist/cli/chunk-jxkxjsc1.js.map +10 -0
  53. package/dist/cli/chunk-kwx90v78.js +81 -0
  54. package/dist/cli/chunk-kwx90v78.js.map +10 -0
  55. package/dist/cli/chunk-n0nyat6g.js +30 -0
  56. package/dist/cli/chunk-n0nyat6g.js.map +10 -0
  57. package/dist/cli/chunk-pxj10x8y.js +35 -0
  58. package/dist/cli/chunk-pxj10x8y.js.map +10 -0
  59. package/dist/cli/chunk-qq9nm3qd.js +1141 -0
  60. package/dist/cli/chunk-qq9nm3qd.js.map +19 -0
  61. package/dist/cli/chunk-s102bysw.js +5170 -0
  62. package/dist/cli/chunk-s102bysw.js.map +47 -0
  63. package/dist/cli/chunk-s5dsk8bj.js +769 -0
  64. package/dist/cli/chunk-s5dsk8bj.js.map +13 -0
  65. package/dist/cli/chunk-s5e5jt53.js +227 -0
  66. package/dist/cli/chunk-s5e5jt53.js.map +11 -0
  67. package/dist/cli/chunk-sbdqrjbb.js +81 -0
  68. package/dist/cli/chunk-sbdqrjbb.js.map +10 -0
  69. package/dist/cli/chunk-tnskyrej.js +117 -0
  70. package/dist/cli/chunk-tnskyrej.js.map +10 -0
  71. package/dist/cli/chunk-v2ymm99c.js +1016 -0
  72. package/dist/cli/chunk-v2ymm99c.js.map +13 -0
  73. package/dist/cli/chunk-v5mm027v.js +185 -0
  74. package/dist/cli/chunk-v5mm027v.js.map +11 -0
  75. package/dist/cli/chunk-vt8fgygt.js +23 -0
  76. package/dist/cli/chunk-vt8fgygt.js.map +10 -0
  77. package/dist/cli/chunk-vxv4x1n8.js +17 -0
  78. package/dist/cli/chunk-vxv4x1n8.js.map +10 -0
  79. package/dist/cli/chunk-wd27zjcz.js +60 -0
  80. package/dist/cli/chunk-wd27zjcz.js.map +10 -0
  81. package/dist/cli/chunk-x66c5yjn.js +23 -0
  82. package/dist/cli/chunk-x66c5yjn.js.map +10 -0
  83. package/dist/cli/chunk-xv91q4nm.js +5314 -0
  84. package/dist/cli/chunk-xv91q4nm.js.map +58 -0
  85. package/dist/cli/chunk-y3g15rvv.js +679 -0
  86. package/dist/cli/chunk-y3g15rvv.js.map +15 -0
  87. package/dist/cli/chunk-ye9zdkgv.js +136 -0
  88. package/dist/cli/chunk-ye9zdkgv.js.map +10 -0
  89. package/dist/cli/chunk-ynacq3ev.js +1062 -0
  90. package/dist/cli/chunk-ynacq3ev.js.map +25 -0
  91. package/dist/cli/chunk-zr3ygrq3.js +54 -0
  92. package/dist/cli/chunk-zr3ygrq3.js.map +10 -0
  93. package/dist/cli/index.js +55 -27597
  94. package/dist/cli/index.js.map +5 -243
  95. package/dist/types/ai/ask-context.d.ts +26 -0
  96. package/dist/types/components/layout/nav-utils.d.ts +33 -1
  97. package/dist/types/core/code-fences.d.ts +11 -0
  98. package/dist/types/core/package-root.d.ts +1 -1
  99. package/dist/types/core/schema.d.ts +70 -0
  100. package/dist/types/theme/fonts.d.ts +22 -22
  101. package/docs/02-deployment.mdx +22 -1
  102. package/docs/configuration/ask-ai.mdx +1 -1
  103. package/docs/configuration/customization.mdx +2 -9
  104. package/docs/content/navigation.mdx +2 -0
  105. package/docs/content/syntax.mdx +1 -1
  106. package/docs/discoverability/open-graph.mdx +4 -0
  107. package/docs/reference/cli.mdx +1 -1
  108. package/package.json +4 -2
  109. package/src/ai/api/handlers.ts +4 -7
  110. package/src/ai/api/paths.ts +8 -0
  111. package/src/ai/api/spec.ts +2 -1
  112. package/src/ai/ask-context.ts +378 -22
  113. package/src/astro/generate.ts +161 -28
  114. package/src/astro/include-hmr.ts +10 -13
  115. package/src/astro/include-refresh.ts +0 -0
  116. package/src/astro/index.ts +6 -1
  117. package/src/astro/integration.ts +280 -53
  118. package/src/astro/module-types.ts +83 -0
  119. package/src/astro/templates.ts +256 -108
  120. package/src/audit/image-size.ts +10 -8
  121. package/src/cli/command-meta.ts +77 -0
  122. package/src/cli/commands/add.ts +2 -4
  123. package/src/cli/commands/audit.ts +2 -4
  124. package/src/cli/commands/build.ts +70 -346
  125. package/src/cli/commands/check.ts +2 -4
  126. package/src/cli/commands/dev.ts +31 -42
  127. package/src/cli/commands/doctor.ts +2 -4
  128. package/src/cli/commands/eject.ts +3 -41
  129. package/src/cli/commands/eval.ts +2 -5
  130. package/src/cli/commands/init.ts +2 -4
  131. package/src/cli/commands/mcp-stdio.ts +2 -5
  132. package/src/cli/commands/preview.ts +3 -5
  133. package/src/cli/commands/sync.ts +2 -4
  134. package/src/cli/commands/translate.ts +2 -5
  135. package/src/cli/commands/validate.ts +2 -4
  136. package/src/cli/commands/version.ts +2 -4
  137. package/src/cli/eject-scripts.ts +0 -45
  138. package/src/cli/host-args.ts +16 -0
  139. package/src/cli/index.ts +84 -35
  140. package/src/cli/lazy-command.ts +47 -0
  141. package/src/components/Icon.astro +24 -0
  142. package/src/components/content/GithubInfo.astro +4 -1
  143. package/src/components/icon-sprite-middleware.ts +41 -0
  144. package/src/components/icon-sprite.ts +93 -0
  145. package/src/components/layout/IconSprite.astro +11 -0
  146. package/src/components/layout/NavTree.astro +156 -188
  147. package/src/components/layout/NavTreeCache.astro +45 -0
  148. package/src/components/layout/NavTreeScript.astro +256 -0
  149. package/src/components/layout/PageActions.astro +11 -5
  150. package/src/components/layout/PageLayout.astro +21 -3
  151. package/src/components/layout/ReferenceLayout.astro +21 -4
  152. package/src/components/layout/RootLayout.astro +44 -6
  153. package/src/components/layout/nav-cache.ts +49 -0
  154. package/src/components/layout/nav-utils.ts +69 -1
  155. package/src/components/layout/page-locale.ts +29 -0
  156. package/src/core/api-name.ts +18 -0
  157. package/src/core/code-fences.ts +48 -0
  158. package/src/core/content-assets.ts +3 -7
  159. package/src/core/includes.ts +3 -7
  160. package/src/core/package-root.ts +1 -1
  161. package/src/core/schema.ts +19 -0
  162. package/src/core/sources/normalize.ts +2 -37
  163. package/src/core/sources/obsidian.ts +3 -2
  164. package/src/core/svg-dimensions.ts +97 -0
  165. package/src/core/version-cut.ts +2 -2
  166. package/src/deploy/artifacts.ts +370 -0
  167. package/src/deploy/cloudflare-negotiation.ts +97 -32
  168. package/src/deploy/function-bundle.ts +66 -20
  169. package/src/deploy/sitemap.ts +6 -0
  170. package/src/deploy/vercel-negotiation.ts +8 -30
  171. package/src/markdown/language-icon.ts +64 -20
  172. package/src/markdown/mermaid.ts +11 -0
  173. package/src/og/cache.ts +236 -0
  174. package/src/og/card.ts +18 -16
  175. package/src/og/index.ts +8 -1
  176. package/src/openapi/render-mdx.ts +9 -5
  177. package/src/registry/eject.ts +23 -10
  178. package/src/theme/entry.ts +41 -7
  179. package/src/theme/fonts.ts +30 -23
@@ -0,0 +1,769 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ EXAMPLE_SCAN_GLOB,
4
+ buildAskData,
5
+ buildMcpDiscovery,
6
+ buildMcpServerCard,
7
+ buildReferenceFiles,
8
+ buildRuntimeData,
9
+ buildThemeCss,
10
+ clientFeaturesFor,
11
+ collectStaged,
12
+ detectNeedsReact,
13
+ detectUsesMath,
14
+ discoverExamples,
15
+ discoverIslands,
16
+ exampleMarkdownLookup,
17
+ exampleScanRoots,
18
+ examplesEntryTemplate,
19
+ languageIconCssFor,
20
+ planComponentSlots,
21
+ rebaseSourceDirectives,
22
+ tailwindEntryTemplate,
23
+ twoslashCss
24
+ } from "./chunk-5d4q7121.js";
25
+ import {
26
+ buildMcpData,
27
+ buildRawMarkdown,
28
+ buildRssFeeds,
29
+ buildSearchDocuments,
30
+ renderRssFeed
31
+ } from "./chunk-ynacq3ev.js";
32
+ import {
33
+ customOgRoutes,
34
+ discoverPages,
35
+ routeIsTaken
36
+ } from "./chunk-sbdqrjbb.js";
37
+ import {
38
+ askComponentTemplate,
39
+ askEndpointTemplate,
40
+ astroConfigTemplate,
41
+ catchAllPageTemplate,
42
+ changelogIndexTemplate,
43
+ contentConfigTemplate,
44
+ exampleMapTemplate,
45
+ exampleSlug,
46
+ exampleWrapperTemplate,
47
+ examplesPageTemplate,
48
+ featuresTemplate,
49
+ islandMapTemplate,
50
+ islandWrapperTemplate,
51
+ mcpEndpointTemplate,
52
+ mcpPageFile,
53
+ mixedbreadSearchEndpointTemplate,
54
+ notFoundPageTemplate,
55
+ ogEndpointTemplate,
56
+ rawMarkdownEndpointTemplate,
57
+ resolveAskBackend,
58
+ rssEndpointTemplate,
59
+ runtimeTsconfigTemplate,
60
+ searchClientTemplate,
61
+ searchEndpointTemplate,
62
+ staticJsonEndpointTemplate
63
+ } from "./chunk-agy5rzxy.js";
64
+ import {
65
+ servesStaticIndex
66
+ } from "./chunk-wd27zjcz.js";
67
+ import {
68
+ buildIncludeGraph,
69
+ isOpenApiSource,
70
+ scanProject
71
+ } from "./chunk-s102bysw.js";
72
+ import {
73
+ hasScalarReferences
74
+ } from "./chunk-xv91q4nm.js";
75
+ import {
76
+ getBlumeVersion
77
+ } from "./chunk-4xyggvgf.js";
78
+ import {
79
+ packageRoot
80
+ } from "./chunk-6kzzpsx8.js";
81
+
82
+ // src/registry/eject.ts
83
+ import { existsSync } from "node:fs";
84
+ import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
85
+ import { join, relative } from "pathe";
86
+ var toPosix = (path) => path.split("\\").join("/");
87
+ var LOCAL_BLUME_SOURCE = "../../node_modules/blume/src/**/*.{astro,ts,tsx}";
88
+ var blumeSourceGlob = (root, genDir, resolveBlumeRoot = packageRoot) => {
89
+ if (existsSync(join(root, "node_modules", "blume"))) {
90
+ return LOCAL_BLUME_SOURCE;
91
+ }
92
+ try {
93
+ const src = join(resolveBlumeRoot(), "src");
94
+ return `${toPosix(relative(genDir, src))}/**/*.{astro,ts,tsx}`;
95
+ } catch {
96
+ console.warn('blume: could not locate the installed blume package; src/generated/app.css keeps its default `@source "../../node_modules/blume/..."` glob. If blume is hoisted elsewhere, point that glob at its install location or Blume\'s utility classes will be missing.');
97
+ return LOCAL_BLUME_SOURCE;
98
+ }
99
+ };
100
+ var ejectOpenApiData = (project) => {
101
+ const source = project.sources.find(isOpenApiSource);
102
+ return source ? source.openApiData() : {};
103
+ };
104
+ var askFiles = async (project, srcDir, genDir) => {
105
+ const { ask } = project.config.ai;
106
+ if (!(ask?.enabled && !ask.endpoint)) {
107
+ const endpointPath = join(srcDir, "pages", "api", "ask.ts");
108
+ if (existsSync(endpointPath)) {
109
+ const content = await readFile(endpointPath, "utf-8");
110
+ if (content.startsWith("// Generated by Blume. Do not edit.")) {
111
+ await rm(endpointPath, { force: true });
112
+ }
113
+ }
114
+ await rm(join(genDir, "ask-data.json"), { force: true });
115
+ return [];
116
+ }
117
+ const grounded = ask.provider !== "inkeep";
118
+ const files = [
119
+ {
120
+ content: askEndpointTemplate(resolveAskBackend(ask), grounded, {
121
+ instructions: ask.instructions,
122
+ retrieval: ask.retrieval
123
+ }),
124
+ path: join(srcDir, "pages", "api", "ask.ts")
125
+ }
126
+ ];
127
+ if (grounded) {
128
+ files.push({
129
+ content: `${JSON.stringify(await buildAskData(project))}
130
+ `,
131
+ path: join(genDir, "ask-data.json")
132
+ });
133
+ }
134
+ return files;
135
+ };
136
+ var hostsMcp = (project, userPages) => project.config.ai.mcp.enabled && !routeIsTaken(userPages, project.graph.pages, project.config.ai.mcp.route);
137
+ var mcpDiscoveryPages = (project, userPages) => hostsMcp(project, userPages) ? [
138
+ {
139
+ entrypoint: "src/blume-mcp/discovery.ts",
140
+ pattern: "/.well-known/mcp.json"
141
+ },
142
+ {
143
+ entrypoint: "src/blume-mcp/server-card.ts",
144
+ pattern: "/.well-known/mcp/server-card.json"
145
+ }
146
+ ] : [];
147
+ var mcpFiles = async (project, userPages, srcDir, genDir) => {
148
+ if (!hostsMcp(project, userPages)) {
149
+ return [];
150
+ }
151
+ const { route } = project.config.ai.mcp;
152
+ const data = await buildMcpData(project);
153
+ const discoveryInput = {
154
+ base: data.base,
155
+ name: data.name,
156
+ route,
157
+ site: data.site,
158
+ version: data.version
159
+ };
160
+ return [
161
+ {
162
+ content: `${JSON.stringify(data)}
163
+ `,
164
+ path: join(genDir, "mcp-data.json")
165
+ },
166
+ {
167
+ content: mcpEndpointTemplate(),
168
+ path: join(srcDir, "pages", mcpPageFile(route))
169
+ },
170
+ {
171
+ content: staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput)),
172
+ path: join(srcDir, "blume-mcp", "discovery.ts")
173
+ },
174
+ {
175
+ content: staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)),
176
+ path: join(srcDir, "blume-mcp", "server-card.ts")
177
+ }
178
+ ];
179
+ };
180
+ var changelogFiles = (project, userPages, srcDir, options) => {
181
+ const hasChangelog = project.graph.pages.some((page) => page.contentType === "changelog" && !(page.meta.draft || page.meta.sidebar.hidden));
182
+ const hasChangelogSource = (project.config.content.sources ?? []).some((source) => source.type === "github-releases");
183
+ if (!(hasChangelog || hasChangelogSource) || routeIsTaken(userPages, project.graph.pages, "/changelog")) {
184
+ return [];
185
+ }
186
+ return [
187
+ {
188
+ content: changelogIndexTemplate(options),
189
+ path: join(srcDir, "pages", "changelog.astro")
190
+ }
191
+ ];
192
+ };
193
+ var readUserCss = async (file, genDir) => {
194
+ if (!(file && existsSync(file))) {
195
+ return "";
196
+ }
197
+ const css = await readFile(file, "utf-8");
198
+ return rebaseSourceDirectives(css, { from: file, to: genDir });
199
+ };
200
+ var examplesPreviewFiles = (srcDir, basePath, hasExamples) => hasExamples ? [
201
+ {
202
+ content: examplesPageTemplate(),
203
+ path: join(srcDir, "pages", ...basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro")
204
+ }
205
+ ] : [];
206
+ var ejectIntegrationBridge = (config, root, configFile) => config.integrations.length > 0 && configFile ? { configFile: toPosix(relative(root, configFile)) } : undefined;
207
+ var eject = async (root) => {
208
+ const project = await scanProject(root, { mode: "build" });
209
+ const { context, config } = project;
210
+ const srcDir = join(root, "src");
211
+ const genDir = join(srcDir, "generated");
212
+ const askEnabled = config.ai.ask?.enabled ?? false;
213
+ const exportPdf = config.export.pdf;
214
+ const exportEpub = config.export.epub;
215
+ const examples = await discoverExamples(root, config.examples.source);
216
+ project.examples = exampleMarkdownLookup(examples.examples);
217
+ const [
218
+ pages,
219
+ needsReactRaw,
220
+ usesMath,
221
+ userTheme,
222
+ userExamplesCss,
223
+ rawMarkdown,
224
+ islands
225
+ ] = await Promise.all([
226
+ context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),
227
+ detectNeedsReact(root),
228
+ detectUsesMath(root),
229
+ readUserCss(context.themeFile, genDir),
230
+ readUserCss(config.examples.css ? join(root, config.examples.css) : null, genDir),
231
+ buildRawMarkdown(project),
232
+ discoverIslands(root)
233
+ ]);
234
+ const frameworks = new Set([
235
+ ...islands.islands.map((island) => island.framework),
236
+ ...examples.examples.map((example) => example.framework)
237
+ ]);
238
+ const needsReact = needsReactRaw || askEnabled || frameworks.has("react");
239
+ const needsVue = frameworks.has("vue");
240
+ const needsSvelte = frameworks.has("svelte");
241
+ const relContext = {
242
+ ...context,
243
+ contentRoot: toPosix(relative(root, context.contentRoot)),
244
+ outDir: ".",
245
+ root: "."
246
+ };
247
+ const componentsImport = context.componentsFile ? `../../${toPosix(relative(root, context.componentsFile))}` : null;
248
+ const relPages = [
249
+ ...pages.map((page) => ({
250
+ entrypoint: toPosix(relative(root, page.entrypoint)),
251
+ pattern: page.pattern
252
+ })),
253
+ ...mcpDiscoveryPages(project, pages)
254
+ ];
255
+ const staged = collectStaged(project);
256
+ const hasStaged = staged.size > 0;
257
+ const stagedDir = "blume-staged";
258
+ const features = await clientFeaturesFor(project);
259
+ const languageIcons = await languageIconCssFor(project);
260
+ const files = [
261
+ {
262
+ content: astroConfigTemplate({
263
+ askPath: "./src/generated/Ask.astro",
264
+ config,
265
+ contentRoot: relContext.contentRoot,
266
+ contentRoutes: project.manifest.routes.map((route) => route.path),
267
+ context: relContext,
268
+ examplesPath: "./src/generated/examples.ts",
269
+ examplesThemePath: "./src/generated/examples.css",
270
+ features,
271
+ featuresPath: "./src/generated/features.ts",
272
+ generatedModulesDir: "./src/generated",
273
+ integrationBridge: ejectIntegrationBridge(config, root, context.configFile),
274
+ needsReact,
275
+ needsSvelte,
276
+ needsVue,
277
+ pages: relPages,
278
+ searchClientPath: "./src/generated/search-client.ts",
279
+ themePath: "./src/generated/app.css"
280
+ }),
281
+ path: join(root, "astro.config.mjs")
282
+ },
283
+ {
284
+ content: runtimeTsconfigTemplate(),
285
+ path: join(root, "tsconfig.json"),
286
+ skipIfExists: true
287
+ },
288
+ {
289
+ content: contentConfigTemplate({
290
+ config,
291
+ context: relContext,
292
+ staged: hasStaged,
293
+ stagedBase: stagedDir
294
+ }),
295
+ path: join(srcDir, "content.config.ts")
296
+ },
297
+ {
298
+ content: catchAllPageTemplate({
299
+ exportEpub,
300
+ exportPdf,
301
+ mathEnabled: usesMath,
302
+ needsReact
303
+ }),
304
+ path: join(srcDir, "pages", "[...slug].astro")
305
+ },
306
+ {
307
+ content: planComponentSlots(componentsImport, null).module,
308
+ path: join(genDir, "components.ts")
309
+ },
310
+ {
311
+ content: islandMapTemplate(islands.islands),
312
+ path: join(genDir, "islands.ts")
313
+ },
314
+ {
315
+ content: exampleMapTemplate(examples.examples, config.basePath),
316
+ path: join(genDir, "examples.ts")
317
+ },
318
+ {
319
+ content: examplesEntryTemplate({
320
+ configTokens: buildThemeCss(config.theme),
321
+ sources: exampleScanRoots(root, examples.dir).map((dir) => `${relative(genDir, dir)}/${EXAMPLE_SCAN_GLOB}`),
322
+ userCss: userExamplesCss
323
+ }),
324
+ path: join(genDir, "examples.css")
325
+ },
326
+ {
327
+ content: tailwindEntryTemplate({
328
+ configTokens: buildThemeCss(config.theme),
329
+ languageIcons,
330
+ sources: [
331
+ blumeSourceGlob(root, genDir),
332
+ "../../**/*.{astro,mdx,ts,tsx}"
333
+ ],
334
+ twoslashCss: twoslashCss(),
335
+ userTheme
336
+ }),
337
+ path: join(genDir, "app.css")
338
+ },
339
+ { content: buildRuntimeData(project), path: join(genDir, "data.json") },
340
+ {
341
+ content: askComponentTemplate(askEnabled),
342
+ path: join(genDir, "Ask.astro")
343
+ },
344
+ {
345
+ content: `${JSON.stringify(ejectOpenApiData(project))}
346
+ `,
347
+ path: join(genDir, "openapi.json")
348
+ },
349
+ {
350
+ content: `${JSON.stringify(rawMarkdown)}
351
+ `,
352
+ path: join(genDir, "raw-markdown.json")
353
+ },
354
+ {
355
+ content: `${JSON.stringify(buildIncludeGraph(project.graph.pages))}
356
+ `,
357
+ path: join(genDir, "includes.json")
358
+ },
359
+ {
360
+ content: rawMarkdownEndpointTemplate("md"),
361
+ path: join(srcDir, "pages", "[...slug].md.ts")
362
+ },
363
+ {
364
+ content: rawMarkdownEndpointTemplate("mdx"),
365
+ path: join(srcDir, "pages", "[...slug].mdx.ts")
366
+ }
367
+ ];
368
+ if (askEnabled) {
369
+ files.push(...await askFiles(project, srcDir, genDir));
370
+ }
371
+ if (config.seo.og.enabled) {
372
+ files.push({
373
+ content: ogEndpointTemplate(customOgRoutes(pages, config.title, config.seo.og.titles), { pageDescriptions: config.seo.og.description !== false }),
374
+ path: join(srcDir, "pages", "og", "[...slug].png.ts")
375
+ });
376
+ }
377
+ files.push(...await mcpFiles(project, pages, srcDir, genDir), ...changelogFiles(project, pages, srcDir, {
378
+ exportEpub,
379
+ exportPdf,
380
+ needsReact,
381
+ staged: hasStaged
382
+ }));
383
+ if (!routeIsTaken(pages, project.graph.pages, "/404")) {
384
+ files.push({
385
+ content: notFoundPageTemplate(),
386
+ path: join(srcDir, "pages", "404.astro")
387
+ });
388
+ }
389
+ files.push({
390
+ content: featuresTemplate(features),
391
+ path: join(genDir, "features.ts")
392
+ }, {
393
+ content: searchClientTemplate(config),
394
+ path: join(genDir, "search-client.ts")
395
+ });
396
+ if (servesStaticIndex(config.search.provider)) {
397
+ const documents = await buildSearchDocuments(project);
398
+ files.push({
399
+ content: `${JSON.stringify(documents)}
400
+ `,
401
+ path: join(genDir, "search.json")
402
+ }, {
403
+ content: searchEndpointTemplate(),
404
+ path: join(srcDir, "pages", "blume-search.json.ts")
405
+ });
406
+ }
407
+ if (config.search.provider === "mixedbread") {
408
+ files.push({
409
+ content: mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""),
410
+ path: join(srcDir, "pages", "api", "search.ts")
411
+ });
412
+ }
413
+ const feeds = buildRssFeeds(project);
414
+ if (feeds.length > 0) {
415
+ const feedXml = Object.fromEntries(feeds.map((feed) => [feed.type, renderRssFeed(feed)]));
416
+ files.push({
417
+ content: `${JSON.stringify(feedXml)}
418
+ `,
419
+ path: join(genDir, "rss.json")
420
+ }, {
421
+ content: rssEndpointTemplate(),
422
+ path: join(srcDir, "pages", "[section]", "rss.xml.ts")
423
+ });
424
+ }
425
+ const warnings = [];
426
+ if (hasScalarReferences(config)) {
427
+ const references = await buildReferenceFiles({
428
+ config,
429
+ contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
430
+ root
431
+ });
432
+ warnings.push(...references.warnings);
433
+ for (const file of references.files) {
434
+ files.push({
435
+ content: file.content,
436
+ path: join(srcDir, "pages", file.pagePath)
437
+ });
438
+ }
439
+ }
440
+ files.push(...islands.islands.map((island) => ({
441
+ content: islandWrapperTemplate(island),
442
+ path: join(genDir, "islands", `${island.name}.astro`)
443
+ })), ...examples.examples.map((example) => ({
444
+ content: exampleWrapperTemplate(example),
445
+ path: join(genDir, "examples", `${exampleSlug(example.path)}.astro`)
446
+ })), ...examplesPreviewFiles(srcDir, config.basePath, examples.examples.length > 0));
447
+ for (const [entryId, content] of staged) {
448
+ files.push({ content, path: join(root, stagedDir, entryId) });
449
+ }
450
+ const written = files.filter((file) => !(file.skipIfExists && existsSync(file.path)));
451
+ await Promise.all(written.map(async (file) => {
452
+ await mkdir(join(file.path, ".."), { recursive: true });
453
+ await writeFile(file.path, file.content, "utf-8");
454
+ }));
455
+ const assetsSrc = join(context.outDir, "public", "blume-assets");
456
+ if (existsSync(assetsSrc)) {
457
+ await cp(assetsSrc, join(root, "public", "blume-assets"), {
458
+ recursive: true
459
+ });
460
+ }
461
+ await rm(context.outDir, { force: true, recursive: true });
462
+ return { files: written.map((file) => file.path), warnings };
463
+ };
464
+
465
+ // src/cli/eject-scripts.ts
466
+ import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
467
+ import { join as join2 } from "pathe";
468
+ var updatePackageScripts = async (root) => {
469
+ const pkgPath = join2(root, "package.json");
470
+ let pkg;
471
+ try {
472
+ pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
473
+ } catch {
474
+ return;
475
+ }
476
+ const scripts = pkg.scripts ?? {};
477
+ pkg.scripts = {
478
+ ...scripts,
479
+ build: "astro build",
480
+ dev: "astro dev",
481
+ preview: "astro preview"
482
+ };
483
+ await writeFile2(pkgPath, `${JSON.stringify(pkg, null, 2)}
484
+ `, "utf-8");
485
+ };
486
+
487
+ // src/cli/init/scaffold.ts
488
+ import { existsSync as existsSync2 } from "node:fs";
489
+ import { mkdir as mkdir2, writeFile as writeFile3 } from "node:fs/promises";
490
+ import { detect } from "package-manager-detector/detect";
491
+ import { basename, dirname, isAbsolute, join as join3, relative as relative2 } from "pathe";
492
+
493
+ // src/core/package-json.ts
494
+ var toPackageName = (raw) => raw.toLowerCase().replaceAll(/[^a-z0-9._-]+/gu, "-").replaceAll(/^[-_.]+|[-_.]+$/gu, "") || "docs";
495
+ var blumePackageJson = (name, extraDeps = {}) => {
496
+ const dependencies = Object.entries({
497
+ blume: `^${getBlumeVersion()}`,
498
+ ...extraDeps
499
+ }).toSorted(([a], [b]) => a < b ? -1 : 1).map(([dep, range]) => ` ${JSON.stringify(dep)}: ${JSON.stringify(range)}`).join(`,
500
+ `);
501
+ return `{
502
+ "name": ${JSON.stringify(name)},
503
+ "private": true,
504
+ "type": "module",
505
+ "scripts": {
506
+ "dev": "blume dev",
507
+ "build": "blume build",
508
+ "doctor": "blume doctor"
509
+ },
510
+ "dependencies": {
511
+ ${dependencies}
512
+ }
513
+ }
514
+ `;
515
+ };
516
+
517
+ // src/cli/init/scaffold.ts
518
+ var TEMPLATES = ["docs", "api", "sdk", "changelog"];
519
+ var PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
520
+ var SOURCE_KINDS = [
521
+ "filesystem",
522
+ "obsidian",
523
+ "github-releases",
524
+ "notion",
525
+ "sanity",
526
+ "mdx-remote"
527
+ ];
528
+ var page = (title, description, body) => `---
529
+ title: ${title}
530
+ description: ${description}
531
+ ---
532
+
533
+ ${body}
534
+ `;
535
+ var STARTERS = {
536
+ api: {
537
+ configExtra: `
538
+ openapi: {
539
+ enabled: true,
540
+ route: "/api",
541
+ sources: [
542
+ {
543
+ label: "Petstore",
544
+ spec: "https://petstore3.swagger.io/api/v3/openapi.json",
545
+ },
546
+ ],
547
+ },`,
548
+ files: (dir) => [
549
+ {
550
+ 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`."),
551
+ path: join3(dir, "index.mdx")
552
+ }
553
+ ]
554
+ },
555
+ changelog: {
556
+ configExtra: `
557
+ navigation: {
558
+ tabs: [
559
+ { label: "Docs", path: "/" },
560
+ { label: "Changelog", path: "/changelog" },
561
+ ],
562
+ },`,
563
+ files: (dir) => [
564
+ {
565
+ content: page("Introduction", "Welcome to your new Blume docs.", "# Introduction\n\nWrite your docs here, and log releases under `changelog/`."),
566
+ path: join3(dir, "index.mdx")
567
+ },
568
+ {
569
+ content: `---
570
+ title: v1.0.0
571
+ type: changelog
572
+ date: 2026-01-01
573
+ ---
574
+
575
+ The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries beside it.
576
+ `,
577
+ path: join3(dir, "changelog", "v1-0-0.mdx")
578
+ }
579
+ ]
580
+ },
581
+ docs: {
582
+ configExtra: "",
583
+ files: (dir) => [
584
+ {
585
+ content: page("Introduction", "Welcome to your new Blume docs.", `# Introduction
586
+
587
+ Welcome to **Blume** — markdown-first docs powered by Astro and Vite.
588
+
589
+ Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
590
+ path: join3(dir, "index.mdx")
591
+ }
592
+ ]
593
+ },
594
+ sdk: {
595
+ configExtra: "",
596
+ files: (dir) => [
597
+ {
598
+ content: page("Introduction", "Get started with the SDK.", `# Introduction
599
+
600
+ Install the SDK and make your first call. See [Installation](/installation).`),
601
+ path: join3(dir, "index.mdx")
602
+ },
603
+ {
604
+ content: page("Installation", "Install the SDK.", "# Installation\n\n```package-install\nyour-sdk\n```"),
605
+ path: join3(dir, "installation.mdx")
606
+ }
607
+ ]
608
+ }
609
+ };
610
+ var commandsFor = (pm) => ({
611
+ build: pm === "npm" || pm === "bun" ? `${pm} run build` : `${pm} build`,
612
+ dev: pm === "npm" ? "npm run dev" : `${pm} dev`,
613
+ exec: { bun: "bunx", npm: "npx", pnpm: "pnpm exec", yarn: "yarn" }[pm],
614
+ install: `${pm} install`
615
+ });
616
+ var isPackageManager = (value) => PACKAGE_MANAGERS.some((pm) => pm === value);
617
+ var detectPackageManager = (userAgent) => {
618
+ const name = userAgent?.split("/")[0];
619
+ return name !== undefined && isPackageManager(name) ? name : "npm";
620
+ };
621
+ var detectProjectPackageManager = async (root) => {
622
+ const detected = await detect({ cwd: root });
623
+ const name = detected?.name;
624
+ return name !== undefined && isPackageManager(name) ? name : detectPackageManager(process.env.npm_config_user_agent);
625
+ };
626
+ var validateContentDir = (root, dir) => isAbsolute(dir) || relative2(root, join3(root, dir)).startsWith("..") ? "Must be a relative path inside the project." : undefined;
627
+ var titleize = (raw) => {
628
+ const words = raw.replaceAll(/[-_.]+/gu, " ").split(/\s+/u).filter(Boolean);
629
+ return words.length === 0 ? "My Docs" : words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
630
+ };
631
+ var needsExplicitSources = (sources) => sources.some((source) => source !== "filesystem");
632
+ var SOURCE_SNIPPETS = {
633
+ "github-releases": ` // Changelog entries from GitHub Releases. Private repos read
634
+ // GITHUB_TOKEN from the environment.
635
+ {
636
+ type: "github-releases",
637
+ owner: "your-org",
638
+ repo: "your-repo",
639
+ prefix: "changelog",
640
+ },`,
641
+ "mdx-remote": ` // MDX fetched from a GitHub repo. Private repos read GITHUB_TOKEN
642
+ // from the environment.
643
+ {
644
+ type: "mdx-remote",
645
+ github: { owner: "your-org", repo: "your-repo", path: "docs" },
646
+ prefix: "remote",
647
+ },`,
648
+ notion: ` // Pages from a Notion database. Reads NOTION_TOKEN from the environment.
649
+ {
650
+ type: "notion",
651
+ database: "your-database-id",
652
+ prefix: "notion",
653
+ },`,
654
+ obsidian: ` // An Obsidian vault, read in place. No export step, and no
655
+ // generated notes in your repo. Point \`vault\` at your vault directory,
656
+ // relative to this config file.
657
+ {
658
+ type: "obsidian",
659
+ vault: "vault",
660
+ prefix: "notes",
661
+ },`,
662
+ sanity: ` // Documents from a Sanity dataset. Private datasets read SANITY_TOKEN
663
+ // from the environment.
664
+ {
665
+ type: "sanity",
666
+ projectId: "your-project-id",
667
+ dataset: "production",
668
+ query: \`*[_type == "doc"]\`,
669
+ prefix: "sanity",
670
+ },`
671
+ };
672
+ var contentBlockFor = (answers) => {
673
+ const sources = answers.sources.length === 0 ? ["filesystem"] : answers.sources;
674
+ if (!needsExplicitSources(sources)) {
675
+ return answers.contentDir === "docs" ? "" : `
676
+ content: {
677
+ root: ${JSON.stringify(answers.contentDir)},
678
+ },`;
679
+ }
680
+ const entries = SOURCE_KINDS.filter((kind) => sources.includes(kind)).map((kind) => kind === "filesystem" ? ` { type: "filesystem", root: ${JSON.stringify(answers.contentDir)} },` : SOURCE_SNIPPETS[kind]);
681
+ return `
682
+ content: {
683
+ sources: [
684
+ ${entries.join(`
685
+ `)}
686
+ ],
687
+ },`;
688
+ };
689
+ var buildConfig = (answers) => `import { defineConfig } from "blume";
690
+
691
+ export default defineConfig({
692
+ title: ${JSON.stringify(answers.title)},
693
+ description: "Documentation powered by Blume.",${STARTERS[answers.template].configExtra}${contentBlockFor(answers)}
694
+ });
695
+ `;
696
+ var extraDepsFor = (sources) => {
697
+ const deps = {};
698
+ if (sources.includes("notion")) {
699
+ deps["@notionhq/client"] = "^2.2.15";
700
+ }
701
+ if (sources.includes("sanity")) {
702
+ deps["@sanity/client"] = "^7.25.0";
703
+ }
704
+ return deps;
705
+ };
706
+ var buildPlan = (root, answers) => {
707
+ const files = [
708
+ {
709
+ content: blumePackageJson(toPackageName(basename(root)), extraDepsFor(answers.sources)),
710
+ path: join3(root, "package.json")
711
+ },
712
+ { content: buildConfig(answers), path: join3(root, "blume.config.ts") }
713
+ ];
714
+ if (answers.sources.length === 0 || answers.sources.includes("filesystem")) {
715
+ files.push(...STARTERS[answers.template].files(answers.contentDir).map((file) => ({ ...file, path: join3(root, file.path) })));
716
+ }
717
+ if (answers.sources.includes("obsidian")) {
718
+ files.push({
719
+ content: "# Welcome\n\nThis folder is read by Blume's `obsidian` source. Open it as a vault in Obsidian and write notes — `[[Wikilinks]]` become site links.\n",
720
+ path: join3(root, "vault", "Welcome.md")
721
+ });
722
+ }
723
+ return files;
724
+ };
725
+ var writeFileSafe = async (file, log) => {
726
+ if (existsSync2(file.path)) {
727
+ log.info(`Skipped existing ${file.path}`);
728
+ return false;
729
+ }
730
+ await mkdir2(dirname(file.path), { recursive: true });
731
+ await writeFile3(file.path, file.content, "utf-8");
732
+ log.success(`Created ${file.path}`);
733
+ return true;
734
+ };
735
+ var applyPlan = async (files, log) => {
736
+ const created = await Promise.all(files.map((file) => writeFileSafe(file, log)));
737
+ const createdPackage = files.some((file, index) => created[index] && basename(file.path) === "package.json");
738
+ return { createdPackage };
739
+ };
740
+ var envVarsFor = (sources) => [
741
+ ["GITHUB_TOKEN", ["github-releases", "mdx-remote"]],
742
+ ["NOTION_TOKEN", ["notion"]],
743
+ ["SANITY_TOKEN", ["sanity"]]
744
+ ].filter(([, kinds]) => kinds.some((kind) => sources.includes(kind))).map(([envVar]) => envVar);
745
+ var nextSteps = (answers, createdPackage) => {
746
+ const commands = commandsFor(answers.packageManager);
747
+ const lines = [];
748
+ if (answers.directory !== ".") {
749
+ lines.push(`cd ${answers.directory}`);
750
+ }
751
+ if (createdPackage) {
752
+ lines.push(commands.install);
753
+ }
754
+ lines.push(commands.dev);
755
+ const envVars = envVarsFor(answers.sources);
756
+ const auth = envVars.length > 0 ? `
757
+ Set ${envVars.join(" and ")} in .env.local so your sources can authenticate.
758
+ ` : "";
759
+ return `Next steps:
760
+
761
+ ${lines.join(`
762
+ `)}
763
+ ${auth}`;
764
+ };
765
+
766
+ export { eject, updatePackageScripts, TEMPLATES, PACKAGE_MANAGERS, commandsFor, detectPackageManager, detectProjectPackageManager, validateContentDir, titleize, buildPlan, applyPlan, nextSteps };
767
+
768
+ //# debugId=8ECB678F8EF6146364756E2164756E21
769
+ //# sourceMappingURL=chunk-s5dsk8bj.js.map