blume 0.6.0 → 0.6.2
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/dist/cli/index.js +6070 -5718
- package/dist/cli/index.js.map +41 -40
- package/dist/types/core/config-input.d.ts +749 -0
- package/dist/types/core/config.d.ts +126 -3
- package/dist/types/core/schema.d.ts +10 -27
- package/dist/types/core/sources/types.d.ts +6 -0
- package/dist/types/index.d.ts +2 -1
- package/docs/advanced/changelog.mdx +10 -2
- package/docs/configuration/index.mdx +0 -2
- package/docs/content/syntax.mdx +4 -8
- package/package.json +1 -1
- package/src/astro/generate.ts +59 -24
- package/src/astro/markdown-negotiation.ts +12 -3
- package/src/astro/templates.ts +94 -12
- package/src/cli/commands/build.ts +26 -1
- package/src/cli/commands/dev.ts +30 -14
- package/src/cli/commands/doctor.ts +35 -7
- package/src/cli/commands/sync.ts +14 -2
- package/src/cli/dev-lock.ts +40 -10
- package/src/cli/env.ts +5 -1
- package/src/components/content/Update.astro +12 -2
- package/src/components/content/changelog-element.ts +62 -0
- package/src/components/islands/ask-ai.tsx +3 -1
- package/src/components/islands/hooks.ts +5 -1
- package/src/components/layout/Header.astro +10 -2
- package/src/components/layout/NavSelector.astro +5 -3
- package/src/components/layout/PageLayout.astro +2 -1
- package/src/components/layout/ReferenceLayout.astro +1 -0
- package/src/components/layout/RootLayout.astro +47 -10
- package/src/components/layout/Search.astro +8 -3
- package/src/components/layout/nav-utils.ts +7 -3
- package/src/core/config-input.ts +923 -0
- package/src/core/config.ts +126 -3
- package/src/core/i18n.ts +6 -5
- package/src/core/links.ts +16 -1
- package/src/core/meta.ts +112 -52
- package/src/core/navigation.ts +15 -5
- package/src/core/project-graph.ts +68 -2
- package/src/core/schema.ts +8 -14
- package/src/core/sources/assets.ts +21 -5
- package/src/core/sources/cache.ts +19 -1
- package/src/core/sources/github-releases.ts +9 -3
- package/src/core/sources/mdx-remote.ts +14 -4
- package/src/core/sources/normalize.ts +13 -1
- package/src/core/sources/notion.ts +43 -7
- package/src/core/sources/resolve.ts +44 -1
- package/src/core/sources/sanity.ts +9 -3
- package/src/core/sources/types.ts +6 -0
- package/src/deploy/adapter-output.ts +82 -0
- package/src/deploy/rss.ts +3 -1
- package/src/index.ts +1 -1
- package/src/markdown/code-title.ts +11 -4
- package/src/markdown/index.ts +28 -30
- package/src/markdown/math.ts +3 -2
- package/src/markdown/package-commands.ts +13 -0
- package/src/og/card.ts +3 -1
- package/src/openapi/model.ts +2 -1
- package/src/openapi/parse.ts +9 -1
- package/src/openapi/references.ts +11 -1
- package/src/openapi/render-mdx.ts +30 -3
- package/src/openapi/source.ts +3 -1
- package/src/registry/eject.ts +21 -14
- package/src/search/documents.ts +4 -1
- package/src/theme/entry.ts +10 -3
- package/src/theme/icons.ts +7 -11
|
@@ -25,7 +25,13 @@ const ENTITIES: Record<string, string> = {
|
|
|
25
25
|
// SDK…" is common spec prose). Entity-escape the keyword's first letter so the
|
|
26
26
|
// construct can't match; it still renders as the literal word.
|
|
27
27
|
const MDX_ESM_KEYWORD = /^(?<keyword>import|export)\b/gmu;
|
|
28
|
-
|
|
28
|
+
// Backtick code — inline spans and fences alike — is already literal in MDX,
|
|
29
|
+
// and entities are NOT decoded inside it, so escaping there would render the
|
|
30
|
+
// entity text verbatim (`/pets/{petId}`). Matching any balanced
|
|
31
|
+
// backtick run covers `code`, ``code``, and ```fences``` in one shot.
|
|
32
|
+
const BACKTICK_CODE = /(?<bt>`+)[\s\S]*?\k<bt>/gu;
|
|
33
|
+
|
|
34
|
+
const escapeProse = (text: string): string =>
|
|
29
35
|
text
|
|
30
36
|
.replace(MDX_UNSAFE, (char) => ENTITIES[char] ?? char)
|
|
31
37
|
.replace(
|
|
@@ -33,6 +39,19 @@ const mdxSafe = (text: string): string =>
|
|
|
33
39
|
(keyword) => `&#${keyword.codePointAt(0)};${keyword.slice(1)}`
|
|
34
40
|
);
|
|
35
41
|
|
|
42
|
+
/** Escape MDX-special syntax in prose while leaving backtick code verbatim. */
|
|
43
|
+
const mdxSafe = (text: string): string => {
|
|
44
|
+
let out = "";
|
|
45
|
+
let cursor = 0;
|
|
46
|
+
for (const match of text.matchAll(BACKTICK_CODE)) {
|
|
47
|
+
const start = match.index ?? 0;
|
|
48
|
+
out += escapeProse(text.slice(cursor, start));
|
|
49
|
+
out += match[0];
|
|
50
|
+
cursor = start + match[0].length;
|
|
51
|
+
}
|
|
52
|
+
return out + escapeProse(text.slice(cursor));
|
|
53
|
+
};
|
|
54
|
+
|
|
36
55
|
/** Frontmatter + body for one operation or overview page. */
|
|
37
56
|
export interface RenderedPage {
|
|
38
57
|
data: Record<string, unknown>;
|
|
@@ -80,8 +99,16 @@ export const overviewMdx = (spec: ApiSpecData): RenderedPage => {
|
|
|
80
99
|
// markdown pipeline gives them ids, permalink anchors, and table-of-contents
|
|
81
100
|
// entries; only the operation-link list defers to a component.
|
|
82
101
|
const operations = Object.values(spec.operations);
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
// Dedupe by slug: two declared tags that slugify identically (`Store` and
|
|
103
|
+
// `store`) must render one section, not the same operation list twice.
|
|
104
|
+
const sections: typeof spec.tags = [];
|
|
105
|
+
const known = new Set<string>();
|
|
106
|
+
for (const tag of spec.tags) {
|
|
107
|
+
if (!known.has(tag.slug)) {
|
|
108
|
+
known.add(tag.slug);
|
|
109
|
+
sections.push(tag);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
85
112
|
for (const operation of operations) {
|
|
86
113
|
if (!known.has(operation.tagSlug)) {
|
|
87
114
|
known.add(operation.tagSlug);
|
package/src/openapi/source.ts
CHANGED
|
@@ -59,8 +59,10 @@ const specEntries = (
|
|
|
59
59
|
);
|
|
60
60
|
// Overview last so an operation sets the section's routePath before the index
|
|
61
61
|
// page is inserted (the group's routePath is derived from its first child).
|
|
62
|
+
// A root-mounted reference refs `index.mdx`, not `/index.mdx`.
|
|
63
|
+
const base = routeToRef(spec.route);
|
|
62
64
|
entries.push(
|
|
63
|
-
toEntry(overviewMdx(spec), `${
|
|
65
|
+
toEntry(overviewMdx(spec), base ? `${base}/index.mdx` : "index.mdx")
|
|
64
66
|
);
|
|
65
67
|
return entries;
|
|
66
68
|
};
|
package/src/registry/eject.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
buildRuntimeData,
|
|
13
13
|
collectStaged,
|
|
14
14
|
detectNeedsReact,
|
|
15
|
+
detectUsesMath,
|
|
15
16
|
} from "../astro/generate.ts";
|
|
16
17
|
import { discoverIslands } from "../astro/islands.ts";
|
|
17
18
|
import { customOgRoutes, discoverPages, routeIsTaken } from "../astro/pages.ts";
|
|
@@ -102,19 +103,25 @@ export const eject = async (root: string): Promise<string[]> => {
|
|
|
102
103
|
const exportPdf = config.export.pdf;
|
|
103
104
|
const exportEpub = config.export.epub;
|
|
104
105
|
|
|
105
|
-
const [
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
106
|
+
const [
|
|
107
|
+
pages,
|
|
108
|
+
needsReactRaw,
|
|
109
|
+
usesMath,
|
|
110
|
+
userTheme,
|
|
111
|
+
rawMarkdown,
|
|
112
|
+
islands,
|
|
113
|
+
examples,
|
|
114
|
+
] = await Promise.all([
|
|
115
|
+
context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),
|
|
116
|
+
detectNeedsReact(root),
|
|
117
|
+
detectUsesMath(root),
|
|
118
|
+
context.themeFile
|
|
119
|
+
? readFile(context.themeFile, "utf-8")
|
|
120
|
+
: Promise.resolve(""),
|
|
121
|
+
buildRawMarkdown(project),
|
|
122
|
+
discoverIslands(root),
|
|
123
|
+
discoverExamples(root, config.examples),
|
|
124
|
+
]);
|
|
118
125
|
// Island/example frameworks drive which Astro renderers the ejected config
|
|
119
126
|
// wires in; React also switches on for project `.tsx`/`.jsx` and Ask AI.
|
|
120
127
|
const frameworks = new Set<string>([
|
|
@@ -192,7 +199,7 @@ export const eject = async (root: string): Promise<string[]> => {
|
|
|
192
199
|
askEnabled,
|
|
193
200
|
exportEpub,
|
|
194
201
|
exportPdf,
|
|
195
|
-
mathEnabled:
|
|
202
|
+
mathEnabled: usesMath,
|
|
196
203
|
needsReact,
|
|
197
204
|
}),
|
|
198
205
|
path: join(srcDir, "pages", "[...slug].astro"),
|
package/src/search/documents.ts
CHANGED
|
@@ -39,7 +39,10 @@ export interface SearchRecord {
|
|
|
39
39
|
|
|
40
40
|
const CODE_FENCE = /```[\s\S]*?```/gu;
|
|
41
41
|
const INLINE_CODE = /`(?<code>[^`]+)`/gu;
|
|
42
|
-
|
|
42
|
+
// Tag-shaped only: a name (or closing slash/fragment) right after `<`, and no
|
|
43
|
+
// newline inside. A bare `<` in prose ("costs < 5 credits") must not swallow
|
|
44
|
+
// everything up to some later `>` — potentially whole paragraphs.
|
|
45
|
+
const HTML_OR_JSX = /<\/?[a-zA-Z][^\n<>]*>|<\/?>/gu;
|
|
43
46
|
const IMAGE = /!\[[^\]]*\]\([^)]*\)/gu;
|
|
44
47
|
const LINK = /\[(?<text>[^\]]*)\]\([^)]*\)/gu;
|
|
45
48
|
const HEADING_MARK = /^#{1,6}\s+/gmu;
|
package/src/theme/entry.ts
CHANGED
|
@@ -435,6 +435,13 @@ blume-diff {
|
|
|
435
435
|
font-size: 0.8125rem;
|
|
436
436
|
}
|
|
437
437
|
|
|
438
|
+
/* GFM renders cells as <td><code> directly, which the descendant form alone
|
|
439
|
+
never matches (a cell is not its own descendant). */
|
|
440
|
+
.prose :where(td, th) > code,
|
|
441
|
+
.prose :where(td, th) :not(pre) > code {
|
|
442
|
+
white-space: nowrap;
|
|
443
|
+
}
|
|
444
|
+
|
|
438
445
|
blume-tabs pre,
|
|
439
446
|
.not-prose > div > pre {
|
|
440
447
|
background: var(--blume-code-background);
|
|
@@ -610,9 +617,9 @@ pre:has(.line.focused):hover .line:not(.focused) {
|
|
|
610
617
|
content: none;
|
|
611
618
|
}
|
|
612
619
|
|
|
613
|
-
/* Inline code highlighting
|
|
614
|
-
|
|
615
|
-
|
|
620
|
+
/* Inline code highlighting: Shiki colors the tokens of a \`code\`{:lang} snippet
|
|
621
|
+
via the same dual-theme CSS variables as fenced blocks, keeping the inline
|
|
622
|
+
pill background. Always on — it only fires on the trailing {:lang} marker. */
|
|
616
623
|
.prose code.blume-inline-code span {
|
|
617
624
|
color: var(--shiki-light);
|
|
618
625
|
}
|
package/src/theme/icons.ts
CHANGED
|
@@ -79,14 +79,10 @@ export const resolveIcon = (name: string): ResolvedIcon | null => {
|
|
|
79
79
|
return fromSet(DEFAULT_SET, normalized);
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
? normalized.slice(normalized.indexOf(":") + 1)
|
|
90
|
-
: normalized;
|
|
91
|
-
return Object.values(SETS).some((set) => getIconData(set, bare) !== null);
|
|
92
|
-
};
|
|
82
|
+
/**
|
|
83
|
+
* Whether a name resolves to a renderable icon. Exactly mirrors
|
|
84
|
+
* {@link resolveIcon}: a laxer check (e.g. matching the bare name under an
|
|
85
|
+
* unknown prefix) would let callers suppress their fallback and diagnostics
|
|
86
|
+
* for a name `<Icon>` then renders as nothing.
|
|
87
|
+
*/
|
|
88
|
+
export const hasIcon = (name: string): boolean => resolveIcon(name) !== null;
|