@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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/dist/compile-SBpZsgmI.d.ts +42 -0
  3. package/dist/compile.d.ts +2 -0
  4. package/dist/compile.js +63 -0
  5. package/dist/index-AvS9vC2S.d.ts +29 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +1 -0
  8. package/dist/local-md/client.d.ts +2 -0
  9. package/dist/local-md/client.js +2 -0
  10. package/dist/local-md/dev/react-client.d.ts +2 -0
  11. package/dist/local-md/dev/react-client.js +2 -0
  12. package/dist/local-md/dev/vite.d.ts +2 -0
  13. package/dist/local-md/dev/vite.js +2 -0
  14. package/dist/local-md/dev/ws.d.ts +2 -0
  15. package/dist/local-md/dev/ws.js +2 -0
  16. package/dist/local-md/index.d.ts +70 -0
  17. package/dist/local-md/index.js +104 -0
  18. package/dist/preset-DdTDKnqd.js +104 -0
  19. package/dist/preset-DzMkd76i.d.ts +35 -0
  20. package/dist/preset.d.ts +2 -0
  21. package/dist/preset.js +2 -0
  22. package/dist/rehype-code-CLGqDUSe.d.ts +13 -0
  23. package/dist/rehype-code.d.ts +2 -0
  24. package/dist/rehype-code.js +79 -0
  25. package/dist/rehype-katex-h_E-WUwT.d.ts +15 -0
  26. package/dist/rehype-katex.d.ts +2 -0
  27. package/dist/rehype-katex.js +52 -0
  28. package/dist/rehype-toc-wT8IcPFi.d.ts +33 -0
  29. package/dist/rehype-toc.d.ts +2 -0
  30. package/dist/rehype-toc.js +84 -0
  31. package/dist/remark-admonition.d.ts +13 -0
  32. package/dist/remark-admonition.js +79 -0
  33. package/dist/remark-auto-type-table.d.ts +9 -0
  34. package/dist/remark-auto-type-table.js +189 -0
  35. package/dist/remark-block-id.d.ts +18 -0
  36. package/dist/remark-block-id.js +46 -0
  37. package/dist/remark-code-tab-BYruQJUs.d.ts +15 -0
  38. package/dist/remark-code-tab.d.ts +2 -0
  39. package/dist/remark-code-tab.js +170 -0
  40. package/dist/remark-directive-admonition.d.ts +13 -0
  41. package/dist/remark-directive-admonition.js +54 -0
  42. package/dist/remark-feedback-block.d.ts +18 -0
  43. package/dist/remark-feedback-block.js +51 -0
  44. package/dist/remark-heading-CYhI2Dkg.d.ts +15 -0
  45. package/dist/remark-heading.d.ts +2 -0
  46. package/dist/remark-heading.js +43 -0
  47. package/dist/remark-image-C28nbosW.d.ts +29 -0
  48. package/dist/remark-image.d.ts +2 -0
  49. package/dist/remark-image.js +141 -0
  50. package/dist/remark-include.d.ts +22 -0
  51. package/dist/remark-include.js +336 -0
  52. package/dist/remark-llms.d.ts +36 -0
  53. package/dist/remark-llms.js +191 -0
  54. package/dist/remark-mdx-mermaid.d.ts +10 -0
  55. package/dist/remark-mdx-mermaid.js +22 -0
  56. package/dist/remark-npm-DdmTKhyi.d.ts +18 -0
  57. package/dist/remark-npm.d.ts +2 -0
  58. package/dist/remark-npm.js +72 -0
  59. package/dist/remark-steps.d.ts +14 -0
  60. package/dist/remark-steps.js +127 -0
  61. package/dist/remark-structure.d.ts +29 -0
  62. package/dist/remark-structure.js +94 -0
  63. package/dist/remark-ts2js.d.ts +25 -0
  64. package/dist/remark-ts2js.js +56 -0
  65. package/dist/renderer-BtFWPZB8.js +54 -0
  66. package/dist/renderer-E5KzV19n.d.ts +30 -0
  67. package/dist/stringifier-COIWcXys.js +130 -0
  68. package/dist/utils-I3DkmAkd.js +27 -0
  69. package/package.json +124 -0
