blume 1.6.5 → 1.6.6
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 +22 -0
- package/bin/blume.mjs +3 -2
- package/dist/cli/chunk-0ewz4trd.js +679 -0
- package/dist/cli/chunk-0ewz4trd.js.map +15 -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-3k0kzs6d.js +69 -0
- package/dist/cli/chunk-3k0kzs6d.js.map +11 -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-5hs6gb7n.js +32 -0
- package/dist/cli/chunk-5hs6gb7n.js.map +10 -0
- package/dist/cli/chunk-5yvt556e.js +185 -0
- package/dist/cli/chunk-5yvt556e.js.map +11 -0
- package/dist/cli/chunk-62qsssnh.js +3808 -0
- package/dist/cli/chunk-62qsssnh.js.map +36 -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-9sh49q0h.js +30 -0
- package/dist/cli/chunk-9sh49q0h.js.map +10 -0
- package/dist/cli/chunk-aerwpe14.js +2370 -0
- package/dist/cli/chunk-aerwpe14.js.map +15 -0
- package/dist/cli/chunk-ag1zyr5x.js +176 -0
- package/dist/cli/chunk-ag1zyr5x.js.map +10 -0
- package/dist/cli/chunk-bawgnt8x.js +277 -0
- package/dist/cli/chunk-bawgnt8x.js.map +11 -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-cnvm6k3e.js +96 -0
- package/dist/cli/chunk-cnvm6k3e.js.map +10 -0
- package/dist/cli/chunk-etsqspj6.js +5170 -0
- package/dist/cli/chunk-etsqspj6.js.map +47 -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-f75cqye8.js +76 -0
- package/dist/cli/chunk-f75cqye8.js.map +10 -0
- package/dist/cli/chunk-j00ezcg5.js +259 -0
- package/dist/cli/chunk-j00ezcg5.js.map +11 -0
- package/dist/cli/chunk-jtb45atp.js +467 -0
- package/dist/cli/chunk-jtb45atp.js.map +14 -0
- package/dist/cli/chunk-m3p3wahd.js +117 -0
- package/dist/cli/chunk-m3p3wahd.js.map +10 -0
- package/dist/cli/chunk-n0y172hf.js +387 -0
- package/dist/cli/chunk-n0y172hf.js.map +12 -0
- package/dist/cli/chunk-n4qjabmt.js +1062 -0
- package/dist/cli/chunk-n4qjabmt.js.map +25 -0
- package/dist/cli/chunk-nyqzjdhj.js +111 -0
- package/dist/cli/chunk-nyqzjdhj.js.map +11 -0
- package/dist/cli/chunk-pxj10x8y.js +35 -0
- package/dist/cli/chunk-pxj10x8y.js.map +10 -0
- package/dist/cli/chunk-s4jn7f1q.js +54 -0
- package/dist/cli/chunk-s4jn7f1q.js.map +10 -0
- package/dist/cli/chunk-s4k1pnvf.js +81 -0
- package/dist/cli/chunk-s4k1pnvf.js.map +10 -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-tc89yh2r.js +136 -0
- package/dist/cli/chunk-tc89yh2r.js.map +10 -0
- package/dist/cli/chunk-vt8fgygt.js +23 -0
- package/dist/cli/chunk-vt8fgygt.js.map +10 -0
- package/dist/cli/chunk-vv237fp3.js +1002 -0
- package/dist/cli/chunk-vv237fp3.js.map +13 -0
- package/dist/cli/chunk-vv3f8mb6.js +5314 -0
- package/dist/cli/chunk-vv3f8mb6.js.map +58 -0
- package/dist/cli/chunk-vxv4x1n8.js +17 -0
- package/dist/cli/chunk-vxv4x1n8.js.map +10 -0
- package/dist/cli/chunk-wb067mv3.js +758 -0
- package/dist/cli/chunk-wb067mv3.js.map +13 -0
- package/dist/cli/chunk-wd27zjcz.js +60 -0
- package/dist/cli/chunk-wd27zjcz.js.map +10 -0
- package/dist/cli/chunk-wkq5tbtq.js +1141 -0
- package/dist/cli/chunk-wkq5tbtq.js.map +19 -0
- package/dist/cli/chunk-x1vrdjyk.js +1967 -0
- package/dist/cli/chunk-x1vrdjyk.js.map +34 -0
- package/dist/cli/chunk-x66c5yjn.js +23 -0
- package/dist/cli/chunk-x66c5yjn.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/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/docs/02-deployment.mdx +1 -1
- package/docs/configuration/ask-ai.mdx +1 -1
- package/docs/configuration/customization.mdx +2 -9
- 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 +20 -23
- 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 +269 -53
- package/src/astro/module-types.ts +74 -0
- package/src/astro/templates.ts +85 -97
- 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 +42 -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/content/GithubInfo.astro +4 -1
- package/src/components/layout/PageLayout.astro +14 -3
- package/src/components/layout/ReferenceLayout.astro +14 -4
- package/src/components/layout/RootLayout.astro +14 -4
- 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/og/card.ts +6 -12
- package/src/openapi/render-mdx.ts +9 -5
- package/src/registry/eject.ts +0 -2
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
parsePort
|
|
4
|
+
} from "./chunk-x66c5yjn.js";
|
|
5
|
+
import {
|
|
6
|
+
normalizeHost
|
|
7
|
+
} from "./chunk-bcy492zc.js";
|
|
8
|
+
import {
|
|
9
|
+
prepareProject
|
|
10
|
+
} from "./chunk-nyqzjdhj.js";
|
|
11
|
+
import"./chunk-9sh49q0h.js";
|
|
12
|
+
import"./chunk-btfr9yvw.js";
|
|
13
|
+
import {
|
|
14
|
+
DevLockHeldError,
|
|
15
|
+
acquireDevLock,
|
|
16
|
+
describeDevLock,
|
|
17
|
+
updateDevLockPort
|
|
18
|
+
} from "./chunk-tc89yh2r.js";
|
|
19
|
+
import {
|
|
20
|
+
generateRuntime,
|
|
21
|
+
refreshBlumeContent,
|
|
22
|
+
showBlumeErrorOverlay
|
|
23
|
+
} from "./chunk-62qsssnh.js";
|
|
24
|
+
import"./chunk-n4qjabmt.js";
|
|
25
|
+
import"./chunk-pxj10x8y.js";
|
|
26
|
+
import"./chunk-sbdqrjbb.js";
|
|
27
|
+
import"./chunk-5yvt556e.js";
|
|
28
|
+
import"./chunk-ev67ycx0.js";
|
|
29
|
+
import"./chunk-vt8fgygt.js";
|
|
30
|
+
import"./chunk-3r94j3tc.js";
|
|
31
|
+
import"./chunk-aerwpe14.js";
|
|
32
|
+
import"./chunk-3k0kzs6d.js";
|
|
33
|
+
import"./chunk-vxv4x1n8.js";
|
|
34
|
+
import"./chunk-wd27zjcz.js";
|
|
35
|
+
import {
|
|
36
|
+
scanProject
|
|
37
|
+
} from "./chunk-etsqspj6.js";
|
|
38
|
+
import {
|
|
39
|
+
resolveRuntimeDir
|
|
40
|
+
} from "./chunk-vv3f8mb6.js";
|
|
41
|
+
import"./chunk-27gtm2ym.js";
|
|
42
|
+
import"./chunk-4xyggvgf.js";
|
|
43
|
+
import"./chunk-6kzzpsx8.js";
|
|
44
|
+
import {
|
|
45
|
+
logger,
|
|
46
|
+
reportDiagnostics
|
|
47
|
+
} from "./chunk-ey89bjj1.js";
|
|
48
|
+
import {
|
|
49
|
+
commandMeta
|
|
50
|
+
} from "./chunk-2aj8ddew.js";
|
|
51
|
+
|
|
52
|
+
// src/cli/commands/dev.ts
|
|
53
|
+
import { dev } from "astro";
|
|
54
|
+
import { watch } from "chokidar";
|
|
55
|
+
import { defineCommand } from "citty";
|
|
56
|
+
import { debounce } from "perfect-debounce";
|
|
57
|
+
var routeSignature = (routes) => routes.map((route) => `${route.path} ${route.entryId}`).toSorted().join(`
|
|
58
|
+
`);
|
|
59
|
+
var devCommand = defineCommand({
|
|
60
|
+
args: {
|
|
61
|
+
"content-dir": {
|
|
62
|
+
description: "Content folder to scan, overriding config (content.root).",
|
|
63
|
+
type: "string"
|
|
64
|
+
},
|
|
65
|
+
debug: {
|
|
66
|
+
description: "Verbose Astro/Vite logging for troubleshooting.",
|
|
67
|
+
type: "boolean"
|
|
68
|
+
},
|
|
69
|
+
host: { description: "Network host to bind.", type: "string" },
|
|
70
|
+
open: { description: "Open the browser on start.", type: "boolean" },
|
|
71
|
+
port: { description: "Port to listen on.", type: "string" },
|
|
72
|
+
preview: {
|
|
73
|
+
description: "Include drafts and unpublished CMS content.",
|
|
74
|
+
type: "boolean"
|
|
75
|
+
},
|
|
76
|
+
strict: { description: "Fail on diagnostics.", type: "boolean" }
|
|
77
|
+
},
|
|
78
|
+
meta: commandMeta.dev,
|
|
79
|
+
async run({ args }) {
|
|
80
|
+
const root = process.cwd();
|
|
81
|
+
const preview = args.preview ?? false;
|
|
82
|
+
const overrides = args["content-dir"] ? { contentRoot: args["content-dir"] } : undefined;
|
|
83
|
+
const explicitPort = parsePort(args.port);
|
|
84
|
+
const port = explicitPort ?? 4321;
|
|
85
|
+
let devServerUrl = `http://localhost:${port}`;
|
|
86
|
+
const outDir = resolveRuntimeDir(root);
|
|
87
|
+
let releaseLock;
|
|
88
|
+
try {
|
|
89
|
+
releaseLock = acquireDevLock(outDir, port);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error instanceof DevLockHeldError) {
|
|
92
|
+
logger.error(`A \`blume dev\` server is already running${describeDevLock(error.lock)} in this project. Reuse that server instead of starting a second one — two dev servers would corrupt the shared .blume dir. If it crashed, delete .blume/dev.lock.`);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
process.on("exit", releaseLock);
|
|
98
|
+
const project = await prepareProject({
|
|
99
|
+
devServerUrl,
|
|
100
|
+
mode: "dev",
|
|
101
|
+
overrides,
|
|
102
|
+
preview,
|
|
103
|
+
root,
|
|
104
|
+
strict: args.strict
|
|
105
|
+
});
|
|
106
|
+
const createServer = (listenPort, open) => dev({
|
|
107
|
+
logLevel: args.debug ? "debug" : "info",
|
|
108
|
+
root: project.context.outDir,
|
|
109
|
+
server: { host: normalizeHost(args.host), open, port: listenPort }
|
|
110
|
+
});
|
|
111
|
+
let server = await createServer(explicitPort, args.open ?? false);
|
|
112
|
+
const boundPort = server.address.port;
|
|
113
|
+
if (boundPort !== port) {
|
|
114
|
+
updateDevLockPort(outDir, boundPort);
|
|
115
|
+
devServerUrl = `http://localhost:${boundPort}`;
|
|
116
|
+
}
|
|
117
|
+
showBlumeErrorOverlay(project.diagnostics);
|
|
118
|
+
let lastSignature = routeSignature(project.manifest.routes);
|
|
119
|
+
const regenerate = debounce(async () => {
|
|
120
|
+
try {
|
|
121
|
+
const next = await scanProject(root, {
|
|
122
|
+
devServerUrl,
|
|
123
|
+
mode: "dev",
|
|
124
|
+
overrides,
|
|
125
|
+
preview
|
|
126
|
+
});
|
|
127
|
+
const nextSignature = routeSignature(next.manifest.routes);
|
|
128
|
+
const structural = nextSignature !== lastSignature;
|
|
129
|
+
await generateRuntime(next);
|
|
130
|
+
if (structural && !await refreshBlumeContent()) {
|
|
131
|
+
await server.stop();
|
|
132
|
+
server = await createServer(boundPort, false);
|
|
133
|
+
}
|
|
134
|
+
lastSignature = nextSignature;
|
|
135
|
+
reportDiagnostics(next.diagnostics, root);
|
|
136
|
+
showBlumeErrorOverlay(next.diagnostics);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
logger.error(`Regeneration failed: ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
}, 80);
|
|
141
|
+
if (boundPort !== port) {
|
|
142
|
+
regenerate();
|
|
143
|
+
}
|
|
144
|
+
const dirTargets = [project.context.pagesRoot].filter((target) => target !== null);
|
|
145
|
+
const fileTargets = [
|
|
146
|
+
project.context.configFile,
|
|
147
|
+
project.context.themeFile,
|
|
148
|
+
project.context.componentsFile
|
|
149
|
+
].filter((target) => target !== null);
|
|
150
|
+
const projectWatcher = watch([...dirTargets, ...fileTargets], {
|
|
151
|
+
ignoreInitial: true
|
|
152
|
+
}).on("all", regenerate);
|
|
153
|
+
const disposers = [
|
|
154
|
+
...project.sources.map((source) => source.watch?.(regenerate)),
|
|
155
|
+
() => {
|
|
156
|
+
projectWatcher.close();
|
|
157
|
+
}
|
|
158
|
+
].filter((dispose) => dispose !== undefined);
|
|
159
|
+
const shutdown = async () => {
|
|
160
|
+
for (const dispose of disposers) {
|
|
161
|
+
dispose();
|
|
162
|
+
}
|
|
163
|
+
releaseLock();
|
|
164
|
+
await server.stop();
|
|
165
|
+
process.exit(0);
|
|
166
|
+
};
|
|
167
|
+
process.on("SIGINT", shutdown);
|
|
168
|
+
process.on("SIGTERM", shutdown);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
export {
|
|
172
|
+
devCommand
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
//# debugId=EE16C622AD2E2BB164756E2164756E21
|
|
176
|
+
//# sourceMappingURL=chunk-ag1zyr5x.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli/commands/dev.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { dev } from \"astro\";\nimport { watch } from \"chokidar\";\nimport { defineCommand } from \"citty\";\nimport { debounce } from \"perfect-debounce\";\n\nimport { generateRuntime } from \"../../astro/generate.ts\";\nimport {\n refreshBlumeContent,\n showBlumeErrorOverlay,\n} from \"../../astro/integration.ts\";\nimport { scanProject } from \"../../core/project-graph.ts\";\nimport { resolveRuntimeDir } from \"../../core/project.ts\";\nimport { parsePort } from \"../args.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\nimport {\n acquireDevLock,\n describeDevLock,\n DevLockHeldError,\n updateDevLockPort,\n} from \"../dev-lock.ts\";\nimport { normalizeHost } from \"../host-args.ts\";\nimport { logger, reportDiagnostics } from \"../log.ts\";\nimport { prepareProject } from \"../prepare.ts\";\n\n/**\n * A fingerprint of the route set: the sorted `path entryId` pairs. It changes\n * when a page is added, removed, or renamed (a folder rename shifts many at\n * once) but stays equal across pure body edits — so the dev loop can tell a\n * \"structural\" change (needs a content re-sync) from a hot-reloadable one.\n */\nconst routeSignature = (\n routes: readonly { entryId: string; path: string }[]\n): string =>\n routes\n .map((route) => `${route.path} ${route.entryId}`)\n .toSorted()\n .join(\"\\n\");\n\nexport const devCommand = defineCommand({\n args: {\n \"content-dir\": {\n description: \"Content folder to scan, overriding config (content.root).\",\n type: \"string\",\n },\n debug: {\n description: \"Verbose Astro/Vite logging for troubleshooting.\",\n type: \"boolean\",\n },\n host: { description: \"Network host to bind.\", type: \"string\" },\n open: { description: \"Open the browser on start.\", type: \"boolean\" },\n port: { description: \"Port to listen on.\", type: \"string\" },\n preview: {\n description: \"Include drafts and unpublished CMS content.\",\n type: \"boolean\",\n },\n strict: { description: \"Fail on diagnostics.\", type: \"boolean\" },\n },\n meta: commandMeta.dev,\n async run({ args }) {\n const root = process.cwd();\n const preview = args.preview ?? false;\n const overrides = args[\"content-dir\"]\n ? { contentRoot: args[\"content-dir\"] }\n : undefined;\n // Astro's dev server defaults to 4321 when no port is passed. Feeding the\n // resolved URL in as the `deployment.site` fallback lets site-gated features\n // (OG images, canonicals, sitemap) work locally without configuring a site.\n const explicitPort = parsePort(args.port);\n const port = explicitPort ?? 4321;\n let devServerUrl = `http://localhost:${port}`;\n\n // Claim the shared `.blume` dir BEFORE preparing: `prepareProject`\n // regenerates the runtime, so even a refused second dev server would\n // otherwise clobber the running one's generated tree (with this\n // invocation's port baked in) on its way out. The claim is atomic, so two\n // simultaneous starts can't both win. Dev never relocates the runtime dir,\n // so the lock always lives at `<root>/.blume`.\n const outDir = resolveRuntimeDir(root);\n let releaseLock: () => void;\n try {\n releaseLock = acquireDevLock(outDir, port);\n } catch (error) {\n if (error instanceof DevLockHeldError) {\n logger.error(\n `A \\`blume dev\\` server is already running${describeDevLock(error.lock)} in this project. Reuse that server instead of starting a second one — two dev servers would corrupt the shared .blume dir. If it crashed, delete .blume/dev.lock.`\n );\n process.exit(1);\n }\n throw error;\n }\n process.on(\"exit\", releaseLock);\n\n const project = await prepareProject({\n devServerUrl,\n mode: \"dev\",\n overrides,\n preview,\n root,\n strict: args.strict,\n });\n\n // A factory so the regenerate loop can recreate the server when a\n // structural (route-set) change can't be re-synced in place (see below).\n // `open` is honored on first boot only — a restart must not reopen the\n // browser.\n const createServer = (listenPort: number | undefined, open: boolean) =>\n dev({\n logLevel: args.debug ? \"debug\" : \"info\",\n root: project.context.outDir,\n server: { host: normalizeHost(args.host), open, port: listenPort },\n });\n\n let server = await createServer(explicitPort, args.open ?? false);\n\n // Vite bumps to the next free port when the default is taken, so record\n // the port the server actually bound — the lock's URL is what a refused\n // second invocation tells its caller to reuse. The site fallback baked\n // into the runtime also carries the port, so it must follow suit (below,\n // once the regeneration closure exists).\n const boundPort = server.address.port;\n if (boundPort !== port) {\n updateDevLockPort(outDir, boundPort);\n devServerUrl = `http://localhost:${boundPort}`;\n }\n\n // Mirror any initial diagnostics into the browser overlay now the server\n // (and its HMR channel) is up.\n showBlumeErrorOverlay(project.diagnostics);\n\n let lastSignature = routeSignature(project.manifest.routes);\n\n // Watch user inputs and regenerate the runtime data on change. A body edit\n // hot-reloads via Vite (fast path). A route-set change also needs Astro's\n // content store re-synced: its glob watcher misses directory renames, so a\n // renamed page would 404 (`getEntry` reads a stale store). Astro hands the\n // integration `refreshContent` for exactly that — a full loader run\n // against the live server, after which Astro's own store watcher clears\n // the route cache and reloads the browser. Only a server that registered\n // no refresh (none since Astro 5) falls back to a cold restart: stop,\n // then bring up a fresh container whose cold sync re-globs everything.\n // perfect-debounce both debounces the watch burst (80ms) and\n // single-flights the scan: a\n // trigger during a run never starts a second run, only marks one trailing\n // rerun after the current settles. Both halves are load-bearing — a plain\n // debounce once let bursts stack overlapping scans until the heap was\n // exhausted (observed as an OOM after minutes of looping). The contract is\n // pinned by test/dev-debounce.test.ts. The task must not reject (the\n // library re-invokes it from an unhandled .finally), so the body catches\n // its own errors and always resolves.\n const regenerate = debounce(async () => {\n try {\n const next = await scanProject(root, {\n devServerUrl,\n mode: \"dev\",\n overrides,\n preview,\n });\n const nextSignature = routeSignature(next.manifest.routes);\n const structural = nextSignature !== lastSignature;\n // Generate first: the new runtime data (and any staged remote content)\n // is on disk and published before the store re-syncs against it.\n await generateRuntime(next);\n if (structural && !(await refreshBlumeContent())) {\n await server.stop();\n server = await createServer(boundPort, false);\n }\n // Commit the signature only after the (re)generation succeeded. If the\n // re-sync or restart above throws mid-sequence, the signature stays\n // stale so the next watch event retries the structural path.\n lastSignature = nextSignature;\n // Surface any content/config errors in the terminal AND the browser\n // overlay. The terminal report is not redundant: the overlay only\n // shows once the browser has connected, and it clears on the next HMR\n // update.\n reportDiagnostics(next.diagnostics, root);\n showBlumeErrorOverlay(next.diagnostics);\n } catch (error) {\n // SAFETY: regeneration failures come from the generator and Astro's\n // server API, which raise Error instances; only the message is shown.\n logger.error(`Regeneration failed: ${(error as Error).message}`);\n }\n }, 80);\n\n // The runtime prepared above baked the *requested* port into the site\n // fallback; if Vite bumped it, regenerate so OG images, canonicals, and\n // other site-gated URLs point at the port actually serving.\n if (boundPort !== port) {\n void regenerate();\n }\n\n // Content is watched per source (filesystem uses fs.watch; remote sources\n // are frozen for the session). The remaining project inputs — user pages,\n // config, theme, and component overrides — are watched directly.\n const dirTargets = [project.context.pagesRoot].filter(\n (target) => target !== null\n );\n const fileTargets = [\n project.context.configFile,\n project.context.themeFile,\n project.context.componentsFile,\n ].filter((target) => target !== null);\n\n // chokidar handles what raw fs.watch made us hand-roll: recursive\n // directory watching on every platform, and single files surviving a\n // rename-replace save (vim and most \"atomic save\" editors), which orphans\n // an inode-tracking fs.watch watcher after the first write.\n const projectWatcher = watch([...dirTargets, ...fileTargets], {\n ignoreInitial: true,\n }).on(\"all\", regenerate);\n const disposers = [\n ...project.sources.map((source) => source.watch?.(regenerate)),\n () => {\n void projectWatcher.close();\n },\n ].filter((dispose) => dispose !== undefined);\n\n const shutdown = async () => {\n for (const dispose of disposers) {\n dispose();\n }\n releaseLock();\n await server.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n },\n});\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "EE16C622AD2E2BB164756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
reportInternalError
|
|
4
|
+
} from "./chunk-btfr9yvw.js";
|
|
5
|
+
import {
|
|
6
|
+
writeTextAtomic
|
|
7
|
+
} from "./chunk-ev67ycx0.js";
|
|
8
|
+
import {
|
|
9
|
+
VERSION_LIKE,
|
|
10
|
+
isWithin,
|
|
11
|
+
localizeRoute,
|
|
12
|
+
nextFenceState,
|
|
13
|
+
scanProject,
|
|
14
|
+
versionizeRoute
|
|
15
|
+
} from "./chunk-etsqspj6.js";
|
|
16
|
+
import {
|
|
17
|
+
VERSION_ID,
|
|
18
|
+
loadConfig
|
|
19
|
+
} from "./chunk-vv3f8mb6.js";
|
|
20
|
+
import {
|
|
21
|
+
stripBasePath
|
|
22
|
+
} from "./chunk-27gtm2ym.js";
|
|
23
|
+
import"./chunk-4xyggvgf.js";
|
|
24
|
+
import"./chunk-6kzzpsx8.js";
|
|
25
|
+
import {
|
|
26
|
+
BlumeError,
|
|
27
|
+
logger
|
|
28
|
+
} from "./chunk-ey89bjj1.js";
|
|
29
|
+
import {
|
|
30
|
+
commandMeta
|
|
31
|
+
} from "./chunk-2aj8ddew.js";
|
|
32
|
+
|
|
33
|
+
// src/cli/commands/version.ts
|
|
34
|
+
import { defineCommand } from "citty";
|
|
35
|
+
|
|
36
|
+
// src/core/version-cut.ts
|
|
37
|
+
import { existsSync } from "node:fs";
|
|
38
|
+
import { cp, readdir, readFile, rm } from "node:fs/promises";
|
|
39
|
+
import { join, relative } from "pathe";
|
|
40
|
+
class CutError extends Error {
|
|
41
|
+
constructor(message) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.name = "CutError";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
var INLINE_CODE = /`[^`]*`/gu;
|
|
47
|
+
var ROOT_LINK = /(?<prefix>\]\(|href="|src=")(?<target>\/[^\s"')]*)/gu;
|
|
48
|
+
var buildRouteRewrites = (project, id) => {
|
|
49
|
+
const { basePath, i18n } = project.config;
|
|
50
|
+
const { contentRoot } = project.context;
|
|
51
|
+
const rewrites = new Map;
|
|
52
|
+
for (const page of project.graph.pages) {
|
|
53
|
+
const { sourcePath } = page;
|
|
54
|
+
if (page.version !== "" || page.collection === "staged" || !sourcePath || relative(contentRoot, sourcePath).startsWith("..")) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const logical = versionizeRoute(page.versionKey, id);
|
|
58
|
+
rewrites.set(stripBasePath(basePath, page.route), i18n ? localizeRoute(logical, page.locale, i18n) : logical);
|
|
59
|
+
}
|
|
60
|
+
return rewrites;
|
|
61
|
+
};
|
|
62
|
+
var rewriteLine = (line, rewrites) => {
|
|
63
|
+
const masked = line.replaceAll(INLINE_CODE, (span) => " ".repeat(span.length));
|
|
64
|
+
let out = "";
|
|
65
|
+
let cursor = 0;
|
|
66
|
+
let count = 0;
|
|
67
|
+
for (const match of masked.matchAll(ROOT_LINK)) {
|
|
68
|
+
const target = match.groups?.target ?? "";
|
|
69
|
+
const hash = target.search(/[#?]/u);
|
|
70
|
+
const path = hash === -1 ? target : target.slice(0, hash);
|
|
71
|
+
const suffix = hash === -1 ? "" : target.slice(hash);
|
|
72
|
+
const bare = path !== "/" && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
73
|
+
const replacement = rewrites.get(bare);
|
|
74
|
+
if (replacement === undefined) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const offset = (match.index ?? 0) + (match.groups?.prefix?.length ?? 0);
|
|
78
|
+
out += line.slice(cursor, offset) + replacement + suffix;
|
|
79
|
+
cursor = offset + target.length;
|
|
80
|
+
count += 1;
|
|
81
|
+
}
|
|
82
|
+
return count === 0 ? line : out + line.slice(cursor);
|
|
83
|
+
};
|
|
84
|
+
var rewriteSnapshotLinks = (source, rewrites) => {
|
|
85
|
+
let fence = null;
|
|
86
|
+
let count = 0;
|
|
87
|
+
const lines = source.split(`
|
|
88
|
+
`).map((line) => {
|
|
89
|
+
const next = nextFenceState(line, fence);
|
|
90
|
+
const inFence = fence !== null || next !== null;
|
|
91
|
+
fence = next;
|
|
92
|
+
if (inFence) {
|
|
93
|
+
return line;
|
|
94
|
+
}
|
|
95
|
+
const rewrittenLine = rewriteLine(line, rewrites);
|
|
96
|
+
if (rewrittenLine !== line) {
|
|
97
|
+
count += 1;
|
|
98
|
+
}
|
|
99
|
+
return rewrittenLine;
|
|
100
|
+
});
|
|
101
|
+
return { count, text: lines.join(`
|
|
102
|
+
`) };
|
|
103
|
+
};
|
|
104
|
+
var snippetFor = (id, hasVersions) => hasVersions ? `Add to versions.archived in blume.config.ts (newest first):
|
|
105
|
+
|
|
106
|
+
{ id: "${id}" },
|
|
107
|
+
` : `Add to blume.config.ts:
|
|
108
|
+
|
|
109
|
+
versions: {
|
|
110
|
+
archived: [{ id: "${id}" }],
|
|
111
|
+
current: { label: "…" },
|
|
112
|
+
},
|
|
113
|
+
`;
|
|
114
|
+
var insertArchivedVersion = async (configPath, id) => {
|
|
115
|
+
let text;
|
|
116
|
+
try {
|
|
117
|
+
text = await readFile(configPath, "utf-8");
|
|
118
|
+
} catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
const match = text.match(/(?<lead>archived:\s*\[)/u);
|
|
122
|
+
if (!match || match.index === undefined) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const insertAt = match.index + (match.groups?.lead?.length ?? 0);
|
|
126
|
+
const lineStart = text.lastIndexOf(`
|
|
127
|
+
`, match.index) + 1;
|
|
128
|
+
const indent = text.slice(lineStart).match(/^\s*/u)?.[0] ?? "";
|
|
129
|
+
const rest = text.slice(insertAt);
|
|
130
|
+
const eol = /^\r?\n/u.exec(rest)?.[0];
|
|
131
|
+
let entry;
|
|
132
|
+
if (rest.trimStart().startsWith("]")) {
|
|
133
|
+
entry = `{ id: "${id}" }`;
|
|
134
|
+
} else if (eol) {
|
|
135
|
+
entry = `${eol}${indent} { id: "${id}" },`;
|
|
136
|
+
} else {
|
|
137
|
+
entry = `{ id: "${id}" }, `;
|
|
138
|
+
}
|
|
139
|
+
await writeTextAtomic(configPath, text.slice(0, insertAt) + entry + rest);
|
|
140
|
+
return true;
|
|
141
|
+
};
|
|
142
|
+
var cutVersion = async (root, id, options = {}) => {
|
|
143
|
+
if (!VERSION_ID.test(id)) {
|
|
144
|
+
throw new CutError(`Version ids must start with a letter (e.g. "v1.0") and contain only letters, digits, dots, hyphens, and underscores — got "${id}".`);
|
|
145
|
+
}
|
|
146
|
+
const project = await scanProject(root);
|
|
147
|
+
const errors = project.diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
148
|
+
if (errors.length > 0) {
|
|
149
|
+
throw new CutError(`The project has ${errors.length} error diagnostic(s) — fix them before cutting a version (run \`blume validate\`).`);
|
|
150
|
+
}
|
|
151
|
+
const { contentRoot } = project.context;
|
|
152
|
+
if (project.config.versions?.archived.some((version) => version.id === id)) {
|
|
153
|
+
throw new CutError(`Version "${id}" is already registered in versions.archived.`);
|
|
154
|
+
}
|
|
155
|
+
const dir = join(contentRoot, id);
|
|
156
|
+
if (existsSync(dir)) {
|
|
157
|
+
if (!options.force) {
|
|
158
|
+
throw new CutError(`${dir} already exists — pass --force to overwrite the snapshot.`);
|
|
159
|
+
}
|
|
160
|
+
await rm(dir, { force: true, recursive: true });
|
|
161
|
+
}
|
|
162
|
+
const excluded = new Set([
|
|
163
|
+
id,
|
|
164
|
+
"node_modules",
|
|
165
|
+
...project.config.versions?.archived.map((version) => version.id) ?? []
|
|
166
|
+
]);
|
|
167
|
+
const foreignRoots = project.sources.flatMap((source) => source.staged && source.contentRoot ? [source.contentRoot] : []).filter((tree) => isWithin(contentRoot, tree));
|
|
168
|
+
const isForeign = (path) => foreignRoots.some((tree) => isWithin(tree, path));
|
|
169
|
+
const entries = await readdir(contentRoot, { withFileTypes: true });
|
|
170
|
+
let copied = 0;
|
|
171
|
+
await Promise.all(entries.map(async (entry) => {
|
|
172
|
+
if (excluded.has(entry.name) || entry.name.startsWith(".") || entry.isDirectory() && VERSION_LIKE.test(entry.name)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
await cp(join(contentRoot, entry.name), join(dir, entry.name), {
|
|
176
|
+
filter: (source) => !isForeign(source),
|
|
177
|
+
recursive: true
|
|
178
|
+
});
|
|
179
|
+
}));
|
|
180
|
+
const rewrites = buildRouteRewrites(project, id);
|
|
181
|
+
const rewritten = [];
|
|
182
|
+
const walk = async (current) => {
|
|
183
|
+
const children = await readdir(current, { withFileTypes: true });
|
|
184
|
+
await Promise.all(children.map(async (child) => {
|
|
185
|
+
const abs = join(current, child.name);
|
|
186
|
+
if (child.isDirectory()) {
|
|
187
|
+
await walk(abs);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
copied += 1;
|
|
191
|
+
if (!/\.mdx?$/u.test(child.name)) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const source = await readFile(abs, "utf-8");
|
|
195
|
+
const { text, count } = rewriteSnapshotLinks(source, rewrites);
|
|
196
|
+
if (count > 0) {
|
|
197
|
+
await writeTextAtomic(abs, text);
|
|
198
|
+
rewritten.push({ count, file: relative(dir, abs) });
|
|
199
|
+
}
|
|
200
|
+
}));
|
|
201
|
+
};
|
|
202
|
+
await walk(dir);
|
|
203
|
+
const configPath = join(root, "blume.config.ts");
|
|
204
|
+
const configUpdated = project.config.versions !== undefined && await insertArchivedVersion(configPath, id);
|
|
205
|
+
return {
|
|
206
|
+
configSnippet: configUpdated ? null : snippetFor(id, project.config.versions !== undefined),
|
|
207
|
+
configUpdated,
|
|
208
|
+
copied,
|
|
209
|
+
dir,
|
|
210
|
+
rewritten
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/cli/commands/version.ts
|
|
215
|
+
var versionCommand = defineCommand({
|
|
216
|
+
args: {
|
|
217
|
+
force: {
|
|
218
|
+
description: "Overwrite an existing snapshot directory.",
|
|
219
|
+
type: "boolean"
|
|
220
|
+
},
|
|
221
|
+
id: {
|
|
222
|
+
description: 'Version id to cut (e.g. "v1.0").',
|
|
223
|
+
required: false,
|
|
224
|
+
type: "positional"
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
meta: commandMeta.version,
|
|
228
|
+
async run({ args }) {
|
|
229
|
+
const root = process.cwd();
|
|
230
|
+
if (!args.id) {
|
|
231
|
+
const { config } = await loadConfig(root);
|
|
232
|
+
if (!config.versions) {
|
|
233
|
+
logger.info("Versioning is not configured. Cut the first version with `blume version <id>` (e.g. `blume version v1.0`).");
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const { current, archived } = config.versions;
|
|
237
|
+
process.stdout.write(` ${current.label} (current)${current.badge ? ` — ${current.badge}` : ""}
|
|
238
|
+
`);
|
|
239
|
+
for (const version of archived) {
|
|
240
|
+
process.stdout.write(` ${version.label ?? version.id} — ${version.id}/
|
|
241
|
+
`);
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
const result = await cutVersion(root, args.id, { force: args.force });
|
|
247
|
+
logger.success(`Snapshot ${result.dir} (${result.copied} file(s) copied)`);
|
|
248
|
+
const totalRewrites = result.rewritten.reduce((sum, entry) => sum + entry.count, 0);
|
|
249
|
+
if (totalRewrites > 0) {
|
|
250
|
+
logger.info(`Rewrote root-absolute links in ${result.rewritten.length} page(s) (${totalRewrites} line(s)).`);
|
|
251
|
+
}
|
|
252
|
+
if (result.configUpdated) {
|
|
253
|
+
logger.success(`Added "${args.id}" to versions.archived in blume.config.ts`);
|
|
254
|
+
} else if (result.configSnippet) {
|
|
255
|
+
logger.info(result.configSnippet);
|
|
256
|
+
}
|
|
257
|
+
logger.info("Archived versions are frozen — future edits belong in the live tree. Restart `blume dev` to pick up the snapshot.");
|
|
258
|
+
} catch (error) {
|
|
259
|
+
if (error instanceof CutError) {
|
|
260
|
+
logger.error(error.message);
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
if (error instanceof BlumeError) {
|
|
264
|
+
logger.error(error.diagnostic.message);
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
reportInternalError(error);
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
export {
|
|
273
|
+
versionCommand
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
//# debugId=D706C6D39089651F64756E2164756E21
|
|
277
|
+
//# sourceMappingURL=chunk-bawgnt8x.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli/commands/version.ts", "../src/core/version-cut.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { defineCommand } from \"citty\";\n\nimport { loadConfig } from \"../../core/config.ts\";\nimport { BlumeError } from \"../../core/diagnostics.ts\";\nimport { CutError, cutVersion } from \"../../core/version-cut.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\nimport { reportInternalError } from \"../internal-error.ts\";\nimport { logger } from \"../log.ts\";\n\nexport const versionCommand = defineCommand({\n args: {\n force: {\n description: \"Overwrite an existing snapshot directory.\",\n type: \"boolean\",\n },\n id: {\n description: 'Version id to cut (e.g. \"v1.0\").',\n required: false,\n type: \"positional\",\n },\n },\n meta: commandMeta.version,\n async run({ args }) {\n const root = process.cwd();\n\n if (!args.id) {\n const { config } = await loadConfig(root);\n if (!config.versions) {\n logger.info(\n \"Versioning is not configured. Cut the first version with `blume version <id>` (e.g. `blume version v1.0`).\"\n );\n return;\n }\n const { current, archived } = config.versions;\n process.stdout.write(\n ` ${current.label} (current)${current.badge ? ` — ${current.badge}` : \"\"}\\n`\n );\n for (const version of archived) {\n process.stdout.write(\n ` ${version.label ?? version.id} — ${version.id}/\\n`\n );\n }\n return;\n }\n\n try {\n const result = await cutVersion(root, args.id, { force: args.force });\n logger.success(\n `Snapshot ${result.dir} (${result.copied} file(s) copied)`\n );\n const totalRewrites = result.rewritten.reduce(\n (sum, entry) => sum + entry.count,\n 0\n );\n if (totalRewrites > 0) {\n logger.info(\n `Rewrote root-absolute links in ${result.rewritten.length} page(s) (${totalRewrites} line(s)).`\n );\n }\n if (result.configUpdated) {\n logger.success(\n `Added \"${args.id}\" to versions.archived in blume.config.ts`\n );\n } else if (result.configSnippet) {\n logger.info(result.configSnippet);\n }\n logger.info(\n \"Archived versions are frozen — future edits belong in the live tree. Restart `blume dev` to pick up the snapshot.\"\n );\n } catch (error) {\n if (error instanceof CutError) {\n logger.error(error.message);\n process.exit(1);\n }\n if (error instanceof BlumeError) {\n logger.error(error.diagnostic.message);\n process.exit(1);\n }\n reportInternalError(error);\n process.exit(1);\n }\n },\n});\n",
|
|
6
|
+
"import { existsSync } from \"node:fs\";\nimport { cp, readdir, readFile, rm } from \"node:fs/promises\";\n\nimport { join, relative } from \"pathe\";\n\nimport { stripBasePath } from \"./base-path.ts\";\nimport { nextFenceState } from \"./code-fences.ts\";\nimport type { FenceState } from \"./code-fences.ts\";\nimport { writeTextAtomic } from \"./fs-atomic.ts\";\nimport { localizeRoute } from \"./i18n.ts\";\nimport type { BlumeProject } from \"./project-graph.ts\";\nimport { isWithin, scanProject } from \"./project-graph.ts\";\nimport { VERSION_ID } from \"./schema.ts\";\nimport { VERSION_LIKE, versionizeRoute } from \"./versions.ts\";\n\n/** What `cutVersion` did, for the CLI to report. */\nexport interface CutResult {\n /** Files copied into the snapshot. */\n copied: number;\n /** Markdown pages whose root-absolute links were rewritten, with counts. */\n rewritten: { file: string; count: number }[];\n /** Whether `blume.config.ts` was updated in place. */\n configUpdated: boolean;\n /** Ready-to-paste config snippet when in-place update was not possible. */\n configSnippet: string | null;\n /** Absolute path of the created snapshot directory. */\n dir: string;\n}\n\n/** A `cutVersion` failure the CLI reports as a user error, not a crash. */\nexport class CutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CutError\";\n }\n}\n\n// Inline code spans are syntax being *shown*, not links — blank them before\n// matching so replacements can splice into the real line by index (the mask\n// preserves length). Mirrors the content-assets rewriter.\nconst INLINE_CODE = /`[^`]*`/gu;\n// A markdown link/image target or an HTML href/src attribute whose value is\n// root-absolute. Named groups carry the prefix (kept) and target (rewritten).\nconst ROOT_LINK = /(?<prefix>\\]\\(|href=\"|src=\")(?<target>\\/[^\\s\"')]*)/gu;\n\n/**\n * Build the link-rewrite table: every current-version route (basePath\n * stripped, since authors write root-absolute links as if mounted at root)\n * mapped to the same page's route inside the new snapshot. Only pages the\n * snapshot actually contains qualify — filesystem pages under the content\n * root. Spec-rendered references (`/api`, `/events`) and remote sources\n * aren't copied, so links to them keep pointing at the live pages instead of\n * a 404 inside the snapshot.\n */\nconst buildRouteRewrites = (\n project: BlumeProject,\n id: string\n): Map<string, string> => {\n const { basePath, i18n } = project.config;\n const { contentRoot } = project.context;\n const rewrites = new Map<string, string>();\n for (const page of project.graph.pages) {\n // `sourcePath` marks a page backed by a local file, which excludes\n // generated and remote pages; the content-root check below excludes local\n // files that live outside the tree a version snapshot copies. A staged\n // page (an Obsidian note) is served from its source, not from the copied\n // tree, so it keeps publishing as current even when its file sits under\n // the content root.\n const { sourcePath } = page;\n if (\n page.version !== \"\" ||\n page.collection === \"staged\" ||\n !sourcePath ||\n relative(contentRoot, sourcePath).startsWith(\"..\")\n ) {\n continue;\n }\n const logical = versionizeRoute(page.versionKey, id);\n rewrites.set(\n stripBasePath(basePath, page.route),\n i18n ? localizeRoute(logical, page.locale, i18n) : logical\n );\n }\n return rewrites;\n};\n\n/** Rewrite one line's root-absolute internal links via the rewrite table. */\nconst rewriteLine = (line: string, rewrites: Map<string, string>): string => {\n const masked = line.replaceAll(INLINE_CODE, (span) =>\n \" \".repeat(span.length)\n );\n let out = \"\";\n let cursor = 0;\n let count = 0;\n for (const match of masked.matchAll(ROOT_LINK)) {\n const target = match.groups?.target ?? \"\";\n // An anchor or query stays attached to the rewritten path.\n const hash = target.search(/[#?]/u);\n const path = hash === -1 ? target : target.slice(0, hash);\n const suffix = hash === -1 ? \"\" : target.slice(hash);\n const bare = path !== \"/\" && path.endsWith(\"/\") ? path.slice(0, -1) : path;\n const replacement = rewrites.get(bare);\n if (replacement === undefined) {\n continue;\n }\n const offset = (match.index ?? 0) + (match.groups?.prefix?.length ?? 0);\n out += line.slice(cursor, offset) + replacement + suffix;\n cursor = offset + target.length;\n count += 1;\n }\n return count === 0 ? line : out + line.slice(cursor);\n};\n\n/** A page's link-rewritten text and how many of its lines changed. */\nexport interface SnapshotRewrite {\n text: string;\n count: number;\n}\n\n/**\n * Rewrite a copied page's root-absolute internal links to their snapshot\n * equivalents, skipping fenced and inline code. Relative links need no\n * rewriting — the whole tree copies together, so they stay self-contained.\n */\nexport const rewriteSnapshotLinks = (\n source: string,\n rewrites: Map<string, string>\n): SnapshotRewrite => {\n let fence: FenceState = null;\n let count = 0;\n const lines = source.split(\"\\n\").map((line) => {\n const next = nextFenceState(line, fence);\n const inFence = fence !== null || next !== null;\n fence = next;\n if (inFence) {\n return line;\n }\n const rewrittenLine = rewriteLine(line, rewrites);\n if (rewrittenLine !== line) {\n count += 1;\n }\n return rewrittenLine;\n });\n return { count, text: lines.join(\"\\n\") };\n};\n\n/** The config entry to add, as a paste-ready snippet for the fallback path. */\nconst snippetFor = (id: string, hasVersions: boolean): string =>\n hasVersions\n ? `Add to versions.archived in blume.config.ts (newest first):\\n\\n { id: \"${id}\" },\\n`\n : `Add to blume.config.ts:\\n\\n versions: {\\n archived: [{ id: \"${id}\" }],\\n current: { label: \"…\" },\\n },\\n`;\n\n/**\n * Best-effort in-place config update: insert the new id at the head of an\n * existing \\`archived: [\\` array literal, preserving indentation. Anything\n * fancier (computed arrays, config spread across files) falls back to a\n * printed snippet — the config is user-authored TypeScript, and string-level\n * surgery beyond this simple shape risks corrupting it.\n */\nexport const insertArchivedVersion = async (\n configPath: string,\n id: string\n): Promise<boolean> => {\n let text: string;\n try {\n text = await readFile(configPath, \"utf-8\");\n } catch {\n return false;\n }\n const match = text.match(/(?<lead>archived:\\s*\\[)/u);\n if (!match || match.index === undefined) {\n return false;\n }\n const insertAt = match.index + (match.groups?.lead?.length ?? 0);\n // Indentation: one level deeper than the line holding `archived:`.\n const lineStart = text.lastIndexOf(\"\\n\", match.index) + 1;\n const indent = text.slice(lineStart).match(/^\\s*/u)?.[0] ?? \"\";\n const rest = text.slice(insertAt);\n // Match the array's authored shape: empty stays bare, an inline array gets\n // an inline entry, a multiline array gets its own indented line — on the\n // file's own line ending, so a CRLF config doesn't gain a lone LF.\n const eol = /^\\r?\\n/u.exec(rest)?.[0];\n let entry: string;\n if (rest.trimStart().startsWith(\"]\")) {\n entry = `{ id: \"${id}\" }`;\n } else if (eol) {\n entry = `${eol}${indent} { id: \"${id}\" },`;\n } else {\n entry = `{ id: \"${id}\" }, `;\n }\n await writeTextAtomic(configPath, text.slice(0, insertAt) + entry + rest);\n return true;\n};\n\n/**\n * Freeze the current docs as an archived version: copy the content tree into\n * `<contentRoot>/<id>/` (excluding existing snapshots), rewrite root-absolute\n * internal links so the copy is self-contained, and register the id in\n * `blume.config.ts` (or print the snippet to paste).\n */\nexport const cutVersion = async (\n root: string,\n id: string,\n options: { force?: boolean } = {}\n): Promise<CutResult> => {\n if (!VERSION_ID.test(id)) {\n throw new CutError(\n `Version ids must start with a letter (e.g. \"v1.0\") and contain only letters, digits, dots, hyphens, and underscores — got \"${id}\".`\n );\n }\n\n const project = await scanProject(root);\n const errors = project.diagnostics.filter(\n (diagnostic) => diagnostic.severity === \"error\"\n );\n if (errors.length > 0) {\n throw new CutError(\n `The project has ${errors.length} error diagnostic(s) — fix them before cutting a version (run \\`blume validate\\`).`\n );\n }\n // A missing content root already fails inside `scanProject` (the filesystem\n // source's own validation), so only the version-level checks remain here.\n const { contentRoot } = project.context;\n if (project.config.versions?.archived.some((version) => version.id === id)) {\n throw new CutError(\n `Version \"${id}\" is already registered in versions.archived.`\n );\n }\n\n const dir = join(contentRoot, id);\n if (existsSync(dir)) {\n if (!options.force) {\n throw new CutError(\n `${dir} already exists — pass --force to overwrite the snapshot.`\n );\n }\n await rm(dir, { force: true, recursive: true });\n }\n\n // Existing snapshots must not nest inside the new one: configured ids, plus\n // any version-shaped directory (`v1.0/`) that never made it into the config —\n // e.g. a prior cut whose config update fell back to a printed snippet. A\n // genuine content folder that merely looks like a version already draws the\n // rename-or-register diagnostic, so skipping it here is consistent.\n const excluded = new Set([\n id,\n \"node_modules\",\n ...(project.config.versions?.archived.map((version) => version.id) ?? []),\n ]);\n // Another local source's tree under the content root — an Obsidian vault the\n // filesystem source excludes — is that source's to publish. Copying it would\n // republish its raw notes (wikilinks and `%%comments%%` intact) as\n // filesystem pages of the snapshot, while the source itself keeps serving\n // them as current.\n const foreignRoots = project.sources\n .flatMap((source) =>\n source.staged && source.contentRoot ? [source.contentRoot] : []\n )\n .filter((tree) => isWithin(contentRoot, tree));\n const isForeign = (path: string): boolean =>\n foreignRoots.some((tree) => isWithin(tree, path));\n const entries = await readdir(contentRoot, { withFileTypes: true });\n let copied = 0;\n await Promise.all(\n entries.map(async (entry) => {\n if (\n excluded.has(entry.name) ||\n entry.name.startsWith(\".\") ||\n (entry.isDirectory() && VERSION_LIKE.test(entry.name))\n ) {\n return;\n }\n await cp(join(contentRoot, entry.name), join(dir, entry.name), {\n filter: (source) => !isForeign(source),\n recursive: true,\n });\n })\n );\n\n // Rewrite root-absolute internal links in every copied markdown page.\n const rewrites = buildRouteRewrites(project, id);\n const rewritten: { file: string; count: number }[] = [];\n const walk = async (current: string): Promise<void> => {\n const children = await readdir(current, { withFileTypes: true });\n await Promise.all(\n children.map(async (child) => {\n const abs = join(current, child.name);\n if (child.isDirectory()) {\n await walk(abs);\n return;\n }\n copied += 1;\n if (!/\\.mdx?$/u.test(child.name)) {\n return;\n }\n const source = await readFile(abs, \"utf-8\");\n const { text, count } = rewriteSnapshotLinks(source, rewrites);\n if (count > 0) {\n await writeTextAtomic(abs, text);\n rewritten.push({ count, file: relative(dir, abs) });\n }\n })\n );\n };\n await walk(dir);\n\n const configPath = join(root, \"blume.config.ts\");\n const configUpdated =\n project.config.versions !== undefined &&\n (await insertArchivedVersion(configPath, id));\n\n return {\n configSnippet: configUpdated\n ? null\n : snippetFor(id, project.config.versions !== undefined),\n configUpdated,\n copied,\n dir,\n rewritten,\n };\n};\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;ACAA;AACA;AAEA;AA2BO,MAAM,iBAAiB,MAAM;AAAA,EAClC,WAAW,CAAC,SAAiB;AAAA,IAC3B,MAAM,OAAO;AAAA,IACb,KAAK,OAAO;AAAA;AAEhB;AAKA,IAAM,cAAc;AAGpB,IAAM,YAAY;AAWlB,IAAM,qBAAqB,CACzB,SACA,OACwB;AAAA,EACxB,QAAQ,UAAU,SAAS,QAAQ;AAAA,EACnC,QAAQ,gBAAgB,QAAQ;AAAA,EAChC,MAAM,WAAW,IAAI;AAAA,EACrB,WAAW,QAAQ,QAAQ,MAAM,OAAO;AAAA,IAOtC,QAAQ,eAAe;AAAA,IACvB,IACE,KAAK,YAAY,MACjB,KAAK,eAAe,YACpB,CAAC,cACD,SAAS,aAAa,UAAU,EAAE,WAAW,IAAI,GACjD;AAAA,MACA;AAAA,IACF;AAAA,IACA,MAAM,UAAU,gBAAgB,KAAK,YAAY,EAAE;AAAA,IACnD,SAAS,IACP,cAAc,UAAU,KAAK,KAAK,GAClC,OAAO,cAAc,SAAS,KAAK,QAAQ,IAAI,IAAI,OACrD;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAIT,IAAM,cAAc,CAAC,MAAc,aAA0C;AAAA,EAC3E,MAAM,SAAS,KAAK,WAAW,aAAa,CAAC,SAC3C,IAAI,OAAO,KAAK,MAAM,CACxB;AAAA,EACA,IAAI,MAAM;AAAA,EACV,IAAI,SAAS;AAAA,EACb,IAAI,QAAQ;AAAA,EACZ,WAAW,SAAS,OAAO,SAAS,SAAS,GAAG;AAAA,IAC9C,MAAM,SAAS,MAAM,QAAQ,UAAU;AAAA,IAEvC,MAAM,OAAO,OAAO,OAAO,OAAO;AAAA,IAClC,MAAM,OAAO,SAAS,KAAK,SAAS,OAAO,MAAM,GAAG,IAAI;AAAA,IACxD,MAAM,SAAS,SAAS,KAAK,KAAK,OAAO,MAAM,IAAI;AAAA,IACnD,MAAM,OAAO,SAAS,OAAO,KAAK,SAAS,GAAG,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI;AAAA,IACtE,MAAM,cAAc,SAAS,IAAI,IAAI;AAAA,IACrC,IAAI,gBAAgB,WAAW;AAAA,MAC7B;AAAA,IACF;AAAA,IACA,MAAM,UAAU,MAAM,SAAS,MAAM,MAAM,QAAQ,QAAQ,UAAU;AAAA,IACrE,OAAO,KAAK,MAAM,QAAQ,MAAM,IAAI,cAAc;AAAA,IAClD,SAAS,SAAS,OAAO;AAAA,IACzB,SAAS;AAAA,EACX;AAAA,EACA,OAAO,UAAU,IAAI,OAAO,MAAM,KAAK,MAAM,MAAM;AAAA;AAc9C,IAAM,uBAAuB,CAClC,QACA,aACoB;AAAA,EACpB,IAAI,QAAoB;AAAA,EACxB,IAAI,QAAQ;AAAA,EACZ,MAAM,QAAQ,OAAO,MAAM;AAAA,CAAI,EAAE,IAAI,CAAC,SAAS;AAAA,IAC7C,MAAM,OAAO,eAAe,MAAM,KAAK;AAAA,IACvC,MAAM,UAAU,UAAU,QAAQ,SAAS;AAAA,IAC3C,QAAQ;AAAA,IACR,IAAI,SAAS;AAAA,MACX,OAAO;AAAA,IACT;AAAA,IACA,MAAM,gBAAgB,YAAY,MAAM,QAAQ;AAAA,IAChD,IAAI,kBAAkB,MAAM;AAAA,MAC1B,SAAS;AAAA,IACX;AAAA,IACA,OAAO;AAAA,GACR;AAAA,EACD,OAAO,EAAE,OAAO,MAAM,MAAM,KAAK;AAAA,CAAI,EAAE;AAAA;AAIzC,IAAM,aAAa,CAAC,IAAY,gBAC9B,cACI;AAAA;AAAA,WAA2E;AAAA,IAC3E;AAAA;AAAA;AAAA,wBAAmE;AAAA;AAAA;AAAA;AASlE,IAAM,wBAAwB,OACnC,YACA,OACqB;AAAA,EACrB,IAAI;AAAA,EACJ,IAAI;AAAA,IACF,OAAO,MAAM,SAAS,YAAY,OAAO;AAAA,IACzC,MAAM;AAAA,IACN,OAAO;AAAA;AAAA,EAET,MAAM,QAAQ,KAAK,MAAM,0BAA0B;AAAA,EACnD,IAAI,CAAC,SAAS,MAAM,UAAU,WAAW;AAAA,IACvC,OAAO;AAAA,EACT;AAAA,EACA,MAAM,WAAW,MAAM,SAAS,MAAM,QAAQ,MAAM,UAAU;AAAA,EAE9D,MAAM,YAAY,KAAK,YAAY;AAAA,GAAM,MAAM,KAAK,IAAI;AAAA,EACxD,MAAM,SAAS,KAAK,MAAM,SAAS,EAAE,MAAM,OAAO,IAAI,MAAM;AAAA,EAC5D,MAAM,OAAO,KAAK,MAAM,QAAQ;AAAA,EAIhC,MAAM,MAAM,UAAU,KAAK,IAAI,IAAI;AAAA,EACnC,IAAI;AAAA,EACJ,IAAI,KAAK,UAAU,EAAE,WAAW,GAAG,GAAG;AAAA,IACpC,QAAQ,UAAU;AAAA,EACpB,EAAO,SAAI,KAAK;AAAA,IACd,QAAQ,GAAG,MAAM,kBAAkB;AAAA,EACrC,EAAO;AAAA,IACL,QAAQ,UAAU;AAAA;AAAA,EAEpB,MAAM,gBAAgB,YAAY,KAAK,MAAM,GAAG,QAAQ,IAAI,QAAQ,IAAI;AAAA,EACxE,OAAO;AAAA;AASF,IAAM,aAAa,OACxB,MACA,IACA,UAA+B,CAAC,MACT;AAAA,EACvB,IAAI,CAAC,WAAW,KAAK,EAAE,GAAG;AAAA,IACxB,MAAM,IAAI,SACR,8HAA8H,MAChI;AAAA,EACF;AAAA,EAEA,MAAM,UAAU,MAAM,YAAY,IAAI;AAAA,EACtC,MAAM,SAAS,QAAQ,YAAY,OACjC,CAAC,eAAe,WAAW,aAAa,OAC1C;AAAA,EACA,IAAI,OAAO,SAAS,GAAG;AAAA,IACrB,MAAM,IAAI,SACR,mBAAmB,OAAO,0FAC5B;AAAA,EACF;AAAA,EAGA,QAAQ,gBAAgB,QAAQ;AAAA,EAChC,IAAI,QAAQ,OAAO,UAAU,SAAS,KAAK,CAAC,YAAY,QAAQ,OAAO,EAAE,GAAG;AAAA,IAC1E,MAAM,IAAI,SACR,YAAY,iDACd;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,KAAK,aAAa,EAAE;AAAA,EAChC,IAAI,WAAW,GAAG,GAAG;AAAA,IACnB,IAAI,CAAC,QAAQ,OAAO;AAAA,MAClB,MAAM,IAAI,SACR,GAAG,8DACL;AAAA,IACF;AAAA,IACA,MAAM,GAAG,KAAK,EAAE,OAAO,MAAM,WAAW,KAAK,CAAC;AAAA,EAChD;AAAA,EAOA,MAAM,WAAW,IAAI,IAAI;AAAA,IACvB;AAAA,IACA;AAAA,IACA,GAAI,QAAQ,OAAO,UAAU,SAAS,IAAI,CAAC,YAAY,QAAQ,EAAE,KAAK,CAAC;AAAA,EACzE,CAAC;AAAA,EAMD,MAAM,eAAe,QAAQ,QAC1B,QAAQ,CAAC,WACR,OAAO,UAAU,OAAO,cAAc,CAAC,OAAO,WAAW,IAAI,CAAC,CAChE,EACC,OAAO,CAAC,SAAS,SAAS,aAAa,IAAI,CAAC;AAAA,EAC/C,MAAM,YAAY,CAAC,SACjB,aAAa,KAAK,CAAC,SAAS,SAAS,MAAM,IAAI,CAAC;AAAA,EAClD,MAAM,UAAU,MAAM,QAAQ,aAAa,EAAE,eAAe,KAAK,CAAC;AAAA,EAClE,IAAI,SAAS;AAAA,EACb,MAAM,QAAQ,IACZ,QAAQ,IAAI,OAAO,UAAU;AAAA,IAC3B,IACE,SAAS,IAAI,MAAM,IAAI,KACvB,MAAM,KAAK,WAAW,GAAG,KACxB,MAAM,YAAY,KAAK,aAAa,KAAK,MAAM,IAAI,GACpD;AAAA,MACA;AAAA,IACF;AAAA,IACA,MAAM,GAAG,KAAK,aAAa,MAAM,IAAI,GAAG,KAAK,KAAK,MAAM,IAAI,GAAG;AAAA,MAC7D,QAAQ,CAAC,WAAW,CAAC,UAAU,MAAM;AAAA,MACrC,WAAW;AAAA,IACb,CAAC;AAAA,GACF,CACH;AAAA,EAGA,MAAM,WAAW,mBAAmB,SAAS,EAAE;AAAA,EAC/C,MAAM,YAA+C,CAAC;AAAA,EACtD,MAAM,OAAO,OAAO,YAAmC;AAAA,IACrD,MAAM,WAAW,MAAM,QAAQ,SAAS,EAAE,eAAe,KAAK,CAAC;AAAA,IAC/D,MAAM,QAAQ,IACZ,SAAS,IAAI,OAAO,UAAU;AAAA,MAC5B,MAAM,MAAM,KAAK,SAAS,MAAM,IAAI;AAAA,MACpC,IAAI,MAAM,YAAY,GAAG;AAAA,QACvB,MAAM,KAAK,GAAG;AAAA,QACd;AAAA,MACF;AAAA,MACA,UAAU;AAAA,MACV,IAAI,CAAC,WAAW,KAAK,MAAM,IAAI,GAAG;AAAA,QAChC;AAAA,MACF;AAAA,MACA,MAAM,SAAS,MAAM,SAAS,KAAK,OAAO;AAAA,MAC1C,QAAQ,MAAM,UAAU,qBAAqB,QAAQ,QAAQ;AAAA,MAC7D,IAAI,QAAQ,GAAG;AAAA,QACb,MAAM,gBAAgB,KAAK,IAAI;AAAA,QAC/B,UAAU,KAAK,EAAE,OAAO,MAAM,SAAS,KAAK,GAAG,EAAE,CAAC;AAAA,MACpD;AAAA,KACD,CACH;AAAA;AAAA,EAEF,MAAM,KAAK,GAAG;AAAA,EAEd,MAAM,aAAa,KAAK,MAAM,iBAAiB;AAAA,EAC/C,MAAM,gBACJ,QAAQ,OAAO,aAAa,aAC3B,MAAM,sBAAsB,YAAY,EAAE;AAAA,EAE7C,OAAO;AAAA,IACL,eAAe,gBACX,OACA,WAAW,IAAI,QAAQ,OAAO,aAAa,SAAS;AAAA,IACxD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;;;ADtTK,IAAM,iBAAiB,cAAc;AAAA,EAC1C,MAAM;AAAA,IACJ,OAAO;AAAA,MACL,aAAa;AAAA,MACb,MAAM;AAAA,IACR;AAAA,IACA,IAAI;AAAA,MACF,aAAa;AAAA,MACb,UAAU;AAAA,MACV,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EACA,MAAM,YAAY;AAAA,OACZ,IAAG,GAAG,QAAQ;AAAA,IAClB,MAAM,OAAO,QAAQ,IAAI;AAAA,IAEzB,IAAI,CAAC,KAAK,IAAI;AAAA,MACZ,QAAQ,WAAW,MAAM,WAAW,IAAI;AAAA,MACxC,IAAI,CAAC,OAAO,UAAU;AAAA,QACpB,OAAO,KACL,4GACF;AAAA,QACA;AAAA,MACF;AAAA,MACA,QAAQ,SAAS,aAAa,OAAO;AAAA,MACrC,QAAQ,OAAO,MACb,KAAK,QAAQ,kBAAkB,QAAQ,QAAQ,MAAM,QAAQ,UAAU;AAAA,CACzE;AAAA,MACA,WAAW,WAAW,UAAU;AAAA,QAC9B,QAAQ,OAAO,MACb,KAAK,QAAQ,SAAS,QAAQ,QAAQ,QAAQ;AAAA,CAChD;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,IAEA,IAAI;AAAA,MACF,MAAM,SAAS,MAAM,WAAW,MAAM,KAAK,IAAI,EAAE,OAAO,KAAK,MAAM,CAAC;AAAA,MACpE,OAAO,QACL,YAAY,OAAO,QAAQ,OAAO,wBACpC;AAAA,MACA,MAAM,gBAAgB,OAAO,UAAU,OACrC,CAAC,KAAK,UAAU,MAAM,MAAM,OAC5B,CACF;AAAA,MACA,IAAI,gBAAgB,GAAG;AAAA,QACrB,OAAO,KACL,kCAAkC,OAAO,UAAU,mBAAmB,yBACxE;AAAA,MACF;AAAA,MACA,IAAI,OAAO,eAAe;AAAA,QACxB,OAAO,QACL,UAAU,KAAK,6CACjB;AAAA,MACF,EAAO,SAAI,OAAO,eAAe;AAAA,QAC/B,OAAO,KAAK,OAAO,aAAa;AAAA,MAClC;AAAA,MACA,OAAO,KACL,mHACF;AAAA,MACA,OAAO,OAAO;AAAA,MACd,IAAI,iBAAiB,UAAU;AAAA,QAC7B,OAAO,MAAM,MAAM,OAAO;AAAA,QAC1B,QAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,MACA,IAAI,iBAAiB,YAAY;AAAA,QAC/B,OAAO,MAAM,MAAM,WAAW,OAAO;AAAA,QACrC,QAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,MACA,oBAAoB,KAAK;AAAA,MACzB,QAAQ,KAAK,CAAC;AAAA;AAAA;AAGpB,CAAC;",
|
|
9
|
+
"debugId": "D706C6D39089651F64756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/host-args.ts
|
|
4
|
+
var normalizeHostArgs = (rawArgs) => rawArgs.map((arg, index) => {
|
|
5
|
+
if (arg !== "--host") {
|
|
6
|
+
return arg;
|
|
7
|
+
}
|
|
8
|
+
const next = rawArgs[index + 1];
|
|
9
|
+
return next === undefined || next.startsWith("-") ? "--host=" : arg;
|
|
10
|
+
});
|
|
11
|
+
var normalizeHost = (host) => host === "" ? true : host ?? false;
|
|
12
|
+
|
|
13
|
+
export { normalizeHostArgs, normalizeHost };
|
|
14
|
+
|
|
15
|
+
//# debugId=CBDF4CDAEA11C19764756E2164756E21
|
|
16
|
+
//# sourceMappingURL=chunk-bcy492zc.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli/host-args.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Rewrite a bare `--host` in the raw argv to `--host=` before citty parses it.\n *\n * `host` is a string arg (citty has no mixed string/boolean type), and citty\n * 0.2 parses with `node:util.parseArgs`, where a string option consumes the\n * next token as its value even when that token is another flag: `blume dev\n * --host --open` would bind the literal hostname \"--open\" and drop `--open`.\n * The `--host=` spelling parses as `\"\"` without touching its neighbor, which\n * `normalizeHost` then maps to Astro's \"bind all interfaces\".\n */\nexport const normalizeHostArgs = (rawArgs: readonly string[]): string[] =>\n rawArgs.map((arg, index) => {\n if (arg !== \"--host\") {\n return arg;\n }\n const next = rawArgs[index + 1];\n return next === undefined || next.startsWith(\"-\") ? \"--host=\" : arg;\n });\n\n/**\n * Resolve a `--host` flag value into what Astro/Vite's `server.host` expects.\n * citty has no mixed string/boolean arg type, so `host` is declared as a\n * string and a bare `--host` parses as `\"\"` (`normalizeHostArgs` rewrites it\n * to `--host=` first) — Node would bind all interfaces for `\"\"`, but Vite's\n * `resolveHostname` treats it as a literal hostname and prints malformed URLs\n * like `http://:4321/`. Match Astro's own `--host` semantics instead: bare\n * flag → `true` (bind all interfaces), `--host 10.0.0.1` → that address,\n * absent → `false` (localhost only).\n *\n * Lives beside `normalizeHostArgs` rather than in `commands/dev.ts` so\n * `preview` can share it without importing the whole dev command graph.\n */\nexport const normalizeHost = (host?: string): boolean | string =>\n host === \"\" ? true : (host ?? false);\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;AAUO,IAAM,oBAAoB,CAAC,YAChC,QAAQ,IAAI,CAAC,KAAK,UAAU;AAAA,EAC1B,IAAI,QAAQ,UAAU;AAAA,IACpB,OAAO;AAAA,EACT;AAAA,EACA,MAAM,OAAO,QAAQ,QAAQ;AAAA,EAC7B,OAAO,SAAS,aAAa,KAAK,WAAW,GAAG,IAAI,YAAY;AAAA,CACjE;AAeI,IAAM,gBAAgB,CAAC,SAC5B,SAAS,KAAK,OAAQ,QAAQ;",
|
|
8
|
+
"debugId": "CBDF4CDAEA11C19764756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getBlumeVersion
|
|
4
|
+
} from "./chunk-4xyggvgf.js";
|
|
5
|
+
|
|
6
|
+
// src/cli/internal-error.ts
|
|
7
|
+
import { colors } from "consola/utils";
|
|
8
|
+
var ISSUES_URL = "https://github.com/haydenbleasel/blume/issues";
|
|
9
|
+
var BLUME_FRAME = /(?<abs>(?:\/[^\s()]*\/|[A-Za-z]:\\[^\s()]*\\)\.blume[/\\][^\s()]*)/gu;
|
|
10
|
+
var BLUME_MARKER = /[/\\]\.blume[/\\]/u;
|
|
11
|
+
var remapBlumeStack = (stack) => stack.replaceAll(BLUME_FRAME, (match) => {
|
|
12
|
+
const marker = match.search(BLUME_MARKER);
|
|
13
|
+
return `${match.slice(marker + 1)} (generated)`;
|
|
14
|
+
});
|
|
15
|
+
var reportInternalError = (error) => {
|
|
16
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
17
|
+
const lines = [
|
|
18
|
+
`${colors.red(colors.bold("BLUME_INTERNAL"))} An unexpected error occurred.`,
|
|
19
|
+
` ${err.message}`
|
|
20
|
+
];
|
|
21
|
+
const stack = remapBlumeStack(err.stack ?? "").split(`
|
|
22
|
+
`).slice(1, 5).map((line) => line.trim()).filter(Boolean);
|
|
23
|
+
if (stack.length > 0) {
|
|
24
|
+
lines.push("", colors.dim(stack.join(`
|
|
25
|
+
`)));
|
|
26
|
+
}
|
|
27
|
+
lines.push("", "This is likely a bug in Blume. Please report it with the details below:", colors.dim([
|
|
28
|
+
` Blume: ${getBlumeVersion()}`,
|
|
29
|
+
` Node: ${process.version}`,
|
|
30
|
+
` Platform: ${process.platform} ${process.arch}`
|
|
31
|
+
].join(`
|
|
32
|
+
`)), ` ${ISSUES_URL}`);
|
|
33
|
+
process.stderr.write(`${lines.join(`
|
|
34
|
+
`)}
|
|
35
|
+
`);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { reportInternalError };
|
|
39
|
+
|
|
40
|
+
//# debugId=D59E08D04AAEFF1964756E2164756E21
|
|
41
|
+
//# sourceMappingURL=chunk-btfr9yvw.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli/internal-error.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { colors } from \"consola/utils\";\n\nimport { getBlumeVersion } from \"../core/version.ts\";\n\nconst ISSUES_URL = \"https://github.com/haydenbleasel/blume/issues\";\n\n// Absolute paths into the hidden generated runtime — POSIX (`…/.blume/…`) or\n// Windows drive-letter (`C:\\…\\.blume\\…`) — including any trailing `:line:col`,\n// stopping at whitespace or a closing paren.\nconst BLUME_FRAME =\n /(?<abs>(?:\\/[^\\s()]*\\/|[A-Za-z]:\\\\[^\\s()]*\\\\)\\.blume[/\\\\][^\\s()]*)/gu;\n\n// The separator immediately before `.blume/` (or `.blume\\`) in a matched path.\nconst BLUME_MARKER = /[/\\\\]\\.blume[/\\\\]/u;\n\n/**\n * Rewrite `.blume/` frames in a stack so the generated runtime reads clearly:\n * the machine-absolute prefix is dropped to a project-relative `.blume/…` path\n * and tagged `(generated)`, keeping the reader oriented instead of staring at a\n * long path into a hidden directory. Frames in the user's own source (custom\n * pages keep their real location; wrappers import user files by their real path)\n * are untouched, so the actionable frame stays intact.\n */\nexport const remapBlumeStack = (stack: string): string =>\n stack.replaceAll(BLUME_FRAME, (match) => {\n const marker = match.search(BLUME_MARKER);\n return `${match.slice(marker + 1)} (generated)`;\n });\n\n/**\n * Print an unexpected (non-{@link BlumeError}) failure in a stable, reportable\n * shape instead of a bare stack trace: a fixed `BLUME_INTERNAL` code, the\n * message, a trimmed stack, and an environment dump for bug reports. Callers\n * exit after this — it doesn't exit itself, so it's testable.\n */\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- last-resort handler for whatever a catch clause caught; anything narrower would force casts at every call site\nexport const reportInternalError = (error: unknown): void => {\n const err = error instanceof Error ? error : new Error(String(error));\n const lines = [\n `${colors.red(colors.bold(\"BLUME_INTERNAL\"))} An unexpected error occurred.`,\n ` ${err.message}`,\n ];\n\n // A few frames are enough to locate the fault without burying the report;\n // `.blume/` frames are relativized so the hidden runtime reads clearly.\n const stack = remapBlumeStack(err.stack ?? \"\")\n .split(\"\\n\")\n .slice(1, 5)\n .map((line) => line.trim())\n .filter(Boolean);\n if (stack.length > 0) {\n lines.push(\"\", colors.dim(stack.join(\"\\n\")));\n }\n\n lines.push(\n \"\",\n \"This is likely a bug in Blume. Please report it with the details below:\",\n colors.dim(\n [\n ` Blume: ${getBlumeVersion()}`,\n ` Node: ${process.version}`,\n ` Platform: ${process.platform} ${process.arch}`,\n ].join(\"\\n\")\n ),\n ` ${ISSUES_URL}`\n );\n\n process.stderr.write(`${lines.join(\"\\n\")}\\n`);\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;AAAA;AAIA,IAAM,aAAa;AAKnB,IAAM,cACJ;AAGF,IAAM,eAAe;AAUd,IAAM,kBAAkB,CAAC,UAC9B,MAAM,WAAW,aAAa,CAAC,UAAU;AAAA,EACvC,MAAM,SAAS,MAAM,OAAO,YAAY;AAAA,EACxC,OAAO,GAAG,MAAM,MAAM,SAAS,CAAC;AAAA,CACjC;AASI,IAAM,sBAAsB,CAAC,UAAyB;AAAA,EAC3D,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,EACpE,MAAM,QAAQ;AAAA,IACZ,GAAG,OAAO,IAAI,OAAO,KAAK,gBAAgB,CAAC;AAAA,IAC3C,KAAK,IAAI;AAAA,EACX;AAAA,EAIA,MAAM,QAAQ,gBAAgB,IAAI,SAAS,EAAE,EAC1C,MAAM;AAAA,CAAI,EACV,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,OAAO;AAAA,EACjB,IAAI,MAAM,SAAS,GAAG;AAAA,IACpB,MAAM,KAAK,IAAI,OAAO,IAAI,MAAM,KAAK;AAAA,CAAI,CAAC,CAAC;AAAA,EAC7C;AAAA,EAEA,MAAM,KACJ,IACA,2EACA,OAAO,IACL;AAAA,IACE,eAAe,gBAAgB;AAAA,IAC/B,eAAe,QAAQ;AAAA,IACvB,eAAe,QAAQ,YAAY,QAAQ;AAAA,EAC7C,EAAE,KAAK;AAAA,CAAI,CACb,GACA,KAAK,YACP;AAAA,EAEA,QAAQ,OAAO,MAAM,GAAG,MAAM,KAAK;AAAA,CAAI;AAAA,CAAK;AAAA;",
|
|
8
|
+
"debugId": "D59E08D04AAEFF1964756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|