shelving 1.222.0 → 1.222.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/extract/DirectoryExtractor.js +2 -2
- package/extract/Extractor.d.ts +1 -1
- package/extract/Extractor.js +1 -1
- package/extract/FileExtractor.d.ts +2 -2
- package/extract/FileExtractor.js +2 -2
- package/extract/MarkupExtractor.d.ts +31 -0
- package/extract/MarkupExtractor.js +52 -0
- package/extract/TypescriptExtractor.js +5 -5
- package/extract/index.d.ts +1 -1
- package/extract/index.js +1 -1
- package/markup/MarkupParser.js +7 -2
- package/package.json +1 -1
- package/ui/docs/DirectoryPage.js +2 -1
- package/ui/docs/DirectoryPage.tsx +2 -0
- package/ui/docs/FilePage.js +2 -1
- package/ui/docs/FilePage.tsx +2 -0
- package/util/element.d.ts +2 -1
- package/util/element.js +6 -1
- package/extract/MarkdownExtractor.d.ts +0 -28
- package/extract/MarkdownExtractor.js +0 -58
|
@@ -5,7 +5,7 @@ import { anyMatch, requirePath, splitPath } from "../util/index.js";
|
|
|
5
5
|
import { requireSlug } from "../util/string.js";
|
|
6
6
|
import { Extractor, mergeTreeElements } from "./Extractor.js";
|
|
7
7
|
import { FileExtractor } from "./FileExtractor.js";
|
|
8
|
-
import {
|
|
8
|
+
import { MarkupExtractor } from "./MarkupExtractor.js";
|
|
9
9
|
import { TypescriptExtractor } from "./TypescriptExtractor.js";
|
|
10
10
|
/**
|
|
11
11
|
* Default list of filenames patterns treated as the directory's index file.
|
|
@@ -15,7 +15,7 @@ import { TypescriptExtractor } from "./TypescriptExtractor.js";
|
|
|
15
15
|
const DEFAULT_INDEX = [/^readme\.txt$/i, /^readme\.md$/i, /^index\.md$/i, /^index\.ts$/i, /^index\.tsx$/i];
|
|
16
16
|
/** Default file extractor dispatch by extension. */
|
|
17
17
|
const DEFAULT_EXTRACTORS = {
|
|
18
|
-
md: new
|
|
18
|
+
md: new MarkupExtractor(),
|
|
19
19
|
ts: new TypescriptExtractor(),
|
|
20
20
|
tsx: new TypescriptExtractor(),
|
|
21
21
|
txt: new FileExtractor(),
|
package/extract/Extractor.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare abstract class Extractor<I, O extends TreeElement = TreeElement>
|
|
|
11
11
|
* Priority used to resolve same-key collisions when merging elements.
|
|
12
12
|
* - Higher-priority elements contribute their `title`, `description`, `path` to the merged result.
|
|
13
13
|
* - Higher-priority elements a prefixed (rather than suffixed) in `children` and `content` when merged.
|
|
14
|
-
* - Defaults to `0`; subclasses override (e.g. `
|
|
14
|
+
* - Defaults to `0`; subclasses override (e.g. `MarkupExtractor` is `10`).
|
|
15
15
|
*/
|
|
16
16
|
readonly priority: number;
|
|
17
17
|
}
|
package/extract/Extractor.js
CHANGED
|
@@ -9,7 +9,7 @@ export class Extractor {
|
|
|
9
9
|
* Priority used to resolve same-key collisions when merging elements.
|
|
10
10
|
* - Higher-priority elements contribute their `title`, `description`, `path` to the merged result.
|
|
11
11
|
* - Higher-priority elements a prefixed (rather than suffixed) in `children` and `content` when merged.
|
|
12
|
-
* - Defaults to `0`; subclasses override (e.g. `
|
|
12
|
+
* - Defaults to `0`; subclasses override (e.g. `MarkupExtractor` is `10`).
|
|
13
13
|
*/
|
|
14
14
|
priority = 0;
|
|
15
15
|
}
|
|
@@ -7,8 +7,8 @@ import { Extractor } from "./Extractor.js";
|
|
|
7
7
|
* - Sets `source` to the file's absolute path (`BunFile.name`); throws `RequiredError` if missing or non-absolute.
|
|
8
8
|
* - Sets `name` to the basename without extension, preserving case (e.g. `"OptionalSchema"` from `"OptionalSchema.ts"`); URL paths use `name`.
|
|
9
9
|
* - Sets `key` to the slugified `name` (e.g. `"optionalschema"`) — only used by React for reconciliation and by `DirectoryExtractor` to merge same-key siblings (e.g. `TEMPLATE.md` + `template.ts`).
|
|
10
|
-
* - Does NOT set `title` — `title` is only set by subclasses that have a confident source for one (e.g. `
|
|
11
|
-
* - Subclasses (e.g. `
|
|
10
|
+
* - Does NOT set `title` — `title` is only set by subclasses that have a confident source for one (e.g. `MarkupExtractor` uses the first `<h1>`). Renderers fall back to `name` when missing.
|
|
11
|
+
* - Subclasses (e.g. `MarkupExtractor`, `TypescriptExtractor`) override `extractProps()` to parse the content into richer elements.
|
|
12
12
|
*/
|
|
13
13
|
export declare class FileExtractor extends Extractor<BunFile, FileElement> {
|
|
14
14
|
extract(file: BunFile): Promise<FileElement>;
|
package/extract/FileExtractor.js
CHANGED
|
@@ -9,8 +9,8 @@ import { Extractor } from "./Extractor.js";
|
|
|
9
9
|
* - Sets `source` to the file's absolute path (`BunFile.name`); throws `RequiredError` if missing or non-absolute.
|
|
10
10
|
* - Sets `name` to the basename without extension, preserving case (e.g. `"OptionalSchema"` from `"OptionalSchema.ts"`); URL paths use `name`.
|
|
11
11
|
* - Sets `key` to the slugified `name` (e.g. `"optionalschema"`) — only used by React for reconciliation and by `DirectoryExtractor` to merge same-key siblings (e.g. `TEMPLATE.md` + `template.ts`).
|
|
12
|
-
* - Does NOT set `title` — `title` is only set by subclasses that have a confident source for one (e.g. `
|
|
13
|
-
* - Subclasses (e.g. `
|
|
12
|
+
* - Does NOT set `title` — `title` is only set by subclasses that have a confident source for one (e.g. `MarkupExtractor` uses the first `<h1>`). Renderers fall back to `name` when missing.
|
|
13
|
+
* - Subclasses (e.g. `MarkupExtractor`, `TypescriptExtractor`) override `extractProps()` to parse the content into richer elements.
|
|
14
14
|
*/
|
|
15
15
|
export class FileExtractor extends Extractor {
|
|
16
16
|
async extract(file) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type FileElementProps } from "../util/element.js";
|
|
2
|
+
import { FileExtractor } from "./FileExtractor.js";
|
|
3
|
+
/**
|
|
4
|
+
* File extractor for Markdown files.
|
|
5
|
+
* - Stores the markdown text as `content`; rendering happens at output time via `<Markup>`.
|
|
6
|
+
* - Sets `title` from the first `# h1` heading if one is present — otherwise leaves it undefined
|
|
7
|
+
* (a confident title only).
|
|
8
|
+
* - When a `title` is found, strips the leading `# h1` from `content` so renderers (which show
|
|
9
|
+
* `title` separately) don't display the heading twice.
|
|
10
|
+
* - Sets `description` to the first prose paragraph as a plain-text summary (used for card listings and `<meta>`).
|
|
11
|
+
*/
|
|
12
|
+
export declare class MarkupExtractor extends FileExtractor {
|
|
13
|
+
/** Markdown contributes the canonical title/path when merging same-key elements. */
|
|
14
|
+
readonly priority = 10;
|
|
15
|
+
extractProps(name: string, text: string): Partial<FileElementProps> & {
|
|
16
|
+
name: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse a markdown source string once and derive its `title` and `description` in a single pass.
|
|
21
|
+
* - `title` — plain text of the first `# h1` heading, or `undefined` if there is none.
|
|
22
|
+
* - `description` — plain-text summary of the first prose paragraph, or `undefined` if there is none.
|
|
23
|
+
* - Both query the parsed markup tree, so inline syntax (`` `code` ``, `*emphasis*`, links) resolves to clean plain text.
|
|
24
|
+
*
|
|
25
|
+
* @param text The markdown source string (a markdown file's text, or a JSDoc description).
|
|
26
|
+
* @returns An object whose `title` and `description` are each a plain string, or `undefined` when absent.
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractMarkdownProps(text: string): {
|
|
29
|
+
title: string | undefined;
|
|
30
|
+
description: string | undefined;
|
|
31
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { MARKUP_PARSER } from "../markup/MarkupParser.js";
|
|
2
|
+
import { getElementText, walkElements } from "../util/element.js";
|
|
3
|
+
import { FileExtractor } from "./FileExtractor.js";
|
|
4
|
+
/**
|
|
5
|
+
* File extractor for Markdown files.
|
|
6
|
+
* - Stores the markdown text as `content`; rendering happens at output time via `<Markup>`.
|
|
7
|
+
* - Sets `title` from the first `# h1` heading if one is present — otherwise leaves it undefined
|
|
8
|
+
* (a confident title only).
|
|
9
|
+
* - When a `title` is found, strips the leading `# h1` from `content` so renderers (which show
|
|
10
|
+
* `title` separately) don't display the heading twice.
|
|
11
|
+
* - Sets `description` to the first prose paragraph as a plain-text summary (used for card listings and `<meta>`).
|
|
12
|
+
*/
|
|
13
|
+
export class MarkupExtractor extends FileExtractor {
|
|
14
|
+
/** Markdown contributes the canonical title/path when merging same-key elements. */
|
|
15
|
+
priority = 10;
|
|
16
|
+
extractProps(name, text) {
|
|
17
|
+
const { title, description } = extractMarkdownProps(text);
|
|
18
|
+
// The title `# h1` is surfaced separately as `title`, so strip it from the body to avoid rendering it twice.
|
|
19
|
+
return { name, title, description, content: title ? _stripTitle(text) : text };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parse a markdown source string once and derive its `title` and `description` in a single pass.
|
|
24
|
+
* - `title` — plain text of the first `# h1` heading, or `undefined` if there is none.
|
|
25
|
+
* - `description` — plain-text summary of the first prose paragraph, or `undefined` if there is none.
|
|
26
|
+
* - Both query the parsed markup tree, so inline syntax (`` `code` ``, `*emphasis*`, links) resolves to clean plain text.
|
|
27
|
+
*
|
|
28
|
+
* @param text The markdown source string (a markdown file's text, or a JSDoc description).
|
|
29
|
+
* @returns An object whose `title` and `description` are each a plain string, or `undefined` when absent.
|
|
30
|
+
*/
|
|
31
|
+
export function extractMarkdownProps(text) {
|
|
32
|
+
let title;
|
|
33
|
+
let description;
|
|
34
|
+
// Walk the top-level block elements once, claiming the first `h1` as the title and the first `p` as the description.
|
|
35
|
+
for (const element of walkElements(MARKUP_PARSER.parse(text))) {
|
|
36
|
+
if (!title && element.type === "h1")
|
|
37
|
+
title = _plain(element);
|
|
38
|
+
else if (!description && element.type === "p")
|
|
39
|
+
description = _plain(element);
|
|
40
|
+
if (title && description)
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
return { title, description };
|
|
44
|
+
}
|
|
45
|
+
/** Flatten an element to a single-line plain-text summary, or `undefined` if it has no text. */
|
|
46
|
+
function _plain(element) {
|
|
47
|
+
return getElementText(element).replace(/\s+/g, " ").trim() || undefined;
|
|
48
|
+
}
|
|
49
|
+
/** Strip a leading `# h1` heading (and any blank lines after it) from markdown text. */
|
|
50
|
+
function _stripTitle(text) {
|
|
51
|
+
return text.replace(/^\s*#[^\n\S]+\S[^\n]*(?:\n+|$)/, "");
|
|
52
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { requireSlug } from "../util/string.js";
|
|
3
3
|
import { FileExtractor } from "./FileExtractor.js";
|
|
4
|
-
import {
|
|
4
|
+
import { extractMarkdownProps } from "./MarkupExtractor.js";
|
|
5
5
|
/**
|
|
6
6
|
* File extractor that parses a TypeScript source file into a tree element.
|
|
7
7
|
* - Uses the TypeScript compiler API to parse the AST.
|
|
@@ -27,7 +27,7 @@ export class TypescriptExtractor extends FileExtractor {
|
|
|
27
27
|
}
|
|
28
28
|
// The file element itself gets no `title` — a TS source file has no confident title source (the filename isn't one),
|
|
29
29
|
// so renderers fall back to `name`. The `tree-documentation` children each carry their own `title`.
|
|
30
|
-
return { name, description:
|
|
30
|
+
return { name, description: extractMarkdownProps(content ?? "").description, content, children: Array.from(byKey.values()) };
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
/** Merge a newly-extracted overload into the existing documentation element with the same key. */
|
|
@@ -102,7 +102,7 @@ function _extractStatement(statement, source) {
|
|
|
102
102
|
// Functions read as callable with `()`; other kinds use the bare name.
|
|
103
103
|
title: kind === "function" ? `${name}()` : name,
|
|
104
104
|
kind,
|
|
105
|
-
description:
|
|
105
|
+
description: extractMarkdownProps(jsDoc?.description ?? "").description,
|
|
106
106
|
content: _buildJSDocContent(jsDoc?.description, jsDoc?.unhandled),
|
|
107
107
|
signatures: signature ? [signature] : undefined,
|
|
108
108
|
params,
|
|
@@ -239,7 +239,7 @@ function _getClassMembers(statement, source) {
|
|
|
239
239
|
props: {
|
|
240
240
|
name,
|
|
241
241
|
title: `${name}()`,
|
|
242
|
-
description:
|
|
242
|
+
description: extractMarkdownProps(memberJSDoc?.description ?? "").description,
|
|
243
243
|
content,
|
|
244
244
|
kind: "method",
|
|
245
245
|
signatures: [signature],
|
|
@@ -255,7 +255,7 @@ function _getClassMembers(statement, source) {
|
|
|
255
255
|
props: {
|
|
256
256
|
name,
|
|
257
257
|
title: name,
|
|
258
|
-
description:
|
|
258
|
+
description: extractMarkdownProps(memberJSDoc?.description ?? "").description,
|
|
259
259
|
content,
|
|
260
260
|
kind: "property",
|
|
261
261
|
signatures: type ? [type] : undefined,
|
package/extract/index.d.ts
CHANGED
package/extract/index.js
CHANGED
package/markup/MarkupParser.js
CHANGED
|
@@ -238,11 +238,16 @@ function _findFrom(rule, masked, input, from, higher) {
|
|
|
238
238
|
lo = wallEnd;
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
|
-
|
|
241
|
+
// Placeholder a claimed region is blanked to: non-whitespace and non-word, so lower tiers see a
|
|
242
|
+
// masked region as opaque *content* rather than whitespace. Blanking to a space made a leading or
|
|
243
|
+
// trailing code span look like whitespace to inline emphasis (which rejects whitespace at its
|
|
244
|
+
// start/end), so `**`code`**` failed to form. Newlines are kept so block structure survives.
|
|
245
|
+
const _MASK_CHAR = "\u0000";
|
|
246
|
+
/** Blank the `[start, end)` region of `text` — every character becomes `_MASK_CHAR`, except newlines. */
|
|
242
247
|
function _mask(text, start, end) {
|
|
243
248
|
let blanked = "";
|
|
244
249
|
for (let i = start; i < end; i++)
|
|
245
|
-
blanked += text[i] === "\n" ? "\n" :
|
|
250
|
+
blanked += text[i] === "\n" ? "\n" : _MASK_CHAR;
|
|
246
251
|
return `${text.slice(0, start)}${blanked}${text.slice(end)}`;
|
|
247
252
|
}
|
|
248
253
|
/** MarkupParser sentinel with the default markup rules */
|
package/package.json
CHANGED
package/ui/docs/DirectoryPage.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Prose } from "../block/Prose.js";
|
|
3
|
+
import { Title } from "../block/Title.js";
|
|
3
4
|
import { Markup } from "../misc/Markup.js";
|
|
4
5
|
import { requireMeta } from "../misc/MetaContext.js";
|
|
5
6
|
import { Page } from "../page/Page.js";
|
|
@@ -8,5 +9,5 @@ import { TreeCards } from "../tree/TreeCards.js";
|
|
|
8
9
|
export function DirectoryPage({ title, name, description, content, children }) {
|
|
9
10
|
const { url } = requireMeta();
|
|
10
11
|
const path = url?.pathname ?? "/";
|
|
11
|
-
return (_jsxs(Page, { title: title ?? name, description: description, children: [content && (_jsx(Prose, { children: _jsx(Markup, { children: content }) })), _jsx(TreeCards, { path: path, children: children })] }));
|
|
12
|
+
return (_jsxs(Page, { title: title ?? name, description: description, children: [_jsx(Title, { children: title ?? name }), content && (_jsx(Prose, { children: _jsx(Markup, { children: content }) })), _jsx(TreeCards, { path: path, children: children })] }));
|
|
12
13
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { DirectoryElementProps } from "../../util/element.js";
|
|
3
3
|
import { Prose } from "../block/Prose.js";
|
|
4
|
+
import { Title } from "../block/Title.js";
|
|
4
5
|
import { Markup } from "../misc/Markup.js";
|
|
5
6
|
import { requireMeta } from "../misc/MetaContext.js";
|
|
6
7
|
import { Page } from "../page/Page.js";
|
|
@@ -12,6 +13,7 @@ export function DirectoryPage({ title, name, description, content, children }: D
|
|
|
12
13
|
const path = url?.pathname ?? "/";
|
|
13
14
|
return (
|
|
14
15
|
<Page title={title ?? name} description={description}>
|
|
16
|
+
<Title>{title ?? name}</Title>
|
|
15
17
|
{content && (
|
|
16
18
|
<Prose>
|
|
17
19
|
<Markup>{content}</Markup>
|
package/ui/docs/FilePage.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Prose } from "../block/Prose.js";
|
|
3
|
+
import { Title } from "../block/Title.js";
|
|
3
4
|
import { Markup } from "../misc/Markup.js";
|
|
4
5
|
import { requireMeta } from "../misc/MetaContext.js";
|
|
5
6
|
import { Page } from "../page/Page.js";
|
|
@@ -8,5 +9,5 @@ import { TreeCards } from "../tree/TreeCards.js";
|
|
|
8
9
|
export function FilePage({ title, name, description, content, children }) {
|
|
9
10
|
const { url } = requireMeta();
|
|
10
11
|
const path = url?.pathname ?? "/";
|
|
11
|
-
return (_jsxs(Page, { title: title ?? name, description: description, children: [content && (_jsx(Prose, { children: _jsx(Markup, { children: content }) })), _jsx(TreeCards, { path: path, children: children })] }));
|
|
12
|
+
return (_jsxs(Page, { title: title ?? name, description: description, children: [_jsx(Title, { children: title ?? name }), content && (_jsx(Prose, { children: _jsx(Markup, { children: content }) })), _jsx(TreeCards, { path: path, children: children })] }));
|
|
12
13
|
}
|
package/ui/docs/FilePage.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { FileElementProps } from "../../util/element.js";
|
|
3
3
|
import { Prose } from "../block/Prose.js";
|
|
4
|
+
import { Title } from "../block/Title.js";
|
|
4
5
|
import { Markup } from "../misc/Markup.js";
|
|
5
6
|
import { requireMeta } from "../misc/MetaContext.js";
|
|
6
7
|
import { Page } from "../page/Page.js";
|
|
@@ -12,6 +13,7 @@ export function FilePage({ title, name, description, content, children }: FileEl
|
|
|
12
13
|
const path = url?.pathname ?? "/";
|
|
13
14
|
return (
|
|
14
15
|
<Page title={title ?? name} description={description}>
|
|
16
|
+
<Title>{title ?? name}</Title>
|
|
15
17
|
{content && (
|
|
16
18
|
<Prose>
|
|
17
19
|
<Markup>{content}</Markup>
|
package/util/element.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export interface TreeElementProps extends ElementProps {
|
|
|
41
41
|
/**
|
|
42
42
|
* Markup source string that should be rendered as the element's visible body content.
|
|
43
43
|
* - Rendered via the `<Markup>` component (typically inside a `<Prose>` wrapper).
|
|
44
|
-
* - For Markdown files this is the file's
|
|
44
|
+
* - For Markdown files this is the file's body (the title `# h1` is lifted into `title`); for TypeScript symbols this is the JSDoc description.
|
|
45
45
|
*/
|
|
46
46
|
readonly content?: string | undefined;
|
|
47
47
|
/** Children of a tree element must be other tree elements. */
|
|
@@ -153,6 +153,7 @@ export declare function isElement(value: unknown): value is Element;
|
|
|
153
153
|
export declare function isElements(value: unknown): value is Elements;
|
|
154
154
|
/**
|
|
155
155
|
* Strip all tags from elements to produce a plain text string.
|
|
156
|
+
* - A `<br>` element becomes a newline (`\n`) — matching DOM `innerText`, so words either side of a line break don't fuse together.
|
|
156
157
|
*
|
|
157
158
|
* @param elements An element, a plain string, or null/undefined (or an array of those things).
|
|
158
159
|
* @returns The combined string made from the elements.
|
package/util/element.js
CHANGED
|
@@ -11,6 +11,7 @@ export function isElements(value) {
|
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Strip all tags from elements to produce a plain text string.
|
|
14
|
+
* - A `<br>` element becomes a newline (`\n`) — matching DOM `innerText`, so words either side of a line break don't fuse together.
|
|
14
15
|
*
|
|
15
16
|
* @param elements An element, a plain string, or null/undefined (or an array of those things).
|
|
16
17
|
* @returns The combined string made from the elements.
|
|
@@ -20,8 +21,12 @@ export function isElements(value) {
|
|
|
20
21
|
export function getElementText(elements) {
|
|
21
22
|
if (typeof elements === "string")
|
|
22
23
|
return elements;
|
|
23
|
-
if (isElement(elements))
|
|
24
|
+
if (isElement(elements)) {
|
|
25
|
+
// A `<br>` carries no children but renders as a line break — emit `\n` so adjacent words stay separated.
|
|
26
|
+
if (elements.type === "br")
|
|
27
|
+
return "\n";
|
|
24
28
|
return getElementText(elements.props.children);
|
|
29
|
+
}
|
|
25
30
|
// Iterate the collection directly — `walkElements()` skips loose strings, so it would drop text that sits alongside elements.
|
|
26
31
|
if (isIterable(elements)) {
|
|
27
32
|
let text = "";
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { type FileElementProps } from "../util/element.js";
|
|
2
|
-
import { FileExtractor } from "./FileExtractor.js";
|
|
3
|
-
/**
|
|
4
|
-
* File extractor for Markdown files.
|
|
5
|
-
* - Stores the raw markdown text as `content`; rendering happens at output time via `<Markup>`.
|
|
6
|
-
* - Sets `title` from the first `# h1` heading if one is present — otherwise leaves it undefined
|
|
7
|
-
* (a confident title only).
|
|
8
|
-
* - Sets `description` to the first prose paragraph as a plain-text summary (used for card listings and `<meta>`).
|
|
9
|
-
*/
|
|
10
|
-
export declare class MarkdownExtractor extends FileExtractor {
|
|
11
|
-
/** Markdown contributes the canonical title/path when merging same-key elements. */
|
|
12
|
-
readonly priority = 10;
|
|
13
|
-
extractProps(name: string, text: string): Partial<FileElementProps> & {
|
|
14
|
-
name: string;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Find the first `# h1` heading in a markdown source string and return its text, or `undefined` if none.
|
|
19
|
-
* - Looks for a line starting with a single `#` followed by whitespace; doesn't match `##`+.
|
|
20
|
-
*/
|
|
21
|
-
export declare function extractMarkdownTitle(text: string): string | undefined;
|
|
22
|
-
/**
|
|
23
|
-
* Find the first prose paragraph in a markdown source string and return it as a plain-text summary, or `undefined` if none.
|
|
24
|
-
* - Skips headings, fenced code blocks, and blank lines, then collects the first ordinary paragraph.
|
|
25
|
-
* - Renders that paragraph as markup and strips every tag, so inline syntax (`` `code` ``, `*emphasis*`, links) becomes plain text.
|
|
26
|
-
* - Collapses internal whitespace so the result is a single line suitable for a `description` / `<meta>` summary.
|
|
27
|
-
*/
|
|
28
|
-
export declare function extractMarkdownDescription(text: string): string | undefined;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { MARKUP_PARSER } from "../markup/MarkupParser.js";
|
|
2
|
-
import { getElementText } from "../util/element.js";
|
|
3
|
-
import { FileExtractor } from "./FileExtractor.js";
|
|
4
|
-
/**
|
|
5
|
-
* File extractor for Markdown files.
|
|
6
|
-
* - Stores the raw markdown text as `content`; rendering happens at output time via `<Markup>`.
|
|
7
|
-
* - Sets `title` from the first `# h1` heading if one is present — otherwise leaves it undefined
|
|
8
|
-
* (a confident title only).
|
|
9
|
-
* - Sets `description` to the first prose paragraph as a plain-text summary (used for card listings and `<meta>`).
|
|
10
|
-
*/
|
|
11
|
-
export class MarkdownExtractor extends FileExtractor {
|
|
12
|
-
/** Markdown contributes the canonical title/path when merging same-key elements. */
|
|
13
|
-
priority = 10;
|
|
14
|
-
extractProps(name, text) {
|
|
15
|
-
return { name, title: extractMarkdownTitle(text), description: extractMarkdownDescription(text), content: text };
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Find the first `# h1` heading in a markdown source string and return its text, or `undefined` if none.
|
|
20
|
-
* - Looks for a line starting with a single `#` followed by whitespace; doesn't match `##`+.
|
|
21
|
-
*/
|
|
22
|
-
export function extractMarkdownTitle(text) {
|
|
23
|
-
const match = text.match(/^#\s+(.+?)\s*$/m);
|
|
24
|
-
return match?.[1];
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Find the first prose paragraph in a markdown source string and return it as a plain-text summary, or `undefined` if none.
|
|
28
|
-
* - Skips headings, fenced code blocks, and blank lines, then collects the first ordinary paragraph.
|
|
29
|
-
* - Renders that paragraph as markup and strips every tag, so inline syntax (`` `code` ``, `*emphasis*`, links) becomes plain text.
|
|
30
|
-
* - Collapses internal whitespace so the result is a single line suitable for a `description` / `<meta>` summary.
|
|
31
|
-
*/
|
|
32
|
-
export function extractMarkdownDescription(text) {
|
|
33
|
-
const paragraph = [];
|
|
34
|
-
let fenced = false;
|
|
35
|
-
for (const line of text.split("\n")) {
|
|
36
|
-
const trimmed = line.trim();
|
|
37
|
-
// Toggle in/out of fenced code blocks — never treat their contents as the summary.
|
|
38
|
-
if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
|
|
39
|
-
fenced = !fenced;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (fenced)
|
|
43
|
-
continue;
|
|
44
|
-
// A blank line or heading ends the first paragraph (or is skipped while still searching for it).
|
|
45
|
-
if (!trimmed || trimmed.startsWith("#")) {
|
|
46
|
-
if (paragraph.length)
|
|
47
|
-
break;
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
paragraph.push(trimmed);
|
|
51
|
-
}
|
|
52
|
-
if (!paragraph.length)
|
|
53
|
-
return;
|
|
54
|
-
// Render the paragraph as markup then strip every tag, so inline syntax resolves to clean plain text.
|
|
55
|
-
const rendered = MARKUP_PARSER.parse(paragraph.join(" "));
|
|
56
|
-
const summary = getElementText(rendered).replace(/\s+/g, " ").trim();
|
|
57
|
-
return summary || undefined;
|
|
58
|
-
}
|