@@ -0,0 +1,14 @@
1
+ import { MdastVisitorContext } from "satteri";
2
+ import { Heading } from "mdast";
3
+ //#region src/remark-steps.d.ts
4
+ interface RemarkStepsOptions {
5
+ steps?: string;
6
+ step?: string;
7
+ }
8
+ declare function remarkSteps({ steps, step }?: RemarkStepsOptions): () => {
9
+ name: string;
10
+ heading(node: Readonly<Heading>, ctx: MdastVisitorContext): undefined;
11
+ after(_root: Readonly<import("mdast").Root>, ctx: MdastVisitorContext): void;
12
+ };
13
+ //#endregion
14
+ export { RemarkStepsOptions, remarkSteps };
@@ -0,0 +1,127 @@
1
+ import { n as handleTag } from "./utils-I3DkmAkd.js";
2
+ import { defineMdastPlugin } from "satteri";
3
+ //#region src/remark-steps.ts
4
+ const StepRegex = /^(\d+)\.\s(.+)$/;
5
+ const StepTag = "[step]";
6
+ function remarkSteps({ steps = "fd-steps", step = "fd-step" } = {}) {
7
+ function convertToSteps(nodes) {
8
+ const depth = nodes[0].depth;
9
+ const children = [];
10
+ for (const node of nodes) if (node.type === "heading" && node.depth === depth) children.push({
11
+ type: "mdxJsxFlowElement",
12
+ name: "div",
13
+ attributes: [{
14
+ type: "mdxJsxAttribute",
15
+ name: "className",
16
+ value: step
17
+ }],
18
+ children: [node]
19
+ });
20
+ else children[children.length - 1].children.push(node);
21
+ return {
22
+ type: "mdxJsxFlowElement",
23
+ name: "div",
24
+ attributes: [{
25
+ type: "mdxJsxAttribute",
26
+ name: "className",
27
+ value: steps
28
+ }],
29
+ children
30
+ };
31
+ }
32
+ function handleHeadingStep(node) {
33
+ const head = node.children[0];
34
+ if (head?.type === "text") {
35
+ const match = StepRegex.exec(head.value);
36
+ if (match) {
37
+ const newChildren = [...node.children];
38
+ newChildren[0] = {
39
+ ...head,
40
+ value: match[2]
41
+ };
42
+ return {
43
+ ...node,
44
+ children: newChildren
45
+ };
46
+ }
47
+ }
48
+ const tail = node.children[node.children.length - 1];
49
+ if (tail?.type === "text") {
50
+ const stepValue = handleTag(tail.value, StepTag);
51
+ if (stepValue !== false) {
52
+ const newChildren = [...node.children];
53
+ newChildren[newChildren.length - 1] = {
54
+ ...tail,
55
+ value: stepValue
56
+ };
57
+ return {
58
+ ...node,
59
+ children: newChildren
60
+ };
61
+ }
62
+ }
63
+ return false;
64
+ }
65
+ function processChildren(parent, ctx) {
66
+ const output = [...parent.children];
67
+ let startIdx = -1;
68
+ let i = 0;
69
+ let currentStep = 1;
70
+ let changed = false;
71
+ const onEnd = () => {
72
+ if (startIdx === -1) return;
73
+ const nodes = output.splice(startIdx, i - startIdx);
74
+ output.splice(startIdx, 0, convertToSteps(nodes));
75
+ changed = true;
76
+ i = startIdx + 1;
77
+ startIdx = -1;
78
+ currentStep = 1;
79
+ };
80
+ for (; i < output.length; i++) {
81
+ const node = output[i];
82
+ if (node.type !== "heading") continue;
83
+ if ((node.data ?? {}).hProperties?.["data-fd-step"] !== void 0) continue;
84
+ if (startIdx !== -1) {
85
+ const startDepth = output[startIdx].depth;
86
+ if (node.depth !== startDepth) {
87
+ if (node.depth < startDepth) onEnd();
88
+ continue;
89
+ }
90
+ }
91
+ const stepped = handleHeadingStep(node);
92
+ if (!stepped) {
93
+ onEnd();
94
+ continue;
95
+ }
96
+ const steppedData = stepped.data ?? {};
97
+ output[i] = {
98
+ ...stepped,
99
+ data: {
100
+ ...steppedData,
101
+ hProperties: {
102
+ ...steppedData.hProperties,
103
+ "data-fd-step": currentStep++
104
+ }
105
+ }
106
+ };
107
+ if (startIdx === -1) startIdx = i;
108
+ }
109
+ onEnd();
110
+ if (changed) ctx.setProperty(parent, "children", output);
111
+ }
112
+ return () => {
113
+ const parents = /* @__PURE__ */ new Set();
114
+ return defineMdastPlugin({
115
+ name: "remark-steps",
116
+ heading(node, ctx) {
117
+ const parent = ctx.parent(node);
118
+ if (parent && "children" in parent) parents.add(parent);
119
+ },
120
+ after(_root, ctx) {
121
+ for (const parent of parents) processChildren(parent, ctx);
122
+ }
123
+ });
124
+ };
125
+ }
126
+ //#endregion
127
+ export { remarkSteps };
@@ -0,0 +1,29 @@
1
+ import { o as ExtraPluginHooks } from "./compile-SBpZsgmI.js";
2
+ import { MdastPluginDefinition } from "satteri";
3
+ import { StructuredData } from "@vx-oss/docs-core/mdx-plugins/remark-structure";
4
+ import { Nodes } from "mdast";
5
+ //#region src/remark-structure.d.ts
6
+ interface StructureOptions {
7
+ types?: string[] | ((node: Nodes) => boolean);
8
+ mdxTypes?: (node: Nodes) => boolean;
9
+ /**
10
+ * Include Markdown syntax in content records, sliced from the authored source.
11
+ *
12
+ * Links and JSX elements are flattened into their plain text, return `true` from
13
+ * `filterElement` to keep an element's syntax instead.
14
+ */
15
+ stringify?: boolean | {
16
+ filterElement?: (node: Nodes & {
17
+ name?: string | null;
18
+ }) => boolean;
19
+ };
20
+ /**
21
+ * export as `structuredData` (if true) or specified variable name.
22
+ *
23
+ * @default true
24
+ */
25
+ exportAs?: string | boolean;
26
+ }
27
+ declare function remarkStructure({ types, mdxTypes, stringify, exportAs }?: StructureOptions): ExtraPluginHooks & (() => MdastPluginDefinition);
28
+ //#endregion
29
+ export { StructureOptions, type StructuredData, remarkStructure };
@@ -0,0 +1,94 @@
1
+ import { t as createStringifier } from "./stringifier-COIWcXys.js";
2
+ //#region src/remark-structure.ts
3
+ const STRUCTURE_VISITORS = [
4
+ "heading",
5
+ "paragraph",
6
+ "blockquote",
7
+ "tableCell",
8
+ "mdxJsxFlowElement"
9
+ ];
10
+ function remarkStructure({ types = [
11
+ "heading",
12
+ "paragraph",
13
+ "blockquote",
14
+ "tableCell",
15
+ "mdxJsxFlowElement"
16
+ ], mdxTypes = (node) => !("children" in node) || node.children.length === 0, stringify = false, exportAs = true } = {}) {
17
+ const matchType = typeof types === "function" ? types : (node) => types.includes(node.type);
18
+ const filterElement = typeof stringify === "object" ? stringify.filterElement : void 0;
19
+ const plugin = () => {
20
+ const data = {
21
+ contents: [],
22
+ headings: []
23
+ };
24
+ const records = [];
25
+ let lastHeading;
26
+ let s;
27
+ function visit(node) {
28
+ if (stringify && (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement")) {
29
+ if (!filterElement?.(node)) s.flatten(node);
30
+ }
31
+ if (!matchType(node)) return;
32
+ if ((node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") && !mdxTypes(node)) return;
33
+ if (node.type === "heading") {
34
+ const id = node.data?.hProperties?.id;
35
+ if (!id) return;
36
+ records.push({
37
+ node,
38
+ heading: void 0,
39
+ id
40
+ });
41
+ lastHeading = id;
42
+ return;
43
+ }
44
+ records.push({
45
+ node,
46
+ heading: lastHeading
47
+ });
48
+ }
49
+ const definition = {
50
+ name: "remark-structure",
51
+ options: stringify ? { position: true } : void 0,
52
+ before(_root, ctx) {
53
+ ctx.data.structuredData ??= data;
54
+ s = createStringifier(ctx);
55
+ const openapiData = ctx.data.frontmatter?._openapi?.structuredData;
56
+ if (openapiData) {
57
+ data.headings.push(...openapiData.headings);
58
+ data.contents.push(...openapiData.contents);
59
+ }
60
+ },
61
+ after(_root, ctx) {
62
+ for (const record of records) {
63
+ const content = (stringify && !record.id ? s.stringify(record.node) : ctx.textContent(record.node)).trim();
64
+ if (content.length === 0) continue;
65
+ if (record.id) data.headings.push({
66
+ id: record.id,
67
+ content
68
+ });
69
+ else data.contents.push({
70
+ heading: record.heading,
71
+ content
72
+ });
73
+ }
74
+ },
75
+ ...Object.fromEntries(STRUCTURE_VISITORS.map((key) => [key, visit]))
76
+ };
77
+ if (stringify) Object.assign(definition, {
78
+ mdxJsxTextElement: visit,
79
+ link: (node) => s.flatten(node),
80
+ linkReference: (node) => s.flatten(node),
81
+ image: (node) => s.remove(node)
82
+ });
83
+ return definition;
84
+ };
85
+ plugin.collectExports = ({ data, addExport }) => {
86
+ if (exportAs) addExport(typeof exportAs === "string" ? exportAs : "structuredData", JSON.stringify(data.structuredData ?? {
87
+ headings: [],
88
+ contents: []
89
+ }));
90
+ };
91
+ return plugin;
92
+ }
93
+ //#endregion
94
+ export { remarkStructure };
@@ -0,0 +1,25 @@
1
+ //#region src/remark-ts2js.d.ts
2
+ interface RemarkTs2jsOptions {
3
+ /**
4
+ * Persist Tab value (Fumadocs UI only)
5
+ *
6
+ * @defaultValue false
7
+ */
8
+ persist?: {
9
+ id: string;
10
+ } | false;
11
+ defaultValue?: 'js' | 'ts';
12
+ /**
13
+ * Transform all TypeScript codeblocks by default, without a trigger
14
+ */
15
+ disableTrigger?: boolean;
16
+ }
17
+ /**
18
+ * Transform TypeScript codeblocks into TS/JS tabs. Add `ts2js` to the code fence meta to enable.
19
+ */
20
+ declare function remarkTs2js({ persist, defaultValue, disableTrigger }?: RemarkTs2jsOptions): {
21
+ name: string;
22
+ code(node: Readonly<import("mdast").Code>, ctx: import("satteri").MdastVisitorContext): Promise<undefined>;
23
+ };
24
+ //#endregion
25
+ export { RemarkTs2jsOptions, remarkTs2js };
@@ -0,0 +1,56 @@
1
+ import { defineMdastPlugin } from "satteri";
2
+ import { generateCodeBlockTabs, parseCodeBlockAttributes } from "@vx-oss/docs-core/mdx-plugins/codeblock-utils";
3
+ //#region src/remark-ts2js.ts
4
+ /**
5
+ * Transform TypeScript codeblocks into TS/JS tabs. Add `ts2js` to the code fence meta to enable.
6
+ */
7
+ function remarkTs2js({ persist = false, defaultValue = "ts", disableTrigger = false } = {}) {
8
+ return defineMdastPlugin({
9
+ name: "remark-typescript-to-javascript",
10
+ async code(node, ctx) {
11
+ const lang = node.lang;
12
+ if (lang !== "ts" && lang !== "tsx") return;
13
+ const meta = parseCodeBlockAttributes(node.meta ?? "", ["ts2js"]);
14
+ if (!disableTrigger && !("ts2js" in meta.attributes)) return;
15
+ const [{ parse }, { strip }] = await Promise.all([import("yuku-parser"), import("yuku-codegen")]);
16
+ const { program } = parse(node.value, { lang });
17
+ const result = strip(program);
18
+ ctx.replaceNode(node, generateCodeBlockTabs({
19
+ persist,
20
+ defaultValue,
21
+ triggers: [{
22
+ value: "ts",
23
+ children: [{
24
+ type: "text",
25
+ value: "TypeScript"
26
+ }]
27
+ }, {
28
+ value: "js",
29
+ children: [{
30
+ type: "text",
31
+ value: "JavaScript"
32
+ }]
33
+ }],
34
+ tabs: [{
35
+ value: "ts",
36
+ children: [{
37
+ type: "code",
38
+ lang: node.lang,
39
+ meta: meta.rest,
40
+ value: node.value
41
+ }]
42
+ }, {
43
+ value: "js",
44
+ children: [{
45
+ type: "code",
46
+ lang: lang === "tsx" ? "jsx" : "js",
47
+ meta: typeof meta.attributes.ts2js === "string" ? meta.attributes.ts2js : meta.rest,
48
+ value: result.code
49
+ }]
50
+ }]
51
+ }));
52
+ }
53
+ });
54
+ }
55
+ //#endregion
56
+ export { remarkTs2js };
@@ -0,0 +1,54 @@
1
+ import { asMarkdown, jsxComponents } from "@vx-oss/docs-core/server";
2
+ import * as JsxRuntime from "react/jsx-runtime";
3
+ //#region src/local-md/renderer.ts
4
+ const AsyncFunction = Object.getPrototypeOf(async function() {}).constructor;
5
+ function fromJS(options) {
6
+ const { code, structuredData = {
7
+ headings: [],
8
+ contents: []
9
+ }, baseUrl } = options;
10
+ function createScope(context) {
11
+ const { opts, ...rest } = context ?? {};
12
+ return {
13
+ opts: {
14
+ ...opts,
15
+ ...JsxRuntime,
16
+ asMarkdown,
17
+ jsxComponents,
18
+ baseUrl
19
+ },
20
+ ...rest
21
+ };
22
+ }
23
+ function toResult(out, components) {
24
+ return {
25
+ toc: out.toc ?? [],
26
+ body: JsxRuntime.jsx(out.default, { components }),
27
+ exports: out
28
+ };
29
+ }
30
+ return {
31
+ structuredData,
32
+ async render(components, context) {
33
+ const scope = createScope(context);
34
+ const hydrateFn = new AsyncFunction(...Object.keys(scope), code);
35
+ return toResult(await hydrateFn.apply(hydrateFn, Object.values(scope)), components);
36
+ },
37
+ renderSync(components, context) {
38
+ const scope = createScope(context);
39
+ const hydrateFn = new Function(...Object.keys(scope), code);
40
+ return toResult(hydrateFn.apply(hydrateFn, Object.values(scope)), components);
41
+ },
42
+ serialize() {
43
+ return {
44
+ type: "js",
45
+ ...options
46
+ };
47
+ }
48
+ };
49
+ }
50
+ function fromSerialized(options) {
51
+ return fromJS(options);
52
+ }
53
+ //#endregion
54
+ export { fromSerialized as n, fromJS as t };
@@ -0,0 +1,30 @@
1
+ import { StructuredData } from "@vx-oss/docs-core/mdx-plugins/remark-structure";
2
+ import { TOCItemType } from "@vx-oss/docs-core/toc";
3
+ import { ReactNode } from "react";
4
+ import { MDXComponents } from "mdx/types";
5
+ //#region src/local-md/renderer.d.ts
6
+ interface MarkdownRenderer<M = Record<string, unknown>> {
7
+ structuredData: StructuredData;
8
+ render: (components?: MDXComponents, context?: Record<string, unknown>) => Promise<MarkdownRendererResult<M>>;
9
+ renderSync: (components?: MDXComponents, context?: Record<string, unknown>) => MarkdownRendererResult<M>;
10
+ /** JSON-serializable, rebuild it on the client with {@link fromSerialized} */
11
+ serialize: () => MarkdownRendererSerializedOptions;
12
+ }
13
+ interface MarkdownRendererResult<M = Record<string, unknown>> {
14
+ exports: M;
15
+ toc: TOCItemType[];
16
+ body: ReactNode;
17
+ }
18
+ type MarkdownRendererSerializedOptions = {
19
+ type: 'js';
20
+ } & MarkdownRendererJSOptions;
21
+ interface MarkdownRendererJSOptions {
22
+ /** compiled `function-body` JavaScript. */
23
+ code: string;
24
+ filePath: string;
25
+ baseUrl?: string;
26
+ structuredData?: StructuredData;
27
+ }
28
+ declare function fromSerialized<M>(options: MarkdownRendererSerializedOptions): MarkdownRenderer<M>;
29
+ //#endregion
30
+ export { fromSerialized as i, MarkdownRendererResult as n, MarkdownRendererSerializedOptions as r, MarkdownRenderer as t };
@@ -0,0 +1,130 @@
1
+ //#region src/stringifier.ts
2
+ function offsets(node) {
3
+ const pos = node.position;
4
+ if (pos?.start.offset !== void 0 && pos.end.offset !== void 0) return {
5
+ start: pos.start.offset,
6
+ end: pos.end.offset
7
+ };
8
+ }
9
+ /**
10
+ * Record the Markdown form of generated content over an authored node's source range,
11
+ * for plugins that replace nodes (e.g. includes, generated type tables). Source-based
12
+ * Markdown output cannot see the replacement, it shows this text instead.
13
+ *
14
+ * `markdown` is only called when source positions are tracked, i.e. when a consumer
15
+ * of the recorded edits is present.
16
+ */
17
+ function replaceSource(ctx, node, markdown) {
18
+ const pos = offsets(node);
19
+ if (pos) (ctx.data._sourceEdits ??= []).push({
20
+ start: pos.start,
21
+ end: pos.end,
22
+ text: markdown()
23
+ });
24
+ }
25
+ /**
26
+ * Rows as an aligned Markdown table, the first row being the header.
27
+ * Newlines in cells are collapsed and `|` is escaped.
28
+ */
29
+ function formatTable(rows) {
30
+ const cells = [];
31
+ const widths = [];
32
+ for (const row of rows) {
33
+ const escaped = [];
34
+ for (let i = 0; i < row.length; i++) {
35
+ const text = row[i].replace(/\s*\n\s*/g, " ").replaceAll("|", "\\|");
36
+ widths[i] = Math.max(widths[i] ?? 0, text.length);
37
+ escaped.push(text);
38
+ }
39
+ cells.push(escaped);
40
+ }
41
+ let out = "";
42
+ for (let r = 0; r < cells.length; r++) {
43
+ let line = "|";
44
+ for (let i = 0; i < widths.length; i++) line += ` ${(cells[r][i] ?? "").padEnd(widths[i])} |`;
45
+ out += `${line}\n`;
46
+ if (r === 0) {
47
+ line = "|";
48
+ for (const width of widths) line += ` ${"-".repeat(width)} |`;
49
+ out += `${line}\n`;
50
+ }
51
+ }
52
+ return out;
53
+ }
54
+ function createStringifier(ctx) {
55
+ const source = ctx.source;
56
+ const own = [];
57
+ let sorted;
58
+ function edits() {
59
+ if (!sorted) {
60
+ sorted = ctx.data._sourceEdits ? [...ctx.data._sourceEdits, ...own] : [...own];
61
+ sorted.sort((a, b) => a.start - b.start);
62
+ }
63
+ return sorted;
64
+ }
65
+ return {
66
+ source,
67
+ get edits() {
68
+ return edits();
69
+ },
70
+ edit(start, end, text) {
71
+ own.push({
72
+ start,
73
+ end,
74
+ text
75
+ });
76
+ sorted = void 0;
77
+ },
78
+ remove(node) {
79
+ const pos = offsets(node);
80
+ if (pos) this.edit(pos.start, pos.end, "");
81
+ },
82
+ flatten(node) {
83
+ const pos = offsets(node);
84
+ if (pos) this.edit(pos.start, pos.end, ctx.textContent(node));
85
+ },
86
+ slice(start, end) {
87
+ return splice(source, start, end, edits());
88
+ },
89
+ stringify(node) {
90
+ const pos = offsets(node);
91
+ if (pos) return splice(source, pos.start, pos.end, edits());
92
+ const { type } = node;
93
+ if (type === "mdxJsxFlowElement" || type === "mdxJsxTextElement") return syntheticElement(node, ctx);
94
+ return ctx.textContent(node);
95
+ }
96
+ };
97
+ }
98
+ function splice(source, start, end, edits) {
99
+ let lo = 0;
100
+ let hi = edits.length;
101
+ while (lo < hi) {
102
+ const mid = lo + hi >> 1;
103
+ if (edits[mid].start < start) lo = mid + 1;
104
+ else hi = mid;
105
+ }
106
+ let out = "";
107
+ let cursor = start;
108
+ for (let i = lo; i < edits.length && edits[i].start < end; i++) {
109
+ const edit = edits[i];
110
+ if (edit.end > end) continue;
111
+ out += source.slice(cursor, edit.start) + edit.text;
112
+ cursor = edit.end;
113
+ if (edit.text === "") while (source[cursor] === "\n" && (out === "" || out.endsWith("\n\n"))) cursor++;
114
+ }
115
+ return out + source.slice(cursor, end);
116
+ }
117
+ /** the tag of a plugin-inserted JSX element, reconstructed from its fields */
118
+ function syntheticElement(node, ctx) {
119
+ let attrs = "";
120
+ for (const attr of node.attributes) if (attr.type === "mdxJsxExpressionAttribute") {
121
+ if (attr.value) attrs += ` {${attr.value}}`;
122
+ } else if (attr.value == null) attrs += ` ${attr.name}`;
123
+ else if (typeof attr.value === "string") attrs += ` ${attr.name}="${attr.value}"`;
124
+ else if (attr.value.value) attrs += ` ${attr.name}={${attr.value.value}}`;
125
+ const children = ctx.textContent(node);
126
+ if (!children) return `<${node.name}${attrs} />`;
127
+ return `<${node.name}${attrs}>${children}</${node.name}>`;
128
+ }
129
+ //#endregion
130
+ export { replaceSource as i, formatTable as n, offsets as r, createStringifier as t };
@@ -0,0 +1,27 @@
1
+ import { jsx, toJs } from "estree-util-to-js";
2
+ import { toEstree } from "hast-util-to-estree";
3
+ //#region src/utils.ts
4
+ /**
5
+ * Flatten a node's text content by walking it in JavaScript.
6
+ *
7
+ * Prefer `ctx.textContent(node, { includeImageAlt: false })` for nodes of the
8
+ * visited document — it walks the tree in Rust without materializing the
9
+ * subtree. This util is for detached trees (e.g. `mdxToMdast()` output), which
10
+ * have no handle behind them and can't use the visitor context.
11
+ */
12
+ function flattenNode(node) {
13
+ if ("children" in node && Array.isArray(node.children)) return node.children.map((child) => flattenNode(child)).join("");
14
+ if ("value" in node && typeof node.value === "string") return node.value;
15
+ return "";
16
+ }
17
+ function handleTag(value, tag) {
18
+ const idx = value.indexOf(tag);
19
+ if (idx !== -1) return value.slice(0, idx).trimEnd() + value.slice(idx + tag.length);
20
+ return false;
21
+ }
22
+ function jsxToSource(hast) {
23
+ const source = toJs(toEstree(hast, { elementAttributeNameCase: "react" }), { handlers: jsx }).value.trim();
24
+ return source.endsWith(";") ? source.slice(0, -1) : source;
25
+ }
26
+ //#endregion
27
+ export { handleTag as n, jsxToSource as r, flattenNode as t };