@vx-oss/docs-satteri 1.0.3
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/LICENSE +21 -0
- package/dist/compile-SBpZsgmI.d.ts +42 -0
- package/dist/compile.d.ts +2 -0
- package/dist/compile.js +63 -0
- package/dist/index-AvS9vC2S.d.ts +29 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/local-md/client.d.ts +2 -0
- package/dist/local-md/client.js +2 -0
- package/dist/local-md/dev/react-client.d.ts +2 -0
- package/dist/local-md/dev/react-client.js +2 -0
- package/dist/local-md/dev/vite.d.ts +2 -0
- package/dist/local-md/dev/vite.js +2 -0
- package/dist/local-md/dev/ws.d.ts +2 -0
- package/dist/local-md/dev/ws.js +2 -0
- package/dist/local-md/index.d.ts +70 -0
- package/dist/local-md/index.js +104 -0
- package/dist/preset-DdTDKnqd.js +104 -0
- package/dist/preset-DzMkd76i.d.ts +35 -0
- package/dist/preset.d.ts +2 -0
- package/dist/preset.js +2 -0
- package/dist/rehype-code-CLGqDUSe.d.ts +13 -0
- package/dist/rehype-code.d.ts +2 -0
- package/dist/rehype-code.js +79 -0
- package/dist/rehype-katex-h_E-WUwT.d.ts +15 -0
- package/dist/rehype-katex.d.ts +2 -0
- package/dist/rehype-katex.js +52 -0
- package/dist/rehype-toc-wT8IcPFi.d.ts +33 -0
- package/dist/rehype-toc.d.ts +2 -0
- package/dist/rehype-toc.js +84 -0
- package/dist/remark-admonition.d.ts +13 -0
- package/dist/remark-admonition.js +79 -0
- package/dist/remark-auto-type-table.d.ts +9 -0
- package/dist/remark-auto-type-table.js +189 -0
- package/dist/remark-block-id.d.ts +18 -0
- package/dist/remark-block-id.js +46 -0
- package/dist/remark-code-tab-BYruQJUs.d.ts +15 -0
- package/dist/remark-code-tab.d.ts +2 -0
- package/dist/remark-code-tab.js +170 -0
- package/dist/remark-directive-admonition.d.ts +13 -0
- package/dist/remark-directive-admonition.js +54 -0
- package/dist/remark-feedback-block.d.ts +18 -0
- package/dist/remark-feedback-block.js +51 -0
- package/dist/remark-heading-CYhI2Dkg.d.ts +15 -0
- package/dist/remark-heading.d.ts +2 -0
- package/dist/remark-heading.js +43 -0
- package/dist/remark-image-C28nbosW.d.ts +29 -0
- package/dist/remark-image.d.ts +2 -0
- package/dist/remark-image.js +141 -0
- package/dist/remark-include.d.ts +22 -0
- package/dist/remark-include.js +336 -0
- package/dist/remark-llms.d.ts +36 -0
- package/dist/remark-llms.js +191 -0
- package/dist/remark-mdx-mermaid.d.ts +10 -0
- package/dist/remark-mdx-mermaid.js +22 -0
- package/dist/remark-npm-DdmTKhyi.d.ts +18 -0
- package/dist/remark-npm.d.ts +2 -0
- package/dist/remark-npm.js +72 -0
- package/dist/remark-steps.d.ts +14 -0
- package/dist/remark-steps.js +127 -0
- package/dist/remark-structure.d.ts +29 -0
- package/dist/remark-structure.js +94 -0
- package/dist/remark-ts2js.d.ts +25 -0
- package/dist/remark-ts2js.js +56 -0
- package/dist/renderer-BtFWPZB8.js +54 -0
- package/dist/renderer-E5KzV19n.d.ts +30 -0
- package/dist/stringifier-COIWcXys.js +130 -0
- package/dist/utils-I3DkmAkd.js +27 -0
- package/package.json +124 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { defineMdastPlugin, mdxToMdast } from "satteri";
|
|
2
|
+
import { generateCodeBlockTabs, parseCodeBlockAttributes } from "@vx-oss/docs-core/mdx-plugins/codeblock-utils";
|
|
3
|
+
//#region src/remark-code-tab.ts
|
|
4
|
+
function parseTabAttributes(node) {
|
|
5
|
+
if (!node || node.type !== "code" || !node.meta) return;
|
|
6
|
+
const parsed = parseCodeBlockAttributes(node.meta, ["tab", "tab-group"]);
|
|
7
|
+
if (!parsed.attributes.tab) return;
|
|
8
|
+
return parsed;
|
|
9
|
+
}
|
|
10
|
+
function parseTabName(name) {
|
|
11
|
+
try {
|
|
12
|
+
const children = (mdxToMdast(name, { position: false }).children ?? []).flatMap((child) => child.type === "paragraph" ? child.children : child);
|
|
13
|
+
if (children.length > 0) return children;
|
|
14
|
+
} catch {}
|
|
15
|
+
return [{
|
|
16
|
+
type: "text",
|
|
17
|
+
value: name
|
|
18
|
+
}];
|
|
19
|
+
}
|
|
20
|
+
function buildTabs(entries, mode, withMdx) {
|
|
21
|
+
if (mode === "CodeBlockTabs") {
|
|
22
|
+
const options = {
|
|
23
|
+
triggers: [],
|
|
24
|
+
tabs: [],
|
|
25
|
+
defaultValue: entries[0].name
|
|
26
|
+
};
|
|
27
|
+
if (entries[0].tabGroup) options.persist = { id: entries[0].tabGroup };
|
|
28
|
+
for (const { name, codes } of entries) {
|
|
29
|
+
options.triggers.push({
|
|
30
|
+
value: name,
|
|
31
|
+
children: withMdx ? parseTabName(name) : [{
|
|
32
|
+
type: "text",
|
|
33
|
+
value: name
|
|
34
|
+
}]
|
|
35
|
+
});
|
|
36
|
+
options.tabs.push({
|
|
37
|
+
value: name,
|
|
38
|
+
children: codes
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return generateCodeBlockTabs(options);
|
|
42
|
+
}
|
|
43
|
+
if (!withMdx) return {
|
|
44
|
+
type: "mdxJsxFlowElement",
|
|
45
|
+
name: "Tabs",
|
|
46
|
+
attributes: [{
|
|
47
|
+
type: "mdxJsxAttribute",
|
|
48
|
+
name: "items",
|
|
49
|
+
value: {
|
|
50
|
+
type: "mdxJsxAttributeValueExpression",
|
|
51
|
+
value: JSON.stringify(entries.map(({ name }) => name))
|
|
52
|
+
}
|
|
53
|
+
}],
|
|
54
|
+
children: entries.map(({ name, codes }) => ({
|
|
55
|
+
type: "mdxJsxFlowElement",
|
|
56
|
+
name: "Tab",
|
|
57
|
+
attributes: [{
|
|
58
|
+
type: "mdxJsxAttribute",
|
|
59
|
+
name: "value",
|
|
60
|
+
value: name
|
|
61
|
+
}],
|
|
62
|
+
children: codes
|
|
63
|
+
}))
|
|
64
|
+
};
|
|
65
|
+
const children = [{
|
|
66
|
+
type: "mdxJsxFlowElement",
|
|
67
|
+
name: "TabsList",
|
|
68
|
+
attributes: [],
|
|
69
|
+
children: entries.map(({ name }) => ({
|
|
70
|
+
type: "mdxJsxFlowElement",
|
|
71
|
+
name: "TabsTrigger",
|
|
72
|
+
attributes: [{
|
|
73
|
+
type: "mdxJsxAttribute",
|
|
74
|
+
name: "value",
|
|
75
|
+
value: name
|
|
76
|
+
}],
|
|
77
|
+
children: parseTabName(name)
|
|
78
|
+
}))
|
|
79
|
+
}];
|
|
80
|
+
for (const { name, codes } of entries) children.push({
|
|
81
|
+
type: "mdxJsxFlowElement",
|
|
82
|
+
name: "TabsContent",
|
|
83
|
+
attributes: [{
|
|
84
|
+
type: "mdxJsxAttribute",
|
|
85
|
+
name: "value",
|
|
86
|
+
value: name
|
|
87
|
+
}],
|
|
88
|
+
children: codes
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
type: "mdxJsxFlowElement",
|
|
92
|
+
name: "Tabs",
|
|
93
|
+
attributes: [{
|
|
94
|
+
type: "mdxJsxAttribute",
|
|
95
|
+
name: "defaultValue",
|
|
96
|
+
value: entries[0].name
|
|
97
|
+
}],
|
|
98
|
+
children
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** author-written tab containers whose code blocks are already grouped */
|
|
102
|
+
const TAB_CONTAINERS = /* @__PURE__ */ new Set([
|
|
103
|
+
"CodeBlockTabs",
|
|
104
|
+
"CodeBlockTab",
|
|
105
|
+
"CodeBlockTabsList",
|
|
106
|
+
"CodeBlockTabsTrigger"
|
|
107
|
+
]);
|
|
108
|
+
function collectEntries(children, start, end) {
|
|
109
|
+
const entries = [];
|
|
110
|
+
for (let i = start; i < end; i++) {
|
|
111
|
+
const item = children[i];
|
|
112
|
+
const parsed = parseTabAttributes(item);
|
|
113
|
+
const name = typeof parsed.attributes.tab === "string" ? parsed.attributes.tab : `Tab ${i - start + 1}`;
|
|
114
|
+
const copy = {
|
|
115
|
+
...item,
|
|
116
|
+
meta: parsed.rest || void 0
|
|
117
|
+
};
|
|
118
|
+
const existing = entries.find((entry) => entry.name === name);
|
|
119
|
+
if (existing) existing.codes.push(copy);
|
|
120
|
+
else entries.push({
|
|
121
|
+
name,
|
|
122
|
+
tabGroup: typeof parsed.attributes["tab-group"] === "string" ? parsed.attributes["tab-group"] : void 0,
|
|
123
|
+
codes: [copy]
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return entries;
|
|
127
|
+
}
|
|
128
|
+
function isInsideTabContainer(node, ctx) {
|
|
129
|
+
let parent = ctx.parent(node);
|
|
130
|
+
while (parent) {
|
|
131
|
+
if (parent.type === "mdxJsxFlowElement" && TAB_CONTAINERS.has(parent.name ?? "")) return true;
|
|
132
|
+
parent = ctx.parent(parent);
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
function remarkCodeTab({ parseMdx = false, Tabs = "CodeBlockTabs" } = {}) {
|
|
137
|
+
return () => {
|
|
138
|
+
const parents = /* @__PURE__ */ new Set();
|
|
139
|
+
return defineMdastPlugin({
|
|
140
|
+
name: "remark-code-tab",
|
|
141
|
+
code(node, ctx) {
|
|
142
|
+
if (!parseTabAttributes(node) || isInsideTabContainer(node, ctx)) return;
|
|
143
|
+
const parent = ctx.parent(node);
|
|
144
|
+
if (parent && "children" in parent) parents.add(parent);
|
|
145
|
+
},
|
|
146
|
+
after(_root, ctx) {
|
|
147
|
+
for (const parent of parents) {
|
|
148
|
+
const children = parent.children;
|
|
149
|
+
let rebuilt;
|
|
150
|
+
for (let i = 0; i < children.length;) {
|
|
151
|
+
const node = children[i];
|
|
152
|
+
if (!parseTabAttributes(node)) {
|
|
153
|
+
rebuilt?.push(node);
|
|
154
|
+
i++;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
let end = i + 1;
|
|
158
|
+
while (end < children.length && parseTabAttributes(children[end])) end++;
|
|
159
|
+
rebuilt ??= children.slice(0, i);
|
|
160
|
+
rebuilt.push(buildTabs(collectEntries(children, i, end), Tabs, parseMdx));
|
|
161
|
+
i = end;
|
|
162
|
+
}
|
|
163
|
+
if (rebuilt) ctx.setProperty(parent, "children", rebuilt);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
export { remarkCodeTab };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MdastPluginDefinition } from "satteri";
|
|
2
|
+
//#region src/remark-directive-admonition.d.ts
|
|
3
|
+
interface RemarkDirectiveAdmonitionOptions {
|
|
4
|
+
tags?: {
|
|
5
|
+
CalloutContainer?: string;
|
|
6
|
+
CalloutTitle?: string;
|
|
7
|
+
CalloutDescription?: string;
|
|
8
|
+
};
|
|
9
|
+
types?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
declare function remarkDirectiveAdmonition(options?: RemarkDirectiveAdmonitionOptions): MdastPluginDefinition;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { RemarkDirectiveAdmonitionOptions, remarkDirectiveAdmonition };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { defineMdastPlugin } from "satteri";
|
|
2
|
+
//#region src/remark-directive-admonition.ts
|
|
3
|
+
function remarkDirectiveAdmonition(options = {}) {
|
|
4
|
+
const { tags: { CalloutContainer = "CalloutContainer", CalloutTitle = "CalloutTitle", CalloutDescription = "CalloutDescription" } = {}, types = {
|
|
5
|
+
note: "info",
|
|
6
|
+
tip: "info",
|
|
7
|
+
info: "info",
|
|
8
|
+
warn: "warning",
|
|
9
|
+
warning: "warning",
|
|
10
|
+
danger: "error",
|
|
11
|
+
success: "success"
|
|
12
|
+
} } = options;
|
|
13
|
+
return defineMdastPlugin({
|
|
14
|
+
name: "remark-directive-admonition",
|
|
15
|
+
containerDirective(node, ctx) {
|
|
16
|
+
if (!(node.name in types)) return;
|
|
17
|
+
const attributes = [{
|
|
18
|
+
type: "mdxJsxAttribute",
|
|
19
|
+
name: "type",
|
|
20
|
+
value: types[node.name]
|
|
21
|
+
}];
|
|
22
|
+
for (const [k, v] of Object.entries(node.attributes ?? {})) attributes.push({
|
|
23
|
+
type: "mdxJsxAttribute",
|
|
24
|
+
name: k,
|
|
25
|
+
value: String(v)
|
|
26
|
+
});
|
|
27
|
+
const titleNodes = [];
|
|
28
|
+
const descriptionNodes = [];
|
|
29
|
+
for (const item of node.children) if (item.type === "paragraph" && item.data?.directiveLabel) titleNodes.push(...item.children);
|
|
30
|
+
else descriptionNodes.push(item);
|
|
31
|
+
const children = [];
|
|
32
|
+
if (titleNodes.length > 0) children.push({
|
|
33
|
+
type: "mdxJsxFlowElement",
|
|
34
|
+
name: CalloutTitle,
|
|
35
|
+
attributes: [],
|
|
36
|
+
children: titleNodes
|
|
37
|
+
});
|
|
38
|
+
if (descriptionNodes.length > 0) children.push({
|
|
39
|
+
type: "mdxJsxFlowElement",
|
|
40
|
+
name: CalloutDescription,
|
|
41
|
+
attributes: [],
|
|
42
|
+
children: descriptionNodes
|
|
43
|
+
});
|
|
44
|
+
ctx.replaceNode(node, {
|
|
45
|
+
type: "mdxJsxFlowElement",
|
|
46
|
+
attributes,
|
|
47
|
+
name: CalloutContainer,
|
|
48
|
+
children
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { remarkDirectiveAdmonition };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { MdastNode, MdastVisitorContext } from "satteri";
|
|
2
|
+
//#region src/remark-feedback-block.d.ts
|
|
3
|
+
interface RemarkFeedbackBlockOptions {
|
|
4
|
+
generateHash?: (ctx: {
|
|
5
|
+
body: string;
|
|
6
|
+
}) => string;
|
|
7
|
+
tagName?: string;
|
|
8
|
+
resolve?: (node: MdastNode) => boolean | 'skip';
|
|
9
|
+
generateBody?: boolean;
|
|
10
|
+
}
|
|
11
|
+
declare function remarkFeedbackBlock({ generateHash, tagName, resolve, generateBody }?: RemarkFeedbackBlockOptions): () => {
|
|
12
|
+
name: string;
|
|
13
|
+
paragraph: (node: MdastNode, ctx: MdastVisitorContext) => void;
|
|
14
|
+
image: (node: MdastNode, ctx: MdastVisitorContext) => void;
|
|
15
|
+
listItem: (node: MdastNode, ctx: MdastVisitorContext) => void;
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { RemarkFeedbackBlockOptions, remarkFeedbackBlock };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { defineMdastPlugin } from "satteri";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
//#region src/remark-feedback-block.ts
|
|
4
|
+
function remarkFeedbackBlock({ generateHash = ({ body }) => createHash("md5").update(body).digest("hex").substring(0, 16), tagName = "FeedbackBlock", resolve = (node) => {
|
|
5
|
+
switch (node.type) {
|
|
6
|
+
case "mdxJsxFlowElement": return "skip";
|
|
7
|
+
case "paragraph":
|
|
8
|
+
case "image":
|
|
9
|
+
case "listItem": return true;
|
|
10
|
+
default: return false;
|
|
11
|
+
}
|
|
12
|
+
}, generateBody = true } = {}) {
|
|
13
|
+
return () => {
|
|
14
|
+
const counts = /* @__PURE__ */ new Map();
|
|
15
|
+
function visit(node, ctx) {
|
|
16
|
+
const resolved = resolve(node);
|
|
17
|
+
if (resolved === false || resolved === "skip") return;
|
|
18
|
+
const text = ctx.textContent(node, { includeImageAlt: false }).trim();
|
|
19
|
+
if (text.length === 0) return;
|
|
20
|
+
let id = generateHash({ body: text });
|
|
21
|
+
const count = counts.get(id) ?? 0;
|
|
22
|
+
if (count > 0) id = `${id}-${count}`;
|
|
23
|
+
counts.set(id, count + 1);
|
|
24
|
+
const attributes = [{
|
|
25
|
+
type: "mdxJsxAttribute",
|
|
26
|
+
name: "id",
|
|
27
|
+
value: id
|
|
28
|
+
}];
|
|
29
|
+
if (generateBody) attributes.push({
|
|
30
|
+
type: "mdxJsxAttribute",
|
|
31
|
+
name: "body",
|
|
32
|
+
value: text
|
|
33
|
+
});
|
|
34
|
+
ctx.wrapNode(node, {
|
|
35
|
+
type: "mdxJsxFlowElement",
|
|
36
|
+
name: tagName,
|
|
37
|
+
attributes,
|
|
38
|
+
data: { _stringify: "children-only" },
|
|
39
|
+
children: []
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return defineMdastPlugin({
|
|
43
|
+
name: "remark-feedback-block",
|
|
44
|
+
paragraph: visit,
|
|
45
|
+
image: visit,
|
|
46
|
+
listItem: visit
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { remarkFeedbackBlock };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { MdastVisitorContext } from "satteri";
|
|
2
|
+
import { Heading } from "mdast";
|
|
3
|
+
//#region src/remark-heading.d.ts
|
|
4
|
+
interface RemarkHeadingOptions {
|
|
5
|
+
slug?: (heading: Heading, text: string) => string;
|
|
6
|
+
customId?: boolean;
|
|
7
|
+
generateToc?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare function remarkHeading({ slug, customId, generateToc }?: RemarkHeadingOptions): () => {
|
|
10
|
+
name: string;
|
|
11
|
+
before(_root: Readonly<import("mdast").Root>, ctx: MdastVisitorContext): void;
|
|
12
|
+
heading(node: Readonly<Heading>, ctx: MdastVisitorContext): undefined;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { remarkHeading as n, RemarkHeadingOptions as t };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Slugger from "github-slugger";
|
|
2
|
+
import { defineMdastPlugin } from "satteri";
|
|
3
|
+
//#region src/remark-heading.ts
|
|
4
|
+
const regex = /\s*\[#(?<slug>[^]+?)]\s*$/;
|
|
5
|
+
function remarkHeading({ slug, customId = true, generateToc = true } = {}) {
|
|
6
|
+
return () => {
|
|
7
|
+
let resolveSlug = slug;
|
|
8
|
+
if (!resolveSlug) {
|
|
9
|
+
const slugger = new Slugger();
|
|
10
|
+
resolveSlug = (_, v) => slugger.slug(v);
|
|
11
|
+
}
|
|
12
|
+
return defineMdastPlugin({
|
|
13
|
+
name: "remark-heading",
|
|
14
|
+
before(_root, ctx) {
|
|
15
|
+
if (generateToc) ctx.data.toc ??= [];
|
|
16
|
+
},
|
|
17
|
+
heading(node, ctx) {
|
|
18
|
+
const data = { ...node.data };
|
|
19
|
+
const hProperties = data.hProperties = { ...data.hProperties };
|
|
20
|
+
let title = ctx.textContent(node);
|
|
21
|
+
const lastNode = node.children.at(-1);
|
|
22
|
+
if (lastNode?.type === "text" && customId) {
|
|
23
|
+
const match = regex.exec(lastNode.value);
|
|
24
|
+
if (match?.[1]) {
|
|
25
|
+
hProperties.id = match[1];
|
|
26
|
+
const stripped = lastNode.value.slice(0, match.index);
|
|
27
|
+
title = title.slice(0, title.length - lastNode.value.length) + stripped;
|
|
28
|
+
ctx.setProperty(lastNode, "value", stripped);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (!hProperties.id) hProperties.id = resolveSlug(node, title);
|
|
32
|
+
ctx.setProperty(node, "data", data);
|
|
33
|
+
ctx.data.toc?.push({
|
|
34
|
+
title,
|
|
35
|
+
url: `#${hProperties.id}`,
|
|
36
|
+
depth: node.depth
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { remarkHeading };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { o as ExtraPluginHooks } from "./compile-SBpZsgmI.js";
|
|
2
|
+
import { MdastPluginDefinition } from "satteri";
|
|
3
|
+
//#region src/remark-image.d.ts
|
|
4
|
+
type ExternalImageOptions = {
|
|
5
|
+
timeout?: number;
|
|
6
|
+
} | boolean;
|
|
7
|
+
type ImageSize = {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
};
|
|
11
|
+
interface RemarkImageOptions {
|
|
12
|
+
publicDir?: string;
|
|
13
|
+
placeholder?: 'blur' | 'none';
|
|
14
|
+
onError?: 'error' | 'hide' | 'ignore' | ((error: Error) => void);
|
|
15
|
+
useImport?: boolean;
|
|
16
|
+
external?: ExternalImageOptions;
|
|
17
|
+
}
|
|
18
|
+
type Source = {
|
|
19
|
+
type: 'url';
|
|
20
|
+
url: URL;
|
|
21
|
+
} | {
|
|
22
|
+
type: 'file';
|
|
23
|
+
file: string;
|
|
24
|
+
};
|
|
25
|
+
declare function remarkImage({ placeholder, external, useImport, onError, publicDir }?: RemarkImageOptions): MdastPluginDefinition & ExtraPluginHooks;
|
|
26
|
+
declare function parseSrc(src: string, publicDir: string, dir?: string): Source | undefined;
|
|
27
|
+
declare function getImageSize(src: Source, onExternal: ExternalImageOptions, cache?: Map<string, Promise<ImageSize | undefined>>): Promise<ImageSize | undefined>;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { remarkImage as i, getImageSize as n, parseSrc as r, RemarkImageOptions as t };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import * as path$1 from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
//#region src/remark-image.ts
|
|
4
|
+
const VALID_BLUR_EXT = [
|
|
5
|
+
".jpeg",
|
|
6
|
+
".png",
|
|
7
|
+
".webp",
|
|
8
|
+
".avif",
|
|
9
|
+
".jpg"
|
|
10
|
+
];
|
|
11
|
+
const EXTERNAL_URL_REGEX = /^https?:\/\//;
|
|
12
|
+
function remarkImage({ placeholder = "none", external = true, useImport = true, onError = "error", publicDir = path$1.join(process.cwd(), "public") } = {}) {
|
|
13
|
+
const sizeCache = /* @__PURE__ */ new Map();
|
|
14
|
+
return {
|
|
15
|
+
name: "remark-image",
|
|
16
|
+
afterToJs({ result, outputFormat }) {
|
|
17
|
+
if (outputFormat === "program" && result.data._imageImports) result.code += `\n${result.data._imageImports.join("\n")}`;
|
|
18
|
+
},
|
|
19
|
+
async image(node, ctx) {
|
|
20
|
+
const dir = ctx.fileURL ? path$1.dirname(fileURLToPath(ctx.fileURL)) : void 0;
|
|
21
|
+
const src = parseSrc(decodeURI(node.url), publicDir, dir);
|
|
22
|
+
if (!src) return;
|
|
23
|
+
try {
|
|
24
|
+
await updateImage(src, node, ctx, {
|
|
25
|
+
placeholder,
|
|
26
|
+
useImport,
|
|
27
|
+
external,
|
|
28
|
+
dir,
|
|
29
|
+
sizeCache
|
|
30
|
+
});
|
|
31
|
+
} catch (error) {
|
|
32
|
+
if (onError === "ignore" || node.url.endsWith(".svg")) return;
|
|
33
|
+
else if (onError === "hide") ctx.removeNode(node);
|
|
34
|
+
else if (typeof onError === "function") onError(error);
|
|
35
|
+
else throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async function updateImage(src, node, ctx, options) {
|
|
41
|
+
if (src.type === "file" && options.useImport) {
|
|
42
|
+
if (!options.dir) throw new Error("When `useImport` is enabled, pass `fileURL` to the compiler so image paths can be resolved.");
|
|
43
|
+
const imports = ctx.data._imageImports ??= [];
|
|
44
|
+
const variableName = `__img${imports.length}`;
|
|
45
|
+
const importPath = getImportPath(src.file, options.dir);
|
|
46
|
+
imports.push(`import ${variableName} from ${JSON.stringify(importPath)};`);
|
|
47
|
+
const out = {
|
|
48
|
+
type: "mdxJsxFlowElement",
|
|
49
|
+
name: "img",
|
|
50
|
+
children: [],
|
|
51
|
+
attributes: [{
|
|
52
|
+
type: "mdxJsxAttribute",
|
|
53
|
+
name: "alt",
|
|
54
|
+
value: node.alt ?? "image"
|
|
55
|
+
}, {
|
|
56
|
+
type: "mdxJsxAttribute",
|
|
57
|
+
name: "src",
|
|
58
|
+
value: {
|
|
59
|
+
type: "mdxJsxAttributeValueExpression",
|
|
60
|
+
value: variableName
|
|
61
|
+
}
|
|
62
|
+
}]
|
|
63
|
+
};
|
|
64
|
+
if (node.title) out.attributes.push({
|
|
65
|
+
type: "mdxJsxAttribute",
|
|
66
|
+
name: "title",
|
|
67
|
+
value: node.title
|
|
68
|
+
});
|
|
69
|
+
if (options.placeholder === "blur" && VALID_BLUR_EXT.some((ext) => src.file.endsWith(ext))) out.attributes.push({
|
|
70
|
+
type: "mdxJsxAttribute",
|
|
71
|
+
name: "placeholder",
|
|
72
|
+
value: "blur"
|
|
73
|
+
});
|
|
74
|
+
ctx.replaceNode(node, out);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const size = await getImageSize(src, options.external, options.sizeCache);
|
|
78
|
+
if (!size) return;
|
|
79
|
+
const data = { ...node.data };
|
|
80
|
+
const props = { ...data.hProperties };
|
|
81
|
+
props.src = src.type === "url" ? src.url.href : node.url;
|
|
82
|
+
props.width = size.width.toString();
|
|
83
|
+
props.height = size.height.toString();
|
|
84
|
+
data.hProperties = props;
|
|
85
|
+
ctx.setProperty(node, "data", data);
|
|
86
|
+
}
|
|
87
|
+
function getImportPath(file, dir) {
|
|
88
|
+
const relative = path$1.relative(dir, file).replaceAll(path$1.sep, "/");
|
|
89
|
+
return relative.startsWith("../") ? relative : `./${relative}`;
|
|
90
|
+
}
|
|
91
|
+
function parseSrc(src, publicDir, dir) {
|
|
92
|
+
if (src.startsWith("file:///")) return {
|
|
93
|
+
type: "file",
|
|
94
|
+
file: fileURLToPath(src)
|
|
95
|
+
};
|
|
96
|
+
if (EXTERNAL_URL_REGEX.test(src)) return {
|
|
97
|
+
type: "url",
|
|
98
|
+
url: new URL(src)
|
|
99
|
+
};
|
|
100
|
+
if (src.startsWith("/")) {
|
|
101
|
+
if (EXTERNAL_URL_REGEX.test(publicDir)) {
|
|
102
|
+
const url = new URL(publicDir);
|
|
103
|
+
url.pathname = `/${[...url.pathname.split("/"), ...src.split("/")].filter((v) => v.length > 0).join("/")}`;
|
|
104
|
+
return {
|
|
105
|
+
type: "url",
|
|
106
|
+
url
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
type: "file",
|
|
111
|
+
file: path$1.join(publicDir, src)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (!dir) return;
|
|
115
|
+
return {
|
|
116
|
+
type: "file",
|
|
117
|
+
file: path$1.join(dir, src)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function getSizeCacheKey(src, onExternal) {
|
|
121
|
+
if (src.type === "file") return `file:${src.file}`;
|
|
122
|
+
return `url:${(typeof onExternal === "object" ? onExternal.timeout : void 0) ?? ""}:${src.url.href}`;
|
|
123
|
+
}
|
|
124
|
+
async function getImageSize(src, onExternal, cache) {
|
|
125
|
+
if (!cache) return loadImageSize(src, onExternal);
|
|
126
|
+
const key = getSizeCacheKey(src, onExternal);
|
|
127
|
+
const cached = cache.get(key);
|
|
128
|
+
if (cached) return cached;
|
|
129
|
+
const result = loadImageSize(src, onExternal);
|
|
130
|
+
cache.set(key, result);
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
async function loadImageSize(src, onExternal) {
|
|
134
|
+
const { probe } = await import("@vx-oss/docs-image-size");
|
|
135
|
+
if (src.type === "file") return probe(src.file);
|
|
136
|
+
if (onExternal === false) return;
|
|
137
|
+
const { timeout } = typeof onExternal === "object" ? onExternal : {};
|
|
138
|
+
return probe(src.url, { timeout });
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
export { getImageSize, parseSrc, remarkImage };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MdastPluginDefinition } from "satteri";
|
|
2
|
+
//#region src/remark-include.d.ts
|
|
3
|
+
interface RemarkIncludeOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Base directory for includes with the `cwd` attribute, and the fallback
|
|
6
|
+
* when the document has no `fileURL`.
|
|
7
|
+
*
|
|
8
|
+
* @defaultValue process.cwd()
|
|
9
|
+
*/
|
|
10
|
+
cwd?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Sätteri port of @vx-oss/docs-mdx's `remark-include`: embed other documents with
|
|
14
|
+
* `<include>./file.mdx</include>` (or the `:::include` / `::include`
|
|
15
|
+
* directives), sections with `<include>./file.mdx#section</include>`, and
|
|
16
|
+
* non-markdown files as code blocks — with VS Code-style `#region` extraction.
|
|
17
|
+
*
|
|
18
|
+
* Nested includes are resolved recursively, relative to the file they appear in.
|
|
19
|
+
*/
|
|
20
|
+
declare function remarkInclude({ cwd }?: RemarkIncludeOptions): MdastPluginDefinition;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { RemarkIncludeOptions, remarkInclude };
|