blume 1.6.4 → 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 +41 -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 -27587
- 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/config-input.d.ts +10 -0
- package/dist/types/core/package-root.d.ts +1 -1
- package/dist/types/core/schema.d.ts +74 -1
- package/docs/02-deployment.mdx +1 -1
- package/docs/configuration/analytics.mdx +21 -2
- package/docs/configuration/ask-ai.mdx +1 -1
- package/docs/configuration/customization.mdx +2 -9
- package/docs/content/syntax.mdx +1 -1
- package/docs/reference/cli.mdx +1 -1
- package/package.json +16 -14
- 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 +25 -29
- 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/content/mermaid-element.ts +8 -0
- package/src/components/layout/Analytics.astro +20 -1
- package/src/components/layout/PageLayout.astro +14 -3
- package/src/components/layout/ReferenceLayout.astro +15 -4
- package/src/components/layout/RootLayout.astro +15 -4
- package/src/components/layout/analytics-client.ts +2 -1
- package/src/components/layout/page-locale.ts +29 -0
- package/src/components/openapi/AsyncApiOperation.astro +5 -3
- package/src/components/openapi/Authorization.astro +4 -6
- package/src/components/openapi/Bindings.astro +2 -2
- package/src/components/openapi/Description.astro +109 -0
- package/src/components/openapi/GraphqlFieldsTable.astro +5 -7
- package/src/components/openapi/GraphqlOperation.astro +4 -3
- package/src/components/openapi/GraphqlType.astro +4 -6
- package/src/components/openapi/ParametersTable.astro +5 -7
- package/src/components/openapi/RequestBody.astro +2 -4
- package/src/components/openapi/Responses.astro +4 -3
- package/src/components/openapi/SchemaProperty.astro +11 -6
- package/src/components/openapi/description.ts +91 -0
- package/src/core/api-name.ts +18 -0
- package/src/core/code-fences.ts +48 -0
- package/src/core/config-input.ts +10 -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 +27 -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
- package/src/theme/entry.ts +9 -2
|
@@ -2,6 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { readdir, readFile } from "node:fs/promises";
|
|
3
3
|
import { builtinModules } from "node:module";
|
|
4
4
|
|
|
5
|
+
import { init, parse } from "es-module-lexer";
|
|
5
6
|
import { dirname, join, relative } from "pathe";
|
|
6
7
|
import { z } from "zod";
|
|
7
8
|
|
|
@@ -65,32 +66,76 @@ export const packageName = (specifier: string): string | null => {
|
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
|
-
*
|
|
69
|
-
* "x"`
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* is a code sample serialized into a string (the MCP snapshot carries page
|
|
73
|
-
* Markdown), not module syntax.
|
|
69
|
+
* Fallback static specifiers for a module the lexer rejects: a side-effect
|
|
70
|
+
* `import "x"` or an `import`/`export … from "x"` clause. Only `import` takes
|
|
71
|
+
* the bare-string form — `export "x"` is not syntax, so a runtime message
|
|
72
|
+
* quoting `export 'ALL'` must not match.
|
|
74
73
|
*/
|
|
75
74
|
const STATIC_IMPORT =
|
|
76
|
-
/(?:^|[;\s}])(?:import\s*
|
|
75
|
+
/(?:^|[;\s}])(?:import\s*["'](?<bare>[^"'\n]+)["']|(?:import|export)\s*[\w$*{},\s]*?\s*from\s*["'](?<from>[^"'\n]+)["'])/gu;
|
|
77
76
|
|
|
78
|
-
/**
|
|
79
|
-
const DYNAMIC_IMPORT =
|
|
80
|
-
/\bimport\(\s*(?<!\\)["'](?<dynamic>[^"'\n]+)["']\s*\)/gu;
|
|
77
|
+
/** Fallback dynamic `import("…")` specifiers. */
|
|
78
|
+
const DYNAMIC_IMPORT = /\bimport\(\s*["'](?<dynamic>[^"'\n]+)["']\s*\)/gu;
|
|
81
79
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Textual best effort for a module `es-module-lexer` cannot parse: every
|
|
82
|
+
* quoted specifier in import position, string contents included.
|
|
83
|
+
*/
|
|
84
|
+
const scannedSpecifiers = (source: string): string[] => {
|
|
85
|
+
const specifiers: string[] = [];
|
|
85
86
|
for (const pattern of [STATIC_IMPORT, DYNAMIC_IMPORT]) {
|
|
86
87
|
for (const match of source.matchAll(pattern)) {
|
|
87
88
|
const groups = match.groups ?? {};
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
specifiers.push(groups.bare ?? groups.from ?? groups.dynamic ?? "");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return specifiers;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Every specifier a module's syntax imports, read with `es-module-lexer` so
|
|
97
|
+
* text inside string literals never counts. That matters for Blume's data
|
|
98
|
+
* chunks: the MCP snapshot is `JSON.parse("…")` over every page's Markdown,
|
|
99
|
+
* and a code sample there reading `import { config } from 'dotenv'` is prose
|
|
100
|
+
* to a bundle audit, not a module the function needs. `import.meta` carries no
|
|
101
|
+
* specifier and a template-literal `import(\`pkg/${x}\`)` is a glob the
|
|
102
|
+
* bundler already resolved, so neither names a package.
|
|
103
|
+
*/
|
|
104
|
+
const lexedSpecifiers = (source: string, name: string): string[] => {
|
|
105
|
+
const [imports] = parse(source, name);
|
|
106
|
+
const specifiers: string[] = [];
|
|
107
|
+
for (const entry of imports) {
|
|
108
|
+
if (entry.type === "dynamic" && entry.glob) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (entry.specifier) {
|
|
112
|
+
specifiers.push(entry.specifier);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return specifiers;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Every bare package name a module's source imports. A module the lexer
|
|
120
|
+
* rejects (an unterminated string, an invalid escape in a specifier) falls
|
|
121
|
+
* back to the textual scan rather than going unaudited.
|
|
122
|
+
*/
|
|
123
|
+
export const importedPackages = async (
|
|
124
|
+
source: string,
|
|
125
|
+
name = "module"
|
|
126
|
+
): Promise<string[]> => {
|
|
127
|
+
await init();
|
|
128
|
+
let specifiers: string[];
|
|
129
|
+
try {
|
|
130
|
+
specifiers = lexedSpecifiers(source, name);
|
|
131
|
+
} catch {
|
|
132
|
+
specifiers = scannedSpecifiers(source);
|
|
133
|
+
}
|
|
134
|
+
const names = new Set<string>();
|
|
135
|
+
for (const specifier of specifiers) {
|
|
136
|
+
const packageId = packageName(specifier);
|
|
137
|
+
if (packageId) {
|
|
138
|
+
names.add(packageId);
|
|
94
139
|
}
|
|
95
140
|
}
|
|
96
141
|
return [...names];
|
|
@@ -164,7 +209,8 @@ export const auditFunctionBundle = async (
|
|
|
164
209
|
for (const file of await listModules(serverDir)) {
|
|
165
210
|
// oxlint-disable-next-line no-await-in-loop -- sequential read keeps the importer lists ordered
|
|
166
211
|
const source = await readFile(file, "utf-8");
|
|
167
|
-
|
|
212
|
+
// oxlint-disable-next-line no-await-in-loop -- the lexer runs per file, in the same order
|
|
213
|
+
for (const name of await importedPackages(source, file)) {
|
|
168
214
|
if (resolvable(name, dirname(file), funcDir)) {
|
|
169
215
|
continue;
|
|
170
216
|
}
|
package/src/deploy/sitemap.ts
CHANGED
|
@@ -36,6 +36,12 @@ export interface SitemapFile {
|
|
|
36
36
|
xml: string;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/** The build log line for an emitted sitemap set: one file, or an index. */
|
|
40
|
+
export const describeSitemapFiles = (files: readonly SitemapFile[]): string =>
|
|
41
|
+
files.length === 1
|
|
42
|
+
? "Generated sitemap.xml"
|
|
43
|
+
: `Generated sitemap.xml (index of ${files.length - 1} sitemap files)`;
|
|
44
|
+
|
|
39
45
|
/**
|
|
40
46
|
* The sitemaps.org cap on `<url>` entries in a single file. Beyond it,
|
|
41
47
|
* `sitemap.xml` becomes a sitemap index pointing at numbered chunk files —
|
|
@@ -235,21 +235,6 @@ export const buildNegotiationRoutes = (
|
|
|
235
235
|
/** The `src` of the injected homepage `Link` header route. */
|
|
236
236
|
const HOME_SRC = "^/$";
|
|
237
237
|
|
|
238
|
-
/**
|
|
239
|
-
* Permanent redirect from any trailing-slash URL to its slashless twin, so
|
|
240
|
-
* `/docs/` and `/docs` don't serve as duplicate URLs (canonicals, sitemap, and
|
|
241
|
-
* hreflang all use the slashless form; the root `/` is untouched — `.+`
|
|
242
|
-
* requires a non-empty path). Spliced into the main phase before `handle:
|
|
243
|
-
* "filesystem"`, after the Markdown rewrites, so an agent's `Accept:
|
|
244
|
-
* text/markdown` request on a slashed URL still rewrites without the extra
|
|
245
|
-
* hop. Vercel carries the query string over to the `Location` target itself.
|
|
246
|
-
*/
|
|
247
|
-
export const TRAILING_SLASH_REDIRECT: VercelRoute = {
|
|
248
|
-
headers: { Location: "/$1" },
|
|
249
|
-
src: "^/(.+)/$",
|
|
250
|
-
status: 308,
|
|
251
|
-
};
|
|
252
|
-
|
|
253
238
|
/**
|
|
254
239
|
* Whether a route is one this module previously injected, so re-injection
|
|
255
240
|
* replaces rather than duplicates. Rewrites are identified by their `accept`
|
|
@@ -273,9 +258,7 @@ const isNegotiationRoute = (route: VercelRoute): boolean =>
|
|
|
273
258
|
(route.continue === true &&
|
|
274
259
|
isString(route.headers?.link) &&
|
|
275
260
|
route.src === HOME_SRC &&
|
|
276
|
-
Object.keys(route).length === 3)
|
|
277
|
-
(route.status === TRAILING_SLASH_REDIRECT.status &&
|
|
278
|
-
route.src === TRAILING_SLASH_REDIRECT.src);
|
|
261
|
+
Object.keys(route).length === 3);
|
|
279
262
|
|
|
280
263
|
/**
|
|
281
264
|
* Splice the negotiation routes into a Build Output `config.json`, plus — when
|
|
@@ -285,9 +268,11 @@ const isNegotiationRoute = (route: VercelRoute): boolean =>
|
|
|
285
268
|
* rides on the prerendered homepage response. `contentTypeOverrides` maps static-dir
|
|
286
269
|
* relative paths to media types via the Build Output `overrides` field — the
|
|
287
270
|
* platform's mechanism for extensionless static files (e.g. the Web Bot Auth
|
|
288
|
-
* signature directory). The trailing-slash
|
|
289
|
-
*
|
|
290
|
-
*
|
|
271
|
+
* signature directory). The trailing-slash redirect that collapses `/docs/`
|
|
272
|
+
* onto `/docs` is not spliced here: the generated config sets Astro's
|
|
273
|
+
* `trailingSlash: "never"`, which the adapter turns into the platform's own
|
|
274
|
+
* 308 route ahead of everything below (so a slashed Markdown request takes
|
|
275
|
+
* that hop first, then negotiates). For each 404 twin the build emitted (`notFound.markdown` for
|
|
291
276
|
* `404.md`, `notFound.json` for `404.json`), its routes go into the miss
|
|
292
277
|
* phase right before the adapter's `/404.html` fallback — and nowhere when
|
|
293
278
|
* that fallback is absent, since a `dest` with no file behind it would serve
|
|
@@ -342,15 +327,8 @@ export const injectNegotiationRoutes = (
|
|
|
342
327
|
}
|
|
343
328
|
// Headers first: `continue` routes accumulate, so a request the rewrite
|
|
344
329
|
// route then terminates (Markdown negotiation on the homepage) still carries
|
|
345
|
-
// the Link header.
|
|
346
|
-
|
|
347
|
-
routes.splice(
|
|
348
|
-
filesystemIndex,
|
|
349
|
-
0,
|
|
350
|
-
...headerRoutes,
|
|
351
|
-
...rewriteRoutes,
|
|
352
|
-
TRAILING_SLASH_REDIRECT
|
|
353
|
-
);
|
|
330
|
+
// the Link header.
|
|
331
|
+
routes.splice(filesystemIndex, 0, ...headerRoutes, ...rewriteRoutes);
|
|
354
332
|
const notFoundRoutes = [
|
|
355
333
|
...(notFound.markdown ? NOT_FOUND_MARKDOWN_ROUTES : []),
|
|
356
334
|
...(notFound.json ? NOT_FOUND_JSON_ROUTES : []),
|
package/src/og/card.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
|
|
3
|
-
import { imageSize } from "image-size";
|
|
4
3
|
import { render } from "takumi-js";
|
|
5
4
|
import type { RenderOptions } from "takumi-js";
|
|
6
5
|
import { container, googleFonts, image, text } from "takumi-js/helpers";
|
|
7
6
|
import type { FontSubset, GoogleFontFamily, Node } from "takumi-js/helpers";
|
|
8
7
|
|
|
8
|
+
import { svgDimensions } from "../core/svg-dimensions.ts";
|
|
9
9
|
import { ACCENTS, isAccentPreset } from "../theme/palette.ts";
|
|
10
10
|
import { OG_IMAGE_HEIGHT, OG_IMAGE_WIDTH } from "./dimensions.ts";
|
|
11
11
|
|
|
@@ -234,19 +234,13 @@ const MARK_HEIGHT = 32;
|
|
|
234
234
|
const MARK_MAX_WIDTH = 240;
|
|
235
235
|
/**
|
|
236
236
|
* The SVG's aspect ratio (w/h), or null when no usable dimensions exist (the
|
|
237
|
-
* caller falls back to a square mark).
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
* `viewBox = "…"`, newline-separated values — which shipped visibly-squashed
|
|
241
|
-
* marks instead of failing loudly.
|
|
237
|
+
* caller falls back to a square mark). The shared root-tag parser reads
|
|
238
|
+
* explicit width/height and falls back to the viewBox, so the card and the
|
|
239
|
+
* header measure one logo the same way.
|
|
242
240
|
*/
|
|
243
241
|
const logoAspect = (svg: string): number | null => {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
return width && height ? width / height : null;
|
|
247
|
-
} catch {
|
|
248
|
-
return null;
|
|
249
|
-
}
|
|
242
|
+
const size = svgDimensions(svg);
|
|
243
|
+
return size ? size.width / size.height : null;
|
|
250
244
|
};
|
|
251
245
|
|
|
252
246
|
// Render the configured logo as the brand mark. A `currentColor` logo carries
|
|
@@ -3,6 +3,7 @@ import { fromMarkdown } from "mdast-util-from-markdown";
|
|
|
3
3
|
import { toString as mdastToString } from "mdast-util-to-string";
|
|
4
4
|
import stringWidth from "string-width";
|
|
5
5
|
|
|
6
|
+
import { apiNamePhrase } from "../core/api-name.ts";
|
|
6
7
|
import { columnsPrefix } from "../core/text-width.ts";
|
|
7
8
|
import type { GraphqlMember } from "./graphql.ts";
|
|
8
9
|
import { isGraphqlOperationKind } from "./graphql.ts";
|
|
@@ -158,6 +159,8 @@ const clip = (text: string, max: number): string => {
|
|
|
158
159
|
};
|
|
159
160
|
|
|
160
161
|
const apiName = (spec: ApiSpecData): string => spec.title || spec.label;
|
|
162
|
+
/** The spec's name as "the ___" in generated prose (see `apiNamePhrase`). */
|
|
163
|
+
const apiPhrase = (spec: ApiSpecData): string => apiNamePhrase(apiName(spec));
|
|
161
164
|
|
|
162
165
|
/** Human phrase for each GraphQL page kind, for meta descriptions. */
|
|
163
166
|
const GRAPHQL_MEMBER_PHRASES = {
|
|
@@ -194,16 +197,17 @@ const operationDescription = (
|
|
|
194
197
|
}
|
|
195
198
|
// AsyncAPI operations act on a channel, not an HTTP endpoint; GraphQL pages
|
|
196
199
|
// document a root field or a named type.
|
|
197
|
-
let
|
|
200
|
+
let subject: string;
|
|
198
201
|
if (spec.kind === "asyncapi") {
|
|
199
|
-
|
|
202
|
+
subject = `${operation.method} operation on ${operation.path}`;
|
|
200
203
|
} else if (spec.kind === "graphql") {
|
|
201
204
|
// SAFETY: the GraphQL extractor only ever assigns member kinds as the
|
|
202
205
|
// method (see `extractGraphqlOperations`).
|
|
203
|
-
|
|
206
|
+
subject = `${operation.path} ${GRAPHQL_MEMBER_PHRASES[operation.method as GraphqlMember]}`;
|
|
204
207
|
} else {
|
|
205
|
-
|
|
208
|
+
subject = `${operation.method.toUpperCase()} ${operation.path} endpoint`;
|
|
206
209
|
}
|
|
210
|
+
const suffix = `Reference for the ${subject} in the ${apiPhrase(spec)}.`;
|
|
207
211
|
const prose = clip(
|
|
208
212
|
plainProse(operation.description || operation.summary),
|
|
209
213
|
META_DESCRIPTION_MAX - stringWidth(suffix) - 1
|
|
@@ -350,7 +354,7 @@ export const overviewMdx = (
|
|
|
350
354
|
const seo: RenderedPageData["seo"] = {
|
|
351
355
|
description:
|
|
352
356
|
clip(plainProse(spec.description), META_DESCRIPTION_MAX) ||
|
|
353
|
-
`${
|
|
357
|
+
`${apiPhrase(spec)} reference.`,
|
|
354
358
|
};
|
|
355
359
|
if (reference?.noindex) {
|
|
356
360
|
seo.noindex = true;
|
package/src/registry/eject.ts
CHANGED
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
catchAllPageTemplate,
|
|
31
31
|
changelogIndexTemplate,
|
|
32
32
|
contentConfigTemplate,
|
|
33
|
-
envTemplate,
|
|
34
33
|
exampleMapTemplate,
|
|
35
34
|
exampleWrapperTemplate,
|
|
36
35
|
examplesPageTemplate,
|
|
@@ -427,7 +426,6 @@ export const eject = async (
|
|
|
427
426
|
// Never clobber a hand-tuned tsconfig; only write ours if none exists.
|
|
428
427
|
skipIfExists: true,
|
|
429
428
|
},
|
|
430
|
-
{ content: envTemplate(), path: join(srcDir, "env.d.ts") },
|
|
431
429
|
{
|
|
432
430
|
content: contentConfigTemplate({
|
|
433
431
|
config,
|
package/src/theme/entry.ts
CHANGED
|
@@ -560,9 +560,16 @@ blume-diff {
|
|
|
560
560
|
/* Restore inner padding on every cell. Typography zeroes the first/last cell's
|
|
561
561
|
inline padding so a borderless table aligns to the prose margin; inside the
|
|
562
562
|
framed wrapper that leaves edge text touching the border. The :is() selector
|
|
563
|
-
outweighs Typography's :where()-scoped rules so the outer columns get it too.
|
|
563
|
+
outweighs Typography's :where()-scoped rules so the outer columns get it too.
|
|
564
|
+
|
|
565
|
+
The block padding is 0.75rem against a table line-height near 1.7: at 0.5rem a
|
|
566
|
+
cell whose content wrapped put MORE space between its own two lines than
|
|
567
|
+
between itself and the next row, so a table of wrapping cells read as one
|
|
568
|
+
block rather than as rows. The inline padding stays where it was, deliberately
|
|
569
|
+
— widening it comes out of column width in a capped article, and on one corpus
|
|
570
|
+
that pushed cells fitting on two lines onto three. */
|
|
564
571
|
.blume-table-scroll :is(th, td) {
|
|
565
|
-
padding: 0.
|
|
572
|
+
padding: 0.75rem;
|
|
566
573
|
}
|
|
567
574
|
/* Keep column labels on one line so a two-word header does not wrap into a
|
|
568
575
|
ragged stack; the table just scrolls a little wider instead. Body cells keep
|