blume 1.5.3 → 1.6.1
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 +94 -0
- package/dist/cli/index.js +3949 -1403
- package/dist/cli/index.js.map +111 -96
- package/dist/types/ai/component-markdown.d.ts +79 -0
- package/dist/types/components/layout/nav-utils.d.ts +60 -0
- package/dist/types/core/base-path.d.ts +9 -0
- package/dist/types/core/config-input.d.ts +206 -4
- package/dist/types/core/config.d.ts +6 -4
- package/dist/types/core/data.d.ts +33 -2
- package/dist/types/core/github.d.ts +35 -0
- package/dist/types/core/i18n-ui.d.ts +10 -0
- package/dist/types/core/navigation.d.ts +69 -0
- package/dist/types/core/schema.d.ts +122 -1
- package/dist/types/core/sources/types.d.ts +31 -6
- package/dist/types/core/types.d.ts +29 -2
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/openapi/references.d.ts +26 -1
- package/dist/types/seo/jsonld.d.ts +105 -0
- package/dist/types/theme/fonts.d.ts +34 -4
- package/docs/07-faq.mdx +9 -9
- package/docs/_snippets/include-demo.mdx +7 -0
- package/docs/advanced/api-reference.mdx +13 -4
- package/docs/advanced/custom-pages.mdx +4 -2
- package/docs/advanced/graphql.mdx +84 -0
- package/docs/advanced/meta.ts +8 -1
- package/docs/configuration/ai.mdx +25 -3
- package/docs/configuration/index.mdx +24 -0
- package/docs/configuration/search.mdx +13 -1
- package/docs/configuration/seo.mdx +30 -3
- package/docs/configuration/theming.mdx +23 -0
- package/docs/content/components.mdx +15 -1
- package/docs/content/includes.mdx +68 -0
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +25 -0
- package/docs/content/sources.mdx +42 -1
- package/docs/content/syntax.mdx +69 -1
- package/docs/content/versioning.mdx +15 -9
- package/docs/reference/cli.mdx +2 -1
- package/package.json +66 -57
- package/skills/blume-migrate/SKILL.md +16 -7
- package/skills/blume-migrate/references/docusaurus.md +5 -3
- package/skills/blume-migrate/references/fumadocs.md +10 -2
- package/skills/blume-migrate/references/mintlify.md +3 -2
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +1 -1
- package/src/ai/agent-readability.ts +2 -1
- package/src/ai/ask-data.ts +2 -1
- package/src/ai/component-markdown.ts +199 -36
- package/src/ai/llms.ts +93 -6
- package/src/ai/markdown.ts +2 -2
- package/src/ai/mcp/discovery.ts +10 -2
- package/src/ai/mcp/server.ts +74 -2
- package/src/astro/examples.ts +29 -2
- package/src/astro/generate.ts +282 -177
- package/src/astro/include-hmr.ts +81 -0
- package/src/astro/include-refresh.ts +0 -0
- package/src/astro/index.ts +10 -5
- package/src/astro/markdown-negotiation.ts +1 -1
- package/src/astro/runtime-modules.ts +196 -0
- package/src/astro/templates.ts +365 -113
- package/src/cli/commands/build.ts +91 -16
- package/src/cli/commands/dev.ts +6 -3
- package/src/cli/host-args.ts +18 -0
- package/src/cli/index.ts +2 -1
- package/src/cli/init/questions.ts +1 -0
- package/src/cli/init/scaffold.ts +27 -4
- package/src/components/colors.ts +142 -0
- package/src/components/content/Badge.astro +5 -12
- package/src/components/content/Callout.astro +19 -36
- package/src/components/content/Card.astro +15 -21
- package/src/components/content/Component.astro +10 -1
- package/src/components/content/GithubInfo.astro +28 -9
- package/src/components/content/Tabs.astro +27 -5
- package/src/components/content/github-info.ts +20 -5
- package/src/components/copy-feedback.ts +93 -9
- package/src/components/dropdown-dismiss.ts +122 -0
- package/src/components/islands/ask-ai.tsx +4 -1
- package/src/components/islands/hooks.ts +3 -1
- package/src/components/layout/Fonts.astro +15 -8
- package/src/components/layout/Header.astro +44 -0
- package/src/components/layout/LanguageSwitcher.astro +9 -1
- package/src/components/layout/NavSelector.astro +12 -3
- package/src/components/layout/NavTree.astro +6 -18
- package/src/components/layout/PageActions.astro +54 -22
- package/src/components/layout/PageLayout.astro +2 -0
- package/src/components/layout/ReferenceLayout.astro +6 -1
- package/src/components/layout/RootLayout.astro +42 -15
- package/src/components/layout/Search.astro +36 -4
- package/src/components/layout/TableOfContents.astro +8 -2
- package/src/components/layout/head-scripts.ts +30 -1
- package/src/components/openapi/ApiOverview.astro +13 -3
- package/src/components/openapi/AsyncApiOperation.astro +7 -14
- package/src/components/openapi/GraphqlChip.astro +33 -0
- package/src/components/openapi/GraphqlFieldsTable.astro +111 -0
- package/src/components/openapi/GraphqlOperation.astro +186 -0
- package/src/components/openapi/GraphqlType.astro +154 -0
- package/src/components/openapi/MethodBadge.astro +3 -14
- package/src/components/openapi/Operation.astro +12 -5
- package/src/components/openapi/OperationPanel.astro +43 -0
- package/src/components/openapi/RequestPanel.astro +5 -10
- package/src/components/openapi/Responses.astro +1 -16
- package/src/components/openapi/graphql-helpers.ts +466 -0
- package/src/components/openapi/playground-client.ts +15 -0
- package/src/components/openapi/sample-panels.ts +45 -0
- package/src/components/openapi/snippets.ts +13 -35
- package/src/core/base-path.ts +11 -0
- package/src/core/config-input.ts +209 -2
- package/src/core/config.ts +6 -4
- package/src/core/content-assets.ts +15 -4
- package/src/core/data.ts +28 -3
- package/src/core/define-components.ts +2 -0
- package/src/core/diagnostics.ts +8 -0
- package/src/core/frontmatter.ts +20 -8
- package/src/core/github.ts +71 -0
- package/src/core/graph.ts +22 -8
- package/src/core/heading-markers.ts +96 -0
- package/src/core/i18n-ui.ts +12 -0
- package/src/core/includes.ts +633 -0
- package/src/core/last-modified.ts +36 -11
- package/src/core/links.ts +79 -13
- package/src/core/manifest.ts +10 -0
- package/src/core/meta.ts +2 -1
- package/src/core/nav-diagnostics.ts +11 -2
- package/src/core/navigation.ts +27 -6
- package/src/core/project-graph.ts +61 -9
- package/src/core/schema.ts +235 -36
- package/src/core/server-features.ts +5 -9
- package/src/core/sources/github-releases.ts +2 -2
- package/src/core/sources/normalize.ts +502 -115
- package/src/core/sources/notion.ts +43 -8
- package/src/core/sources/obsidian.ts +1038 -0
- package/src/core/sources/read.ts +36 -1
- package/src/core/sources/resolve.ts +34 -1
- package/src/core/sources/types.ts +28 -6
- package/src/core/sources/watch.ts +12 -8
- package/src/core/tsconfig-aliases.ts +48 -35
- package/src/core/types.ts +31 -2
- package/src/core/ui-packs/ar.ts +2 -0
- package/src/core/ui-packs/bg.ts +3 -0
- package/src/core/ui-packs/bn.ts +2 -0
- package/src/core/ui-packs/ca.ts +3 -0
- package/src/core/ui-packs/cs.ts +2 -0
- package/src/core/ui-packs/da.ts +2 -0
- package/src/core/ui-packs/de.ts +3 -0
- package/src/core/ui-packs/el.ts +3 -0
- package/src/core/ui-packs/es.ts +3 -0
- package/src/core/ui-packs/fa.ts +2 -0
- package/src/core/ui-packs/fi.ts +2 -0
- package/src/core/ui-packs/fr.ts +3 -0
- package/src/core/ui-packs/he.ts +2 -0
- package/src/core/ui-packs/hi.ts +2 -0
- package/src/core/ui-packs/hr.ts +3 -0
- package/src/core/ui-packs/hu.ts +3 -0
- package/src/core/ui-packs/id.ts +3 -0
- package/src/core/ui-packs/it.ts +2 -0
- package/src/core/ui-packs/ja.ts +3 -0
- package/src/core/ui-packs/ko.ts +3 -0
- package/src/core/ui-packs/nl.ts +3 -0
- package/src/core/ui-packs/no.ts +3 -0
- package/src/core/ui-packs/pl.ts +3 -0
- package/src/core/ui-packs/pt-br.ts +3 -0
- package/src/core/ui-packs/pt.ts +3 -0
- package/src/core/ui-packs/ro.ts +3 -0
- package/src/core/ui-packs/ru.ts +3 -0
- package/src/core/ui-packs/sk.ts +2 -0
- package/src/core/ui-packs/sr.ts +2 -0
- package/src/core/ui-packs/sv.ts +3 -0
- package/src/core/ui-packs/th.ts +2 -0
- package/src/core/ui-packs/tr.ts +3 -0
- package/src/core/ui-packs/uk.ts +3 -0
- package/src/core/ui-packs/vi.ts +2 -0
- package/src/core/ui-packs/zh-tw.ts +2 -0
- package/src/core/ui-packs/zh.ts +2 -0
- package/src/core/version-cut.ts +26 -6
- package/src/core/yaml.ts +26 -0
- package/src/deploy/function-bundle.ts +251 -0
- package/src/deploy/vercel-negotiation.ts +49 -6
- package/src/eval/schema.ts +3 -1
- package/src/markdown/code-title.ts +22 -16
- package/src/markdown/features.ts +21 -0
- package/src/markdown/fence-meta.ts +50 -0
- package/src/markdown/heading-anchors.ts +198 -37
- package/src/markdown/include.ts +247 -0
- package/src/markdown/index.ts +43 -34
- package/src/markdown/language-icon.ts +2 -2
- package/src/markdown/mdast.ts +7 -3
- package/src/markdown/ts2js.ts +264 -0
- package/src/og/card.ts +1 -1
- package/src/openapi/asyncapi.ts +4 -1
- package/src/openapi/graphql-build.ts +293 -0
- package/src/openapi/graphql.ts +212 -0
- package/src/openapi/model.ts +38 -5
- package/src/openapi/parse.ts +34 -0
- package/src/openapi/proxy.ts +30 -5
- package/src/openapi/references.ts +97 -13
- package/src/openapi/render-mdx.ts +66 -12
- package/src/openapi/scalar.ts +5 -16
- package/src/openapi/source.ts +91 -23
- package/src/registry/eject.ts +47 -17
- package/src/search/documents.ts +229 -37
- package/src/search/orama-index.ts +9 -5
- package/src/seo/jsonld.ts +293 -51
- package/src/theme/code-block-padding.ts +16 -0
- package/src/theme/entry.ts +67 -13
- package/src/theme/fonts.ts +189 -16
- package/src/theme/sources.ts +49 -0
- package/src/translate/prompts.ts +2 -0
- package/src/translate/run.ts +7 -0
- package/src/translate/work-list.ts +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { readFile, utimes } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dev-server invalidation for `<include>` partials. A partial is not an Astro
|
|
5
|
+
* content-collection entry (the default underscore-prefix exclude keeps it
|
|
6
|
+
* out of the `docs` glob), so Vite has no edge from an including page to it —
|
|
7
|
+
* editing the partial would keep serving the page's stale compiled module.
|
|
8
|
+
* The scan records each page's transitive includes; `generateRuntime` writes
|
|
9
|
+
* them to `generated/includes.json` as partial → including pages, and this
|
|
10
|
+
* plugin turns a partial edit into an invalidation of those page modules plus
|
|
11
|
+
* a full reload.
|
|
12
|
+
*
|
|
13
|
+
* The graph file is re-read on every hot update: `blume dev`'s regenerate
|
|
14
|
+
* loop rewrites it after each content change, so the mapping tracks include
|
|
15
|
+
* edits without restarting the server.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** The Vite module-graph slice the plugin touches (structurally typed, like
|
|
19
|
+
* every Blume-authored Vite plugin — see `serverAppResolvePlugin`). */
|
|
20
|
+
interface IncludeHmrServer {
|
|
21
|
+
moduleGraph: {
|
|
22
|
+
getModulesByFile: (file: string) => Set<unknown> | undefined;
|
|
23
|
+
invalidateModule: (mod: never) => void;
|
|
24
|
+
};
|
|
25
|
+
ws: { send: (payload: { type: "full-reload" }) => void };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface IncludeHmrContext {
|
|
29
|
+
file: string;
|
|
30
|
+
server: IncludeHmrServer;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface IncludeHmrPlugin {
|
|
34
|
+
name: string;
|
|
35
|
+
handleHotUpdate: (ctx: IncludeHmrContext) => Promise<never[] | undefined>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const includeHmrPlugin = (graphPath: string): IncludeHmrPlugin => ({
|
|
39
|
+
async handleHotUpdate(ctx) {
|
|
40
|
+
let graph: Record<string, string[]>;
|
|
41
|
+
try {
|
|
42
|
+
// SAFETY: `generateRuntime` is the file's only writer and serializes
|
|
43
|
+
// exactly this shape; a malformed hand-edit throws into the catch below.
|
|
44
|
+
graph = JSON.parse(await readFile(graphPath, "utf-8")) as Record<
|
|
45
|
+
string,
|
|
46
|
+
string[]
|
|
47
|
+
>;
|
|
48
|
+
} catch {
|
|
49
|
+
// No graph yet (first run) — nothing to invalidate.
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const includers = graph[ctx.file];
|
|
53
|
+
if (!includers || includers.length === 0) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const { moduleGraph, ws } = ctx.server;
|
|
57
|
+
const now = new Date();
|
|
58
|
+
for (const includer of includers) {
|
|
59
|
+
for (const mod of moduleGraph.getModulesByFile(includer) ?? []) {
|
|
60
|
+
// SAFETY: the module came out of this module graph; `never` only
|
|
61
|
+
// reflects that the structural slice doesn't model the node type.
|
|
62
|
+
moduleGraph.invalidateModule(mod as never);
|
|
63
|
+
}
|
|
64
|
+
// Plain `.md` pages have no Vite module: their HTML lives in the
|
|
65
|
+
// content-layer store, rendered at sync time. Bump the page's mtime so
|
|
66
|
+
// Astro's content watcher re-syncs it — the include-aware digest
|
|
67
|
+
// (`withIncludeRefresh`) then forces a fresh render that re-reads the
|
|
68
|
+
// edited partial.
|
|
69
|
+
try {
|
|
70
|
+
// oxlint-disable-next-line no-await-in-loop -- ordered per-page touch
|
|
71
|
+
await utimes(includer, now, now);
|
|
72
|
+
} catch {
|
|
73
|
+
// The page may have been deleted since the graph was written.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
ws.send({ type: "full-reload" });
|
|
77
|
+
// The partial itself is not a module; suppress Vite's default handling.
|
|
78
|
+
return [];
|
|
79
|
+
},
|
|
80
|
+
name: "blume:include-hmr",
|
|
81
|
+
});
|
|
Binary file
|
package/src/astro/index.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
export { withAdapterRoot } from "./adapter-root.ts";
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
serverAppResolvePlugin,
|
|
6
|
-
} from "./generate.ts";
|
|
2
|
+
export { generateRuntime, prerenderDepsPlugin } from "./generate.ts";
|
|
3
|
+
export { includeHmrPlugin } from "./include-hmr.ts";
|
|
4
|
+
export { withIncludeRefresh } from "./include-refresh.ts";
|
|
7
5
|
export type { GenerateResult } from "./generate.ts";
|
|
8
6
|
export { blumeIntegration } from "./integration.ts";
|
|
9
7
|
export type { BlumeIntegrationOptions, BlumePageRoute } from "./integration.ts";
|
|
8
|
+
export {
|
|
9
|
+
publishRuntimeModules,
|
|
10
|
+
readRuntimeModule,
|
|
11
|
+
RUNTIME_MODULE_FILES,
|
|
12
|
+
runtimeModulesPlugin,
|
|
13
|
+
} from "./runtime-modules.ts";
|
|
14
|
+
export type { RuntimeModuleId } from "./runtime-modules.ts";
|
|
@@ -29,7 +29,7 @@ const parseAccept = (accept: string): AcceptEntry[] =>
|
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Whether the client explicitly prefers Markdown over HTML. Browsers never send
|
|
32
|
-
* `text/markdown`, so an ordinary page request (`text/html
|
|
32
|
+
* `text/markdown`, so an ordinary page request (`text/html` or the catch-all wildcard) is false.
|
|
33
33
|
*/
|
|
34
34
|
export const prefersMarkdown = (accept: string | null | undefined): boolean => {
|
|
35
35
|
if (!accept) {
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory runtime data modules.
|
|
3
|
+
*
|
|
4
|
+
* The generated runtime's data snapshots — the page data behind `blume:data`,
|
|
5
|
+
* the parsed API specs, the static search index, the raw-Markdown and
|
|
6
|
+
* content-asset maps, the MCP and Ask corpora, and the rendered RSS feeds —
|
|
7
|
+
* used to be written under `.blume/src/generated/*.json` and imported by the
|
|
8
|
+
* generated pages through aliases or relative paths. `generateRuntime` now
|
|
9
|
+
* publishes them here and `runtimeModulesPlugin` serves them to Vite as
|
|
10
|
+
* virtual modules, so a regeneration never round-trips the disk or waits on a
|
|
11
|
+
* file watcher: publishing invalidates the changed modules in every live dev
|
|
12
|
+
* server (Vite walks the importers, so the pages that render them re-evaluate
|
|
13
|
+
* on the next request) and asks the browser to reload — the same effect a JSON
|
|
14
|
+
* file change used to reach through the watcher, minus the write and the
|
|
15
|
+
* watch debounce.
|
|
16
|
+
*
|
|
17
|
+
* The registry hangs off `globalThis`, not module state. On a published
|
|
18
|
+
* install the CLI bundle (`dist/cli`) carries its own copy of this module,
|
|
19
|
+
* separate from the one Vite loads from `blume/astro` for the generated
|
|
20
|
+
* config, and both must see the same map. `blume dev`, `blume build`, and
|
|
21
|
+
* `blume check` all run Astro in-process, so the map the CLI fills is the map
|
|
22
|
+
* the plugin reads.
|
|
23
|
+
*
|
|
24
|
+
* `blume eject` keeps the file form: the ejected project has no CLI to
|
|
25
|
+
* publish, so its `astro.config.mjs` aliases each id to the JSON file eject
|
|
26
|
+
* writes under `src/generated/` — the names in {@link RUNTIME_MODULE_FILES}.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
export type RuntimeModuleId =
|
|
30
|
+
| "blume:ask-data"
|
|
31
|
+
| "blume:content-assets"
|
|
32
|
+
| "blume:data"
|
|
33
|
+
| "blume:mcp-data"
|
|
34
|
+
| "blume:openapi"
|
|
35
|
+
| "blume:raw-markdown"
|
|
36
|
+
| "blume:rss"
|
|
37
|
+
| "blume:search-index";
|
|
38
|
+
|
|
39
|
+
/** Virtual module id → the `src/generated` JSON file eject writes for it. */
|
|
40
|
+
export const RUNTIME_MODULE_FILES: ReadonlyMap<RuntimeModuleId, string> =
|
|
41
|
+
new Map([
|
|
42
|
+
["blume:ask-data", "ask-data.json"],
|
|
43
|
+
["blume:content-assets", "content-assets.json"],
|
|
44
|
+
["blume:data", "data.json"],
|
|
45
|
+
["blume:mcp-data", "mcp-data.json"],
|
|
46
|
+
["blume:openapi", "openapi.json"],
|
|
47
|
+
["blume:raw-markdown", "raw-markdown.json"],
|
|
48
|
+
["blume:rss", "rss.json"],
|
|
49
|
+
["blume:search-index", "search.json"],
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
const RUNTIME_MODULE_IDS: ReadonlySet<string> = new Set(
|
|
53
|
+
RUNTIME_MODULE_FILES.keys()
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
/** Rollup's virtual-module convention: `\0` keeps other plugins off the id. */
|
|
57
|
+
const RESOLVED_PREFIX = "\0";
|
|
58
|
+
|
|
59
|
+
/** A node in Vite's module graph; only its identity matters here. */
|
|
60
|
+
interface RuntimeModuleNode {
|
|
61
|
+
id: string | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The Vite dev-server slice the registry touches (structurally typed, like
|
|
66
|
+
* every Blume-authored Vite plugin — see `includeHmrPlugin`).
|
|
67
|
+
*/
|
|
68
|
+
export interface RuntimeModuleServer {
|
|
69
|
+
httpServer?: {
|
|
70
|
+
once: (event: "close", listener: () => void) => void;
|
|
71
|
+
} | null;
|
|
72
|
+
moduleGraph: {
|
|
73
|
+
getModuleById: (id: string) => RuntimeModuleNode | undefined;
|
|
74
|
+
invalidateModule: (mod: RuntimeModuleNode) => void;
|
|
75
|
+
};
|
|
76
|
+
ws: { send: (payload: { type: "full-reload" }) => void };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface RuntimeModuleRegistry {
|
|
80
|
+
/** Published JSON text by module id. */
|
|
81
|
+
modules: Map<string, string>;
|
|
82
|
+
/** Live dev servers to invalidate on publish. */
|
|
83
|
+
servers: Set<RuntimeModuleServer>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const REGISTRY_KEY = Symbol.for("blume.runtime-modules");
|
|
87
|
+
|
|
88
|
+
type RegistryHost = typeof globalThis & {
|
|
89
|
+
[REGISTRY_KEY]?: RuntimeModuleRegistry;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const registry = (): RuntimeModuleRegistry => {
|
|
93
|
+
// SAFETY: the registry is stashed on globalThis under a well-known symbol so
|
|
94
|
+
// every copy of this module in the process shares it; the intersection only
|
|
95
|
+
// names that slot.
|
|
96
|
+
const host = globalThis as RegistryHost;
|
|
97
|
+
host[REGISTRY_KEY] ??= { modules: new Map(), servers: new Set() };
|
|
98
|
+
return host[REGISTRY_KEY];
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/** The published JSON text for a module, if any (tests and diagnostics). */
|
|
102
|
+
export const readRuntimeModule = (id: RuntimeModuleId): string | undefined =>
|
|
103
|
+
registry().modules.get(id);
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Replace the published snapshot set with `modules` (an id absent from the map
|
|
107
|
+
* is unpublished — its feature was switched off). Returns the ids whose text
|
|
108
|
+
* changed; each is invalidated in every live dev server, followed by one
|
|
109
|
+
* full-reload per server. Nothing is sent when nothing changed, so a
|
|
110
|
+
* regeneration triggered by an unrelated edit stays quiet — the same contract
|
|
111
|
+
* `writeIfChanged` gave the file form.
|
|
112
|
+
*/
|
|
113
|
+
export const publishRuntimeModules = (
|
|
114
|
+
modules: ReadonlyMap<RuntimeModuleId, string>
|
|
115
|
+
): RuntimeModuleId[] => {
|
|
116
|
+
const { modules: current, servers } = registry();
|
|
117
|
+
const changed: RuntimeModuleId[] = [];
|
|
118
|
+
for (const id of RUNTIME_MODULE_FILES.keys()) {
|
|
119
|
+
const next = modules.get(id);
|
|
120
|
+
if (current.get(id) === next) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (next === undefined) {
|
|
124
|
+
current.delete(id);
|
|
125
|
+
} else {
|
|
126
|
+
current.set(id, next);
|
|
127
|
+
}
|
|
128
|
+
changed.push(id);
|
|
129
|
+
}
|
|
130
|
+
if (changed.length === 0) {
|
|
131
|
+
return changed;
|
|
132
|
+
}
|
|
133
|
+
for (const server of servers) {
|
|
134
|
+
for (const id of changed) {
|
|
135
|
+
const mod = server.moduleGraph.getModuleById(`${RESOLVED_PREFIX}${id}`);
|
|
136
|
+
if (mod) {
|
|
137
|
+
server.moduleGraph.invalidateModule(mod);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
server.ws.send({ type: "full-reload" });
|
|
141
|
+
}
|
|
142
|
+
return changed;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export interface RuntimeModulesPlugin {
|
|
146
|
+
configureServer: (server: RuntimeModuleServer) => void;
|
|
147
|
+
enforce: "pre";
|
|
148
|
+
load: (id: string) => string | undefined;
|
|
149
|
+
name: string;
|
|
150
|
+
resolveId: (id: string) => string | undefined;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Serve the published runtime modules to Vite. `enforce: "pre"` claims the
|
|
155
|
+
* `blume:*` ids before Vite's resolver would try (and fail) to find them as
|
|
156
|
+
* packages. Loading an unpublished id is a hard error rather than an empty
|
|
157
|
+
* module: the generated pages only import a module when its data exists, so a
|
|
158
|
+
* miss means the config was run outside the CLI that publishes.
|
|
159
|
+
*/
|
|
160
|
+
export const runtimeModulesPlugin = (): RuntimeModulesPlugin => ({
|
|
161
|
+
configureServer(server) {
|
|
162
|
+
const { servers } = registry();
|
|
163
|
+
servers.add(server);
|
|
164
|
+
// Astro restarts the dev container in place on a config change (and the
|
|
165
|
+
// CLI restarts it on a route-set change): the old Vite server closes and
|
|
166
|
+
// a new one registers, so drop the stale handle rather than invalidating
|
|
167
|
+
// into a dead graph.
|
|
168
|
+
server.httpServer?.once("close", () => {
|
|
169
|
+
servers.delete(server);
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
enforce: "pre",
|
|
173
|
+
load(id) {
|
|
174
|
+
if (!id.startsWith(RESOLVED_PREFIX)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const moduleId = id.slice(RESOLVED_PREFIX.length);
|
|
178
|
+
if (!RUNTIME_MODULE_IDS.has(moduleId)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const text = registry().modules.get(moduleId);
|
|
182
|
+
if (text === undefined) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`Blume runtime module "${moduleId}" was requested before it was published. Run the generated project through the Blume CLI (blume dev / blume build), which publishes the runtime data before starting Astro.`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
// A JSON.parse over a string literal evaluates faster than an equivalent
|
|
188
|
+
// object literal for large snapshots (V8's guidance; Vite's own JSON
|
|
189
|
+
// plugin does the same past a size threshold).
|
|
190
|
+
return `export default JSON.parse(${JSON.stringify(text)});\n`;
|
|
191
|
+
},
|
|
192
|
+
name: "blume:runtime-modules",
|
|
193
|
+
resolveId(id) {
|
|
194
|
+
return RUNTIME_MODULE_IDS.has(id) ? `${RESOLVED_PREFIX}${id}` : undefined;
|
|
195
|
+
},
|
|
196
|
+
});
|