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/cli/commands/dev.ts
CHANGED
|
@@ -4,37 +4,29 @@ import { defineCommand } from "citty";
|
|
|
4
4
|
import { debounce } from "perfect-debounce";
|
|
5
5
|
|
|
6
6
|
import { generateRuntime } from "../../astro/generate.ts";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
refreshBlumeContent,
|
|
9
|
+
showBlumeErrorOverlay,
|
|
10
|
+
} from "../../astro/integration.ts";
|
|
8
11
|
import { scanProject } from "../../core/project-graph.ts";
|
|
9
12
|
import { resolveRuntimeDir } from "../../core/project.ts";
|
|
10
13
|
import { parsePort } from "../args.ts";
|
|
14
|
+
import { commandMeta } from "../command-meta.ts";
|
|
11
15
|
import {
|
|
12
16
|
acquireDevLock,
|
|
13
17
|
describeDevLock,
|
|
14
18
|
DevLockHeldError,
|
|
15
19
|
updateDevLockPort,
|
|
16
20
|
} from "../dev-lock.ts";
|
|
21
|
+
import { normalizeHost } from "../host-args.ts";
|
|
17
22
|
import { logger, reportDiagnostics } from "../log.ts";
|
|
18
23
|
import { prepareProject } from "../prepare.ts";
|
|
19
24
|
|
|
20
|
-
/**
|
|
21
|
-
* Resolve a `--host` flag value into what Astro/Vite's `server.host` expects.
|
|
22
|
-
* citty has no mixed string/boolean arg type, so `host` is declared as a
|
|
23
|
-
* string and a bare `--host` parses as `""` (the CLI entry rewrites it to
|
|
24
|
-
* `--host=` first; see `host-args.ts`) — Node would bind all interfaces
|
|
25
|
-
* for `""`, but Vite's `resolveHostname` treats it as a literal hostname and
|
|
26
|
-
* prints malformed URLs like `http://:4321/`. Match Astro's own `--host`
|
|
27
|
-
* semantics instead: bare flag → `true` (bind all interfaces), `--host
|
|
28
|
-
* 10.0.0.1` → that address, absent → `false` (localhost only).
|
|
29
|
-
*/
|
|
30
|
-
export const normalizeHost = (host: string | undefined): boolean | string =>
|
|
31
|
-
host === "" ? true : (host ?? false);
|
|
32
|
-
|
|
33
25
|
/**
|
|
34
26
|
* A fingerprint of the route set: the sorted `path entryId` pairs. It changes
|
|
35
27
|
* when a page is added, removed, or renamed (a folder rename shifts many at
|
|
36
28
|
* once) but stays equal across pure body edits — so the dev loop can tell a
|
|
37
|
-
* "structural" change (needs a
|
|
29
|
+
* "structural" change (needs a content re-sync) from a hot-reloadable one.
|
|
38
30
|
*/
|
|
39
31
|
const routeSignature = (
|
|
40
32
|
routes: readonly { entryId: string; path: string }[]
|
|
@@ -63,10 +55,7 @@ export const devCommand = defineCommand({
|
|
|
63
55
|
},
|
|
64
56
|
strict: { description: "Fail on diagnostics.", type: "boolean" },
|
|
65
57
|
},
|
|
66
|
-
meta:
|
|
67
|
-
description: "Start the Blume development server.",
|
|
68
|
-
name: "dev",
|
|
69
|
-
},
|
|
58
|
+
meta: commandMeta.dev,
|
|
70
59
|
async run({ args }) {
|
|
71
60
|
const root = process.cwd();
|
|
72
61
|
const preview = args.preview ?? false;
|
|
@@ -110,10 +99,10 @@ export const devCommand = defineCommand({
|
|
|
110
99
|
strict: args.strict,
|
|
111
100
|
});
|
|
112
101
|
|
|
113
|
-
// A factory so
|
|
114
|
-
// (route-set) change
|
|
115
|
-
//
|
|
116
|
-
//
|
|
102
|
+
// A factory so the regenerate loop can recreate the server when a
|
|
103
|
+
// structural (route-set) change can't be re-synced in place (see below).
|
|
104
|
+
// `open` is honored on first boot only — a restart must not reopen the
|
|
105
|
+
// browser.
|
|
117
106
|
const createServer = (listenPort: number | undefined, open: boolean) =>
|
|
118
107
|
dev({
|
|
119
108
|
logLevel: args.debug ? "debug" : "info",
|
|
@@ -141,14 +130,16 @@ export const devCommand = defineCommand({
|
|
|
141
130
|
let lastSignature = routeSignature(project.manifest.routes);
|
|
142
131
|
|
|
143
132
|
// Watch user inputs and regenerate the runtime data on change. A body edit
|
|
144
|
-
// hot-reloads via Vite (fast path). A route-set change
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
133
|
+
// hot-reloads via Vite (fast path). A route-set change also needs Astro's
|
|
134
|
+
// content store re-synced: its glob watcher misses directory renames, so a
|
|
135
|
+
// renamed page would 404 (`getEntry` reads a stale store). Astro hands the
|
|
136
|
+
// integration `refreshContent` for exactly that — a full loader run
|
|
137
|
+
// against the live server, after which Astro's own store watcher clears
|
|
138
|
+
// the route cache and reloads the browser. Only a server that registered
|
|
139
|
+
// no refresh (none since Astro 5) falls back to a cold restart: stop,
|
|
140
|
+
// then bring up a fresh container whose cold sync re-globs everything.
|
|
141
|
+
// perfect-debounce both debounces the watch burst (80ms) and
|
|
142
|
+
// single-flights the scan: a
|
|
152
143
|
// trigger during a run never starts a second run, only marks one trailing
|
|
153
144
|
// rerun after the current settles. Both halves are load-bearing — a plain
|
|
154
145
|
// debounce once let bursts stack overlapping scans until the heap was
|
|
@@ -166,23 +157,21 @@ export const devCommand = defineCommand({
|
|
|
166
157
|
});
|
|
167
158
|
const nextSignature = routeSignature(next.manifest.routes);
|
|
168
159
|
const structural = nextSignature !== lastSignature;
|
|
169
|
-
|
|
160
|
+
// Generate first: the new runtime data (and any staged remote content)
|
|
161
|
+
// is on disk and published before the store re-syncs against it.
|
|
162
|
+
await generateRuntime(next);
|
|
163
|
+
if (structural && !(await refreshBlumeContent())) {
|
|
170
164
|
await server.stop();
|
|
171
|
-
await generateRuntime(next);
|
|
172
165
|
server = await createServer(boundPort, false);
|
|
173
|
-
} else {
|
|
174
|
-
await generateRuntime(next);
|
|
175
166
|
}
|
|
176
167
|
// Commit the signature only after the (re)generation succeeded. If the
|
|
177
|
-
// restart above throws mid-sequence, the signature stays
|
|
178
|
-
// next watch event retries the structural path
|
|
179
|
-
// route it to the non-structural branch with the server still down.
|
|
168
|
+
// re-sync or restart above throws mid-sequence, the signature stays
|
|
169
|
+
// stale so the next watch event retries the structural path.
|
|
180
170
|
lastSignature = nextSignature;
|
|
181
171
|
// Surface any content/config errors in the terminal AND the browser
|
|
182
|
-
// overlay. The terminal report
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
// the overlay call below can be a no-op there.
|
|
172
|
+
// overlay. The terminal report is not redundant: the overlay only
|
|
173
|
+
// shows once the browser has connected, and it clears on the next HMR
|
|
174
|
+
// update.
|
|
186
175
|
reportDiagnostics(next.diagnostics, root);
|
|
187
176
|
showBlumeErrorOverlay(next.diagnostics);
|
|
188
177
|
} catch (error) {
|
|
@@ -9,6 +9,7 @@ import { packageRoot } from "../../core/package-root.ts";
|
|
|
9
9
|
import { scanProject } from "../../core/project-graph.ts";
|
|
10
10
|
import { serverFeatures } from "../../core/server-features.ts";
|
|
11
11
|
import type { Diagnostic } from "../../core/types.ts";
|
|
12
|
+
import { commandMeta } from "../command-meta.ts";
|
|
12
13
|
import { reportInternalError } from "../internal-error.ts";
|
|
13
14
|
import {
|
|
14
15
|
flushStdout,
|
|
@@ -41,10 +42,7 @@ export const doctorCommand = defineCommand({
|
|
|
41
42
|
type: "boolean",
|
|
42
43
|
},
|
|
43
44
|
},
|
|
44
|
-
meta:
|
|
45
|
-
description: "Diagnose common configuration and content problems.",
|
|
46
|
-
name: "doctor",
|
|
47
|
-
},
|
|
45
|
+
meta: commandMeta.doctor,
|
|
48
46
|
async run({ args }) {
|
|
49
47
|
const root = process.cwd();
|
|
50
48
|
const diagnostics: Diagnostic[] = [];
|
|
@@ -1,63 +1,26 @@
|
|
|
1
1
|
import { defineCommand } from "citty";
|
|
2
2
|
import { relative } from "pathe";
|
|
3
3
|
|
|
4
|
-
import { loadConfig } from "../../core/config.ts";
|
|
5
4
|
import { eject } from "../../registry/eject.ts";
|
|
5
|
+
import { commandMeta } from "../command-meta.ts";
|
|
6
6
|
import { refuseIfDevRunning } from "../dev-lock.ts";
|
|
7
|
-
import {
|
|
8
|
-
droppedArtifactNotices,
|
|
9
|
-
updatePackageScripts,
|
|
10
|
-
} from "../eject-scripts.ts";
|
|
7
|
+
import { updatePackageScripts } from "../eject-scripts.ts";
|
|
11
8
|
import { commandsFor, detectProjectPackageManager } from "../init/scaffold.ts";
|
|
12
9
|
import { logger } from "../log.ts";
|
|
13
10
|
|
|
14
|
-
/**
|
|
15
|
-
* Warn which `blume build` post-build artifacts the ejected app stops
|
|
16
|
-
* producing. Printed both at the confirmation (so the decision is informed)
|
|
17
|
-
* and after `--yes` (so a direct eject still sees it). No-op when the config
|
|
18
|
-
* activates none of them.
|
|
19
|
-
*/
|
|
20
|
-
const reportDroppedArtifacts = (notices: string[]): void => {
|
|
21
|
-
if (notices.length === 0) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
logger.warn(
|
|
25
|
-
[
|
|
26
|
-
"The ejected build script runs plain `astro build`, which stops producing these `blume build` artifacts:",
|
|
27
|
-
...notices.map((notice) => ` - ${notice}`),
|
|
28
|
-
].join("\n")
|
|
29
|
-
);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
11
|
export const ejectCommand = defineCommand({
|
|
33
12
|
args: {
|
|
34
13
|
yes: { description: "Skip the confirmation prompt.", type: "boolean" },
|
|
35
14
|
},
|
|
36
|
-
meta:
|
|
37
|
-
description: "Promote the generated runtime into an owned Astro project.",
|
|
38
|
-
name: "eject",
|
|
39
|
-
},
|
|
15
|
+
meta: commandMeta.eject,
|
|
40
16
|
async run({ args }) {
|
|
41
17
|
const root = process.cwd();
|
|
42
18
|
refuseIfDevRunning(root, "ejecting");
|
|
43
19
|
|
|
44
|
-
// Config-aware drop list: only the artifacts this project actually
|
|
45
|
-
// produces are mentioned (e.g. the Pagefind index only for
|
|
46
|
-
// `search.provider: "pagefind"`).
|
|
47
|
-
let notices: string[] = [];
|
|
48
|
-
try {
|
|
49
|
-
const { config } = await loadConfig(root);
|
|
50
|
-
notices = droppedArtifactNotices(config);
|
|
51
|
-
} catch {
|
|
52
|
-
// A config that fails to load can't gate the notice; the eject itself
|
|
53
|
-
// surfaces the load error.
|
|
54
|
-
}
|
|
55
|
-
|
|
56
20
|
if (!args.yes) {
|
|
57
21
|
logger.warn(
|
|
58
22
|
"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."
|
|
59
23
|
);
|
|
60
|
-
reportDroppedArtifacts(notices);
|
|
61
24
|
logger.info("Re-run with --yes to proceed.");
|
|
62
25
|
return;
|
|
63
26
|
}
|
|
@@ -75,7 +38,6 @@ export const ejectCommand = defineCommand({
|
|
|
75
38
|
for (const file of files) {
|
|
76
39
|
process.stdout.write(` ${relative(root, file)}\n`);
|
|
77
40
|
}
|
|
78
|
-
reportDroppedArtifacts(notices);
|
|
79
41
|
// Print run commands matching the project's package manager (lockfile
|
|
80
42
|
// detection, since eject runs inside an existing project).
|
|
81
43
|
const pm = await detectProjectPackageManager(root);
|
package/src/cli/commands/eval.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { runEval } from "../../eval/run.ts";
|
|
23
23
|
import type { EvalResult } from "../../eval/run.ts";
|
|
24
24
|
import { EvalsFileError, loadEvalsFile } from "../../eval/schema.ts";
|
|
25
|
+
import { commandMeta } from "../command-meta.ts";
|
|
25
26
|
import { reportInternalError } from "../internal-error.ts";
|
|
26
27
|
import { flushStdout, logger } from "../log.ts";
|
|
27
28
|
|
|
@@ -183,11 +184,7 @@ export const evalCommand = defineCommand({
|
|
|
183
184
|
type: "boolean",
|
|
184
185
|
},
|
|
185
186
|
},
|
|
186
|
-
meta:
|
|
187
|
-
description:
|
|
188
|
-
"Test the docs: an agent answers your questions using only the documentation.",
|
|
189
|
-
name: "eval",
|
|
190
|
-
},
|
|
187
|
+
meta: commandMeta.eval,
|
|
191
188
|
async run({ args }) {
|
|
192
189
|
const root = process.cwd();
|
|
193
190
|
const { agent, threshold, timeoutS } = parseFlags(args);
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { resolve } from "pathe";
|
|
|
4
4
|
|
|
5
5
|
import { ensureGitignore } from "../../core/gitignore.ts";
|
|
6
6
|
import { eject } from "../../registry/eject.ts";
|
|
7
|
+
import { commandMeta } from "../command-meta.ts";
|
|
7
8
|
import { updatePackageScripts } from "../eject-scripts.ts";
|
|
8
9
|
import { collectAnswers } from "../init/questions.ts";
|
|
9
10
|
import {
|
|
@@ -84,10 +85,7 @@ export const initCommand = defineCommand({
|
|
|
84
85
|
type: "boolean",
|
|
85
86
|
},
|
|
86
87
|
},
|
|
87
|
-
meta:
|
|
88
|
-
description: "Scaffold a minimal Blume project.",
|
|
89
|
-
name: "init",
|
|
90
|
-
},
|
|
88
|
+
meta: commandMeta.init,
|
|
91
89
|
async run({ args }) {
|
|
92
90
|
const cwd = process.cwd();
|
|
93
91
|
|
|
@@ -4,6 +4,7 @@ import { defineCommand } from "citty";
|
|
|
4
4
|
|
|
5
5
|
import type { McpData } from "../../ai/mcp/data.ts";
|
|
6
6
|
import { serveMcpStdio } from "../../ai/mcp/stdio.ts";
|
|
7
|
+
import { commandMeta } from "../command-meta.ts";
|
|
7
8
|
|
|
8
9
|
export const mcpStdioCommand = defineCommand({
|
|
9
10
|
args: {
|
|
@@ -13,11 +14,7 @@ export const mcpStdioCommand = defineCommand({
|
|
|
13
14
|
type: "string",
|
|
14
15
|
},
|
|
15
16
|
},
|
|
16
|
-
meta:
|
|
17
|
-
description:
|
|
18
|
-
"Serve an MCP data snapshot over stdio (internal, used by `blume eval`).",
|
|
19
|
-
name: "mcp-stdio",
|
|
20
|
-
},
|
|
17
|
+
meta: commandMeta["mcp-stdio"],
|
|
21
18
|
async run({ args }) {
|
|
22
19
|
// stdout belongs to the JSON-RPC transport from here on; every diagnostic
|
|
23
20
|
// must go to stderr or the MCP client chokes on the stray line.
|
|
@@ -7,18 +7,16 @@ import { join } from "pathe";
|
|
|
7
7
|
import { loadConfig } from "../../core/config.ts";
|
|
8
8
|
import { resolveProjectContext } from "../../core/project.ts";
|
|
9
9
|
import { parsePort } from "../args.ts";
|
|
10
|
+
import { commandMeta } from "../command-meta.ts";
|
|
11
|
+
import { normalizeHost } from "../host-args.ts";
|
|
10
12
|
import { logger } from "../log.ts";
|
|
11
|
-
import { normalizeHost } from "./dev.ts";
|
|
12
13
|
|
|
13
14
|
export const previewCommand = defineCommand({
|
|
14
15
|
args: {
|
|
15
16
|
host: { description: "Network host to bind.", type: "string" },
|
|
16
17
|
port: { description: "Port to listen on.", type: "string" },
|
|
17
18
|
},
|
|
18
|
-
meta:
|
|
19
|
-
description: "Preview the last production build.",
|
|
20
|
-
name: "preview",
|
|
21
|
-
},
|
|
19
|
+
meta: commandMeta.preview,
|
|
22
20
|
async run({ args }) {
|
|
23
21
|
const root = process.cwd();
|
|
24
22
|
const { config } = await loadConfig(root);
|
package/src/cli/commands/sync.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
resolveProjectContext,
|
|
9
9
|
resolveRuntimeDir,
|
|
10
10
|
} from "../../core/project.ts";
|
|
11
|
+
import { commandMeta } from "../command-meta.ts";
|
|
11
12
|
import { readDevLock } from "../dev-lock.ts";
|
|
12
13
|
import { logger } from "../log.ts";
|
|
13
14
|
import { prepareProject } from "../prepare.ts";
|
|
@@ -24,10 +25,7 @@ export const syncCommand = defineCommand({
|
|
|
24
25
|
},
|
|
25
26
|
strict: { description: "Fail on diagnostics.", type: "boolean" },
|
|
26
27
|
},
|
|
27
|
-
meta:
|
|
28
|
-
description: "Re-fetch remote content sources and regenerate the runtime.",
|
|
29
|
-
name: "sync",
|
|
30
|
-
},
|
|
28
|
+
meta: commandMeta.sync,
|
|
31
29
|
async run({ args }) {
|
|
32
30
|
const root = process.cwd();
|
|
33
31
|
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from "../../translate/report.ts";
|
|
27
27
|
import { runTranslate } from "../../translate/run.ts";
|
|
28
28
|
import { computeWorkList } from "../../translate/work-list.ts";
|
|
29
|
+
import { commandMeta } from "../command-meta.ts";
|
|
29
30
|
import { reportInternalError } from "../internal-error.ts";
|
|
30
31
|
import { flushStdout, logger } from "../log.ts";
|
|
31
32
|
|
|
@@ -181,11 +182,7 @@ export const translateCommand = defineCommand({
|
|
|
181
182
|
type: "string",
|
|
182
183
|
},
|
|
183
184
|
},
|
|
184
|
-
meta:
|
|
185
|
-
description:
|
|
186
|
-
"Translate docs into the configured locales with a local agent CLI.",
|
|
187
|
-
name: "translate",
|
|
188
|
-
},
|
|
185
|
+
meta: commandMeta.translate,
|
|
189
186
|
async run({ args }) {
|
|
190
187
|
const root = process.cwd();
|
|
191
188
|
const { agent, concurrency, timeoutS } = parseFlags(args);
|
|
@@ -13,6 +13,7 @@ import { validateLinks } from "../../core/links.ts";
|
|
|
13
13
|
import { buildManifest } from "../../core/manifest.ts";
|
|
14
14
|
import { scanProject } from "../../core/project-graph.ts";
|
|
15
15
|
import type { Diagnostic } from "../../core/types.ts";
|
|
16
|
+
import { commandMeta } from "../command-meta.ts";
|
|
16
17
|
import { reportInternalError } from "../internal-error.ts";
|
|
17
18
|
import {
|
|
18
19
|
flushStdout,
|
|
@@ -36,10 +37,7 @@ export const validateCommand = defineCommand({
|
|
|
36
37
|
type: "boolean",
|
|
37
38
|
},
|
|
38
39
|
},
|
|
39
|
-
meta:
|
|
40
|
-
description: "Validate internal, anchor, asset, and external links.",
|
|
41
|
-
name: "validate",
|
|
42
|
-
},
|
|
40
|
+
meta: commandMeta.validate,
|
|
43
41
|
async run({ args }) {
|
|
44
42
|
const root = process.cwd();
|
|
45
43
|
const diagnostics: Diagnostic[] = [];
|
|
@@ -3,6 +3,7 @@ import { defineCommand } from "citty";
|
|
|
3
3
|
import { loadConfig } from "../../core/config.ts";
|
|
4
4
|
import { BlumeError } from "../../core/diagnostics.ts";
|
|
5
5
|
import { CutError, cutVersion } from "../../core/version-cut.ts";
|
|
6
|
+
import { commandMeta } from "../command-meta.ts";
|
|
6
7
|
import { reportInternalError } from "../internal-error.ts";
|
|
7
8
|
import { logger } from "../log.ts";
|
|
8
9
|
|
|
@@ -18,10 +19,7 @@ export const versionCommand = defineCommand({
|
|
|
18
19
|
type: "positional",
|
|
19
20
|
},
|
|
20
21
|
},
|
|
21
|
-
meta:
|
|
22
|
-
description: "Freeze the current docs as an archived version.",
|
|
23
|
-
name: "version",
|
|
24
|
-
},
|
|
22
|
+
meta: commandMeta.version,
|
|
25
23
|
async run({ args }) {
|
|
26
24
|
const root = process.cwd();
|
|
27
25
|
|
package/src/cli/eject-scripts.ts
CHANGED
|
@@ -2,9 +2,6 @@ import { readFile, writeFile } from "node:fs/promises";
|
|
|
2
2
|
|
|
3
3
|
import { join } from "pathe";
|
|
4
4
|
|
|
5
|
-
import type { ResolvedConfig } from "../core/schema.ts";
|
|
6
|
-
import { searchProviderMeta } from "../search/providers.ts";
|
|
7
|
-
|
|
8
5
|
/** A JSON value, as `JSON.parse` of a manifest can return. */
|
|
9
6
|
type JsonValue =
|
|
10
7
|
| string
|
|
@@ -20,48 +17,6 @@ interface PackageManifest {
|
|
|
20
17
|
scripts?: Record<string, string>;
|
|
21
18
|
}
|
|
22
19
|
|
|
23
|
-
/**
|
|
24
|
-
* The `blume build`-only artifacts this project's config actually produces, as
|
|
25
|
-
* notice lines for the eject command. After an eject the build script runs
|
|
26
|
-
* plain `astro build`, which emits none of them — printing the config-aware
|
|
27
|
-
* list makes the loss explicit instead of silent (a Pagefind site would
|
|
28
|
-
* otherwise eject into a build whose search fails at runtime). Each gate
|
|
29
|
-
* mirrors the artifact's producer in `blume build` (cli/commands/build.ts).
|
|
30
|
-
*/
|
|
31
|
-
export const droppedArtifactNotices = (config: ResolvedConfig): string[] => {
|
|
32
|
-
const notices: string[] = [];
|
|
33
|
-
if (config.search.provider === "pagefind") {
|
|
34
|
-
notices.push(
|
|
35
|
-
'the Pagefind search index — the search UI loads it from the built site, so search will break in production. Add a post-build step: `"build": "astro build && pagefind --site dist"` (with `pagefind` installed as a devDependency).'
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
if (searchProviderMeta(config.search.provider).syncs) {
|
|
39
|
-
notices.push(
|
|
40
|
-
`the hosted ${config.search.provider} index sync — new and updated pages stop being pushed; re-upload your search records after each build with the provider's API or CLI.`
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
if (config.ai.llmsTxt.enabled) {
|
|
44
|
-
notices.push("llms.txt and llms-full.txt");
|
|
45
|
-
}
|
|
46
|
-
if (config.deployment.site && config.seo.sitemap) {
|
|
47
|
-
notices.push(
|
|
48
|
-
"sitemap.xml — recreate it with the @astrojs/sitemap integration."
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
if (config.seo.robots) {
|
|
52
|
-
notices.push("robots.txt — recreate it as a public/robots.txt file.");
|
|
53
|
-
}
|
|
54
|
-
if (config.seo.agentReadability) {
|
|
55
|
-
notices.push("agent-readability.json");
|
|
56
|
-
}
|
|
57
|
-
if (config.redirects.length > 0 && config.deployment.output === "static") {
|
|
58
|
-
notices.push(
|
|
59
|
-
"the platform redirect files (_redirects, vercel.json) — your redirects still work as Astro-generated meta-refresh pages."
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
return notices;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
20
|
/**
|
|
66
21
|
* Rewrite the project's package.json scripts to run Astro directly. After an
|
|
67
22
|
* eject the Blume CLI no longer manages the runtime, so scaffolded scripts like
|
package/src/cli/host-args.ts
CHANGED
|
@@ -16,3 +16,19 @@ export const normalizeHostArgs = (rawArgs: readonly string[]): string[] =>
|
|
|
16
16
|
const next = rawArgs[index + 1];
|
|
17
17
|
return next === undefined || next.startsWith("-") ? "--host=" : arg;
|
|
18
18
|
});
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolve a `--host` flag value into what Astro/Vite's `server.host` expects.
|
|
22
|
+
* citty has no mixed string/boolean arg type, so `host` is declared as a
|
|
23
|
+
* string and a bare `--host` parses as `""` (`normalizeHostArgs` rewrites it
|
|
24
|
+
* to `--host=` first) — Node would bind all interfaces for `""`, but Vite's
|
|
25
|
+
* `resolveHostname` treats it as a literal hostname and prints malformed URLs
|
|
26
|
+
* like `http://:4321/`. Match Astro's own `--host` semantics instead: bare
|
|
27
|
+
* flag → `true` (bind all interfaces), `--host 10.0.0.1` → that address,
|
|
28
|
+
* absent → `false` (localhost only).
|
|
29
|
+
*
|
|
30
|
+
* Lives beside `normalizeHostArgs` rather than in `commands/dev.ts` so
|
|
31
|
+
* `preview` can share it without importing the whole dev command graph.
|
|
32
|
+
*/
|
|
33
|
+
export const normalizeHost = (host?: string): boolean | string =>
|
|
34
|
+
host === "" ? true : (host ?? false);
|
package/src/cli/index.ts
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
import { defineCommand, runMain } from "citty";
|
|
2
2
|
|
|
3
3
|
import { getBlumeVersion } from "../core/version.ts";
|
|
4
|
-
import {
|
|
5
|
-
import { auditCommand } from "./commands/audit.ts";
|
|
6
|
-
import { buildCommand } from "./commands/build.ts";
|
|
7
|
-
import { checkCommand } from "./commands/check.ts";
|
|
8
|
-
import { devCommand } from "./commands/dev.ts";
|
|
9
|
-
import { doctorCommand } from "./commands/doctor.ts";
|
|
10
|
-
import { ejectCommand } from "./commands/eject.ts";
|
|
11
|
-
import { evalCommand } from "./commands/eval.ts";
|
|
12
|
-
import { initCommand } from "./commands/init.ts";
|
|
13
|
-
import { mcpStdioCommand } from "./commands/mcp-stdio.ts";
|
|
14
|
-
import { previewCommand } from "./commands/preview.ts";
|
|
15
|
-
import { syncCommand } from "./commands/sync.ts";
|
|
16
|
-
import { translateCommand } from "./commands/translate.ts";
|
|
17
|
-
import { validateCommand } from "./commands/validate.ts";
|
|
18
|
-
import { versionCommand } from "./commands/version.ts";
|
|
4
|
+
import { commandMeta } from "./command-meta.ts";
|
|
19
5
|
import { loadEnvFiles } from "./env.ts";
|
|
20
6
|
import { normalizeHostArgs } from "./host-args.ts";
|
|
21
7
|
import { reportInternalError } from "./internal-error.ts";
|
|
8
|
+
import { lazyCommand } from "./lazy-command.ts";
|
|
9
|
+
// consola reads CONSOLA_LEVEL / NODE_ENV / TEST / CI / DEBUG when its module
|
|
10
|
+
// evaluates. Import the logger here, ahead of `loadEnvFiles`, so those values
|
|
11
|
+
// come from the real environment — never from a project `.env`, which would
|
|
12
|
+
// otherwise silence or restyle every command's output. The commands are loaded
|
|
13
|
+
// lazily below, so without this line consola would first evaluate after `.env`
|
|
14
|
+
// had been applied.
|
|
15
|
+
import "./log.ts";
|
|
22
16
|
|
|
23
17
|
const main = defineCommand({
|
|
24
18
|
meta: {
|
|
@@ -27,31 +21,86 @@ const main = defineCommand({
|
|
|
27
21
|
version: getBlumeVersion(),
|
|
28
22
|
},
|
|
29
23
|
subCommands: {
|
|
30
|
-
add:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
24
|
+
add: lazyCommand(
|
|
25
|
+
commandMeta.add,
|
|
26
|
+
() => import("./commands/add.ts"),
|
|
27
|
+
"addCommand"
|
|
28
|
+
),
|
|
29
|
+
audit: lazyCommand(
|
|
30
|
+
commandMeta.audit,
|
|
31
|
+
() => import("./commands/audit.ts"),
|
|
32
|
+
"auditCommand"
|
|
33
|
+
),
|
|
34
|
+
build: lazyCommand(
|
|
35
|
+
commandMeta.build,
|
|
36
|
+
() => import("./commands/build.ts"),
|
|
37
|
+
"buildCommand"
|
|
38
|
+
),
|
|
39
|
+
check: lazyCommand(
|
|
40
|
+
commandMeta.check,
|
|
41
|
+
() => import("./commands/check.ts"),
|
|
42
|
+
"checkCommand"
|
|
43
|
+
),
|
|
44
|
+
dev: lazyCommand(
|
|
45
|
+
commandMeta.dev,
|
|
46
|
+
() => import("./commands/dev.ts"),
|
|
47
|
+
"devCommand"
|
|
48
|
+
),
|
|
49
|
+
doctor: lazyCommand(
|
|
50
|
+
commandMeta.doctor,
|
|
51
|
+
() => import("./commands/doctor.ts"),
|
|
52
|
+
"doctorCommand"
|
|
53
|
+
),
|
|
54
|
+
eject: lazyCommand(
|
|
55
|
+
commandMeta.eject,
|
|
56
|
+
() => import("./commands/eject.ts"),
|
|
57
|
+
"ejectCommand"
|
|
58
|
+
),
|
|
59
|
+
eval: lazyCommand(
|
|
60
|
+
commandMeta.eval,
|
|
61
|
+
() => import("./commands/eval.ts"),
|
|
62
|
+
"evalCommand"
|
|
63
|
+
),
|
|
64
|
+
init: lazyCommand(
|
|
65
|
+
commandMeta.init,
|
|
66
|
+
() => import("./commands/init.ts"),
|
|
67
|
+
"initCommand"
|
|
68
|
+
),
|
|
69
|
+
"mcp-stdio": lazyCommand(
|
|
70
|
+
commandMeta["mcp-stdio"],
|
|
71
|
+
() => import("./commands/mcp-stdio.ts"),
|
|
72
|
+
"mcpStdioCommand"
|
|
73
|
+
),
|
|
74
|
+
preview: lazyCommand(
|
|
75
|
+
commandMeta.preview,
|
|
76
|
+
() => import("./commands/preview.ts"),
|
|
77
|
+
"previewCommand"
|
|
78
|
+
),
|
|
79
|
+
sync: lazyCommand(
|
|
80
|
+
commandMeta.sync,
|
|
81
|
+
() => import("./commands/sync.ts"),
|
|
82
|
+
"syncCommand"
|
|
83
|
+
),
|
|
84
|
+
translate: lazyCommand(
|
|
85
|
+
commandMeta.translate,
|
|
86
|
+
() => import("./commands/translate.ts"),
|
|
87
|
+
"translateCommand"
|
|
88
|
+
),
|
|
89
|
+
validate: lazyCommand(
|
|
90
|
+
commandMeta.validate,
|
|
91
|
+
() => import("./commands/validate.ts"),
|
|
92
|
+
"validateCommand"
|
|
93
|
+
),
|
|
94
|
+
version: lazyCommand(
|
|
95
|
+
commandMeta.version,
|
|
96
|
+
() => import("./commands/version.ts"),
|
|
97
|
+
"versionCommand"
|
|
98
|
+
),
|
|
45
99
|
},
|
|
46
100
|
});
|
|
47
101
|
|
|
48
|
-
// Load `.env`/`.env.local` before any command runs so remote content sources
|
|
49
|
-
// can read their tokens (e.g. `GITHUB_TOKEN`) during the content scan.
|
|
50
102
|
loadEnvFiles(process.cwd());
|
|
51
103
|
|
|
52
|
-
// Backstop for unexpected async failures that escape a command's own handling
|
|
53
|
-
// (e.g. a rejected timer/watcher in `blume dev`), so even those report through
|
|
54
|
-
// the stable internal-error contract rather than a bare stack trace.
|
|
55
104
|
process.on("uncaughtException", (error) => {
|
|
56
105
|
reportInternalError(error);
|
|
57
106
|
process.exit(1);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ArgsDef, CommandDef, CommandMeta } from "citty";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wrap a command so its module is imported only when citty needs its `args`
|
|
5
|
+
* or runs it. `meta` stays static (from `command-meta.ts`), so rendering root
|
|
6
|
+
* usage, matching an unknown name, and `blume <cmd> --help` for *another*
|
|
7
|
+
* command never touch this one's dependency graph.
|
|
8
|
+
*
|
|
9
|
+
* `load` imports the command's module and `key` names its export, so the call
|
|
10
|
+
* site stays a plain `() => import("./commands/x.ts")`.
|
|
11
|
+
*
|
|
12
|
+
* Only `args`, `setup`, `run`, and `cleanup` are forwarded: no Blume command
|
|
13
|
+
* declares `default`, `plugins`, or nested `subCommands`.
|
|
14
|
+
*/
|
|
15
|
+
export const lazyCommand = <Args extends ArgsDef, Key extends string>(
|
|
16
|
+
meta: CommandMeta,
|
|
17
|
+
load: () => Promise<Record<Key, CommandDef<Args>>>,
|
|
18
|
+
key: Key
|
|
19
|
+
): CommandDef<Args> => {
|
|
20
|
+
const command = async () => {
|
|
21
|
+
const module = await load();
|
|
22
|
+
return module[key];
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
args: async () => {
|
|
26
|
+
const { args } = await command();
|
|
27
|
+
// SAFETY: every Blume command passes `args` to `defineCommand` as a plain
|
|
28
|
+
// object literal (the "command registry" test checks it); citty's
|
|
29
|
+
// `Resolvable` widening is the only reason the type also admits a thunk
|
|
30
|
+
// or a promise here.
|
|
31
|
+
return args as Args;
|
|
32
|
+
},
|
|
33
|
+
cleanup: async (context) => {
|
|
34
|
+
const loaded = await command();
|
|
35
|
+
await loaded.cleanup?.(context);
|
|
36
|
+
},
|
|
37
|
+
meta,
|
|
38
|
+
run: async (context) => {
|
|
39
|
+
const loaded = await command();
|
|
40
|
+
await loaded.run?.(context);
|
|
41
|
+
},
|
|
42
|
+
setup: async (context) => {
|
|
43
|
+
const loaded = await command();
|
|
44
|
+
await loaded.setup?.(context);
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
};
|