@temelj/mdx 0.8.0 → 0.8.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.
Files changed (44) hide show
  1. package/dist/compiler.d.ts +28 -20
  2. package/dist/compiler.d.ts.map +1 -0
  3. package/dist/compiler.js +62 -71
  4. package/dist/compiler.js.map +1 -0
  5. package/dist/mod.d.ts +8 -3
  6. package/dist/mod.js +8 -3
  7. package/dist/plugins/heading-id/plugin.d.ts +12 -8
  8. package/dist/plugins/heading-id/plugin.d.ts.map +1 -0
  9. package/dist/plugins/heading-id/plugin.js +20 -15
  10. package/dist/plugins/heading-id/plugin.js.map +1 -0
  11. package/dist/plugins/mod.d.ts +5 -4
  12. package/dist/plugins/mod.js +6 -4
  13. package/dist/plugins/remove-imports-exports.d.ts +9 -5
  14. package/dist/plugins/remove-imports-exports.d.ts.map +1 -0
  15. package/dist/plugins/remove-imports-exports.js +13 -7
  16. package/dist/plugins/remove-imports-exports.js.map +1 -0
  17. package/dist/plugins/syntax-highlight/code-meta.d.ts +19 -14
  18. package/dist/plugins/syntax-highlight/code-meta.d.ts.map +1 -0
  19. package/dist/plugins/syntax-highlight/code-meta.js +31 -43
  20. package/dist/plugins/syntax-highlight/code-meta.js.map +1 -0
  21. package/dist/plugins/syntax-highlight/plugin.d.ts +43 -41
  22. package/dist/plugins/syntax-highlight/plugin.d.ts.map +1 -0
  23. package/dist/plugins/syntax-highlight/plugin.js +88 -129
  24. package/dist/plugins/syntax-highlight/plugin.js.map +1 -0
  25. package/dist/plugins/tree-processor/plugin.d.ts +11 -9
  26. package/dist/plugins/tree-processor/plugin.d.ts.map +1 -0
  27. package/dist/plugins/tree-processor/plugin.js +19 -16
  28. package/dist/plugins/tree-processor/plugin.js.map +1 -0
  29. package/dist/types.d.ts +2 -2
  30. package/dist/types.js +1 -1
  31. package/package.json +9 -4
  32. package/.turbo/turbo-build.log +0 -4
  33. package/compiler.test.ts +0 -117
  34. package/compiler.ts +0 -124
  35. package/mod.ts +0 -3
  36. package/plugins/heading-id/plugin.ts +0 -42
  37. package/plugins/mod.ts +0 -4
  38. package/plugins/remove-imports-exports.ts +0 -18
  39. package/plugins/syntax-highlight/code-meta.test.ts +0 -22
  40. package/plugins/syntax-highlight/code-meta.ts +0 -76
  41. package/plugins/syntax-highlight/plugin.ts +0 -266
  42. package/plugins/tree-processor/plugin.ts +0 -54
  43. package/tsconfig.json +0 -18
  44. package/types.ts +0 -3
@@ -1,56 +1,58 @@
1
- import { type BundledLanguage, type BundledTheme, type CodeToHastOptions } from "shiki";
2
- import { type TransformerNotationHighlightOptions } from "@shikijs/transformers";
3
- import type { Plugin } from "unified";
4
- import type { HastNode } from "../../types";
1
+ import { HastNode as Node } from "../../types.js";
2
+ import { BundledLanguage, BundledTheme, CodeToHastOptions } from "shiki";
3
+ import { TransformerNotationHighlightOptions } from "@shikijs/transformers";
4
+ import { Plugin } from "unified";
5
+
6
+ //#region src/plugins/syntax-highlight/plugin.d.ts
5
7
  type ShikiHastOptions = Partial<CodeToHastOptions<BundledLanguage, BundledTheme>>;
6
8
  type SyntaxDataAttribute = "source-code" | "language" | "line-count";
7
9
  /**
8
10
  * Options for {@linkcode syntaxHighlightPlugin}.
9
11
  */
10
- export interface SyntaxHighlightPluginOptions {
12
+ interface SyntaxHighlightPluginOptions {
13
+ /**
14
+ * Include these data attributes on the `<pre>` element.
15
+ * These might be useful to have when building custom code components.
16
+ */
17
+ includeDataAttributes?: SyntaxDataAttribute[];
18
+ /**
19
+ * Options for the highlight transformer.
20
+ */
21
+ highlight?: {
22
+ transformer?: TransformerNotationHighlightOptions;
23
+ };
24
+ /**
25
+ * Options for line numbers.
26
+ */
27
+ lineNumbers?: {
11
28
  /**
12
- * Include these data attributes on the `<pre>` element.
13
- * These might be useful to have when building custom code components.
29
+ * The name of the variable that contains the line number width.
30
+ * This is used for padding the line numbers so that they align.
14
31
  */
15
- includeDataAttributes?: SyntaxDataAttribute[];
32
+ widthVariableName?: string;
16
33
  /**
17
- * Options for the highlight transformer.
34
+ * A class name to be added to the line numbers.
18
35
  */
19
- highlight?: {
20
- transformer?: TransformerNotationHighlightOptions;
21
- };
36
+ className?: string;
37
+ };
38
+ /**
39
+ * Options for command line.
40
+ */
41
+ commandLine?: {
22
42
  /**
23
- * Options for line numbers.
43
+ * A class name to be added to the prompt line.
24
44
  */
25
- lineNumbers?: {
26
- /**
27
- * The name of the variable that contains the line number width.
28
- * This is used for padding the line numbers so that they align.
29
- */
30
- widthVariableName?: string;
31
- /**
32
- * A class name to be added to the line numbers.
33
- */
34
- className?: string;
35
- };
36
- /**
37
- * Options for command line.
38
- */
39
- commandLine?: {
40
- /**
41
- * A class name to be added to the prompt line.
42
- */
43
- className?: string;
44
- };
45
- /**
46
- * Extra options for the Shiki hast transformer.
47
- */
48
- shikiHastOptions?: ShikiHastOptions;
45
+ className?: string;
46
+ };
47
+ /**
48
+ * Extra options for the Shiki hast transformer.
49
+ */
50
+ shikiHastOptions?: ShikiHastOptions;
49
51
  }
50
52
  /**
51
53
  * A plugin that highlights code blocks using Shiki.
52
54
  */
53
- export declare const syntaxHighlightPlugin: Plugin<[
54
- SyntaxHighlightPluginOptions?
55
- ], HastNode, HastNode>;
56
- export {};
55
+ declare const syntaxHighlightPlugin: Plugin<[SyntaxHighlightPluginOptions?], Node, Node>;
56
+ //#endregion
57
+ export { SyntaxHighlightPluginOptions, syntaxHighlightPlugin };
58
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../../src/plugins/syntax-highlight/plugin.ts"],"sourcesContent":[],"mappings":";;;;;;KAqBK,gBAAA,GAAmB,QACtB,kBAAkB,iBAAiB;KAGhC,mBAAA;AARoD;;;AAKvD,UAQe,4BAAA,CARf;EADsB;;AAAO;AAS/B;EAK0B,qBAAA,CAAA,EAAA,mBAAA,EAAA;EAMR;;;EAqCL,SAAA,CAAA,EAAA;IACV,WAAA,CAAA,EAtCe,mCAsCf;EACD,CAAA;EACA;;;;;;;;;;;;;;;;;;;;;;;;;;qBATmB;;;;;cAMR,uBAAuB,QACjC,gCACD,MACA"}
@@ -1,135 +1,94 @@
1
- import { toString as hastToString } from "hast-util-to-string";
2
- import { codeToHast, ShikiError, } from "shiki";
1
+ import { extractCodeMeta } from "./code-meta.js";
2
+ import { toString } from "hast-util-to-string";
3
3
  import { visit } from "unist-util-visit";
4
- import { transformerNotationHighlight, } from "@shikijs/transformers";
4
+ import { ShikiError, codeToHast } from "shiki";
5
+ import { transformerNotationHighlight } from "@shikijs/transformers";
5
6
  import { numericRangeContains } from "@temelj/iterator";
6
- import { extractCodeMeta } from "./code-meta";
7
+
8
+ //#region src/plugins/syntax-highlight/plugin.ts
7
9
  /**
8
- * A plugin that highlights code blocks using Shiki.
9
- */
10
- export const syntaxHighlightPlugin = (options = {}) => {
11
- return async (tree) => {
12
- const promises = [];
13
- async function visitor(node, index, parent) {
14
- if (!parent || index === null || node.tagName !== "pre") {
15
- return;
16
- }
17
- const codeNode = node.children[0];
18
- if (!codeNode ||
19
- codeNode.type !== "element" ||
20
- codeNode.tagName !== "code" ||
21
- !codeNode.properties) {
22
- return;
23
- }
24
- const lang = getNodeLanguage(codeNode);
25
- if (typeof lang !== "string") {
26
- return;
27
- }
28
- const meta = extractCodeMeta(codeNode);
29
- const sourceCode = hastToString(codeNode).trim();
30
- const hastOptions = {
31
- tokenizeTimeLimit: 10_000,
32
- lang,
33
- themes: {
34
- light: "github-light-default",
35
- dark: "github-dark-default",
36
- },
37
- ...(options.shikiHastOptions ?? {}),
38
- };
39
- if (hastOptions.transformers === undefined) {
40
- hastOptions.transformers = [];
41
- }
42
- if (options.highlight) {
43
- hastOptions.transformers.push(transformerNotationHighlight({
44
- ...(options.highlight.transformer ?? {}),
45
- matchAlgorithm: "v3",
46
- }));
47
- }
48
- let lineCount = 0;
49
- hastOptions.transformers.push({
50
- code(node) {
51
- let lineIndex = 0;
52
- for (const line of node.children.filter((node) => node.type === "element")) {
53
- line.properties["data-line"] = lineIndex + 1;
54
- if (options.highlight) {
55
- if (meta.highlight &&
56
- numericRangeContains(meta.highlight.lineRange, lineIndex)) {
57
- this.addClassToHast(line, options.highlight.transformer?.classActiveLine ??
58
- "highlighted");
59
- }
60
- }
61
- if (options.lineNumbers &&
62
- meta.showLineNumbers &&
63
- (meta.showLineNumbers.lineRange.length === 0 ||
64
- numericRangeContains(meta.showLineNumbers.lineRange, lineIndex))) {
65
- this.addClassToHast(line, options.lineNumbers.className ?? "line-number");
66
- }
67
- if (options.commandLine &&
68
- meta.commandLine &&
69
- (meta.commandLine.commandRange.length === 0 ||
70
- numericRangeContains(meta.commandLine.commandRange, lineIndex))) {
71
- this.addClassToHast(line, options.commandLine.className ?? "line-command");
72
- }
73
- lineIndex++;
74
- }
75
- lineCount = lineIndex;
76
- if (meta.showLineNumbers) {
77
- if (typeof node.properties.style !== "string") {
78
- node.properties.style = "";
79
- }
80
- node.properties.style += `--${options.lineNumbers?.widthVariableName ?? "line-number-width"}:${1 + Math.floor(Math.log10(this.lines.length))}ch;`;
81
- }
82
- },
83
- });
84
- try {
85
- const hast = await codeToHast(sourceCode, hastOptions);
86
- // Insert highlighted code into parent
87
- const hastPre = hast.children[0];
88
- const properties = {
89
- ...hastPre.properties,
90
- dataFileName: meta.fileName,
91
- };
92
- if (options.includeDataAttributes) {
93
- if (options.includeDataAttributes.includes("language")) {
94
- properties.dataLanguage = lang;
95
- }
96
- if (options.includeDataAttributes.includes("source-code")) {
97
- properties.dataSourceCode = sourceCode;
98
- }
99
- if (options.includeDataAttributes.includes("line-count")) {
100
- properties.dataLineCount = lineCount;
101
- }
102
- }
103
- parent.children.splice(index, 1, {
104
- ...hastPre,
105
- properties,
106
- });
107
- }
108
- catch (error) {
109
- if (error instanceof ShikiError) {
110
- if (error.message.includes("Language `math` is not included in this bundle")) {
111
- // Skip if the block is math block.
112
- return;
113
- }
114
- }
115
- throw error;
116
- }
117
- }
118
- visit(tree, "element", (node, index, parent) => {
119
- promises.push(visitor(node, index, parent));
120
- });
121
- await Promise.all(promises);
122
- return tree;
123
- };
10
+ * A plugin that highlights code blocks using Shiki.
11
+ */
12
+ const syntaxHighlightPlugin = (options = {}) => {
13
+ return async (tree) => {
14
+ const promises = [];
15
+ async function visitor(node, index, parent) {
16
+ if (!parent || index === null || node.tagName !== "pre") return;
17
+ const codeNode = node.children[0];
18
+ if (!codeNode || codeNode.type !== "element" || codeNode.tagName !== "code" || !codeNode.properties) return;
19
+ const lang = getNodeLanguage(codeNode);
20
+ if (typeof lang !== "string") return;
21
+ const meta = extractCodeMeta(codeNode);
22
+ const sourceCode = toString(codeNode).trim();
23
+ const hastOptions = {
24
+ tokenizeTimeLimit: 1e4,
25
+ lang,
26
+ themes: {
27
+ light: "github-light-default",
28
+ dark: "github-dark-default"
29
+ },
30
+ ...options.shikiHastOptions ?? {}
31
+ };
32
+ if (hastOptions.transformers === void 0) hastOptions.transformers = [];
33
+ if (options.highlight) hastOptions.transformers.push(transformerNotationHighlight({
34
+ ...options.highlight.transformer ?? {},
35
+ matchAlgorithm: "v3"
36
+ }));
37
+ let lineCount = 0;
38
+ hastOptions.transformers.push({ code(node$1) {
39
+ let lineIndex = 0;
40
+ for (const line of node$1.children.filter((node$2) => node$2.type === "element")) {
41
+ line.properties["data-line"] = lineIndex + 1;
42
+ if (options.highlight) {
43
+ if (meta.highlight && numericRangeContains(meta.highlight.lineRange, lineIndex)) this.addClassToHast(line, options.highlight.transformer?.classActiveLine ?? "highlighted");
44
+ }
45
+ if (options.lineNumbers && meta.showLineNumbers && (meta.showLineNumbers.lineRange.length === 0 || numericRangeContains(meta.showLineNumbers.lineRange, lineIndex))) this.addClassToHast(line, options.lineNumbers.className ?? "line-number");
46
+ if (options.commandLine && meta.commandLine && (meta.commandLine.commandRange.length === 0 || numericRangeContains(meta.commandLine.commandRange, lineIndex))) this.addClassToHast(line, options.commandLine.className ?? "line-command");
47
+ lineIndex++;
48
+ }
49
+ lineCount = lineIndex;
50
+ if (meta.showLineNumbers) {
51
+ if (typeof node$1.properties.style !== "string") node$1.properties.style = "";
52
+ node$1.properties.style += `--${options.lineNumbers?.widthVariableName ?? "line-number-width"}:${1 + Math.floor(Math.log10(this.lines.length))}ch;`;
53
+ }
54
+ } });
55
+ try {
56
+ const hastPre = (await codeToHast(sourceCode, hastOptions)).children[0];
57
+ const properties = {
58
+ ...hastPre.properties,
59
+ dataFileName: meta.fileName
60
+ };
61
+ if (options.includeDataAttributes) {
62
+ if (options.includeDataAttributes.includes("language")) properties.dataLanguage = lang;
63
+ if (options.includeDataAttributes.includes("source-code")) properties.dataSourceCode = sourceCode;
64
+ if (options.includeDataAttributes.includes("line-count")) properties.dataLineCount = lineCount;
65
+ }
66
+ parent.children.splice(index, 1, {
67
+ ...hastPre,
68
+ properties
69
+ });
70
+ } catch (error) {
71
+ if (error instanceof ShikiError) {
72
+ if (error.message.includes("Language `math` is not included in this bundle")) return;
73
+ }
74
+ throw error;
75
+ }
76
+ }
77
+ visit(tree, "element", (node, index, parent) => {
78
+ promises.push(visitor(node, index, parent));
79
+ });
80
+ await Promise.all(promises);
81
+ return tree;
82
+ };
124
83
  };
125
84
  function getNodeLanguage(node) {
126
- if (!Array.isArray(node.properties.className)) {
127
- return undefined;
128
- }
129
- const prefix = "language-";
130
- const language = node.properties.className.find((name) => typeof name === "string" && name.startsWith(prefix));
131
- if (typeof language === "string") {
132
- return language.slice(prefix.length);
133
- }
134
- return language;
85
+ if (!Array.isArray(node.properties.className)) return;
86
+ const prefix = "language-";
87
+ const language = node.properties.className.find((name) => typeof name === "string" && name.startsWith(prefix));
88
+ if (typeof language === "string") return language.slice(9);
89
+ return language;
135
90
  }
91
+
92
+ //#endregion
93
+ export { syntaxHighlightPlugin };
94
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","names":["syntaxHighlightPlugin: Plugin<\n [SyntaxHighlightPluginOptions?],\n HastNode,\n HastNode\n>","promises: Promise<void>[]","hastToString","hastOptions: CodeToHastOptions<BundledLanguage, BundledTheme>","node","properties: Properties"],"sources":["../../../src/plugins/syntax-highlight/plugin.ts"],"sourcesContent":["import { toString as hastToString } from \"hast-util-to-string\";\nimport {\n type BundledLanguage,\n type BundledTheme,\n codeToHast,\n type CodeToHastOptions,\n ShikiError,\n} from \"shiki\";\nimport { visit } from \"unist-util-visit\";\nimport {\n transformerNotationHighlight,\n type TransformerNotationHighlightOptions,\n} from \"@shikijs/transformers\";\nimport { numericRangeContains } from \"@temelj/iterator\";\nimport type { Plugin } from \"unified\";\nimport type { Properties } from \"hast\";\n\nimport type { HastElement, HastNode } from \"../../types\";\n\nimport { extractCodeMeta } from \"./code-meta\";\n\ntype ShikiHastOptions = Partial<\n CodeToHastOptions<BundledLanguage, BundledTheme>\n>;\n\ntype SyntaxDataAttribute = \"source-code\" | \"language\" | \"line-count\";\n\n/**\n * Options for {@linkcode syntaxHighlightPlugin}.\n */\nexport interface SyntaxHighlightPluginOptions {\n /**\n * Include these data attributes on the `<pre>` element.\n * These might be useful to have when building custom code components.\n */\n includeDataAttributes?: SyntaxDataAttribute[];\n\n /**\n * Options for the highlight transformer.\n */\n highlight?: {\n transformer?: TransformerNotationHighlightOptions;\n };\n\n /**\n * Options for line numbers.\n */\n lineNumbers?: {\n /**\n * The name of the variable that contains the line number width.\n * This is used for padding the line numbers so that they align.\n */\n widthVariableName?: string;\n /**\n * A class name to be added to the line numbers.\n */\n className?: string;\n };\n\n /**\n * Options for command line.\n */\n commandLine?: {\n /**\n * A class name to be added to the prompt line.\n */\n className?: string;\n };\n\n /**\n * Extra options for the Shiki hast transformer.\n */\n shikiHastOptions?: ShikiHastOptions;\n}\n\n/**\n * A plugin that highlights code blocks using Shiki.\n */\nexport const syntaxHighlightPlugin: Plugin<\n [SyntaxHighlightPluginOptions?],\n HastNode,\n HastNode\n> = (options: SyntaxHighlightPluginOptions = {}) => {\n return async (tree) => {\n const promises: Promise<void>[] = [];\n\n async function visitor(\n node: HastElement,\n index: number,\n parent: HastElement,\n ): Promise<void> {\n if (!parent || index === null || node.tagName !== \"pre\") {\n return;\n }\n\n const codeNode = node.children[0];\n if (\n !codeNode ||\n codeNode.type !== \"element\" ||\n codeNode.tagName !== \"code\" ||\n !codeNode.properties\n ) {\n return;\n }\n\n const lang = getNodeLanguage(codeNode);\n if (typeof lang !== \"string\") {\n return;\n }\n\n const meta = extractCodeMeta(codeNode);\n\n const sourceCode = hastToString(codeNode).trim();\n\n const hastOptions: CodeToHastOptions<BundledLanguage, BundledTheme> = {\n tokenizeTimeLimit: 10_000,\n lang,\n themes: {\n light: \"github-light-default\",\n dark: \"github-dark-default\",\n },\n ...(options.shikiHastOptions ?? {}),\n };\n if (hastOptions.transformers === undefined) {\n hastOptions.transformers = [];\n }\n\n if (options.highlight) {\n hastOptions.transformers.push(\n transformerNotationHighlight({\n ...(options.highlight.transformer ?? {}),\n matchAlgorithm: \"v3\",\n }),\n );\n }\n\n let lineCount = 0;\n hastOptions.transformers.push({\n code(node): void {\n let lineIndex = 0;\n for (const line of node.children.filter(\n (node: HastNode) => node.type === \"element\",\n ) as HastElement[]) {\n line.properties[\"data-line\"] = lineIndex + 1;\n\n if (options.highlight) {\n if (\n meta.highlight &&\n numericRangeContains(meta.highlight.lineRange, lineIndex)\n ) {\n this.addClassToHast(\n line,\n options.highlight.transformer?.classActiveLine ??\n \"highlighted\",\n );\n }\n }\n\n if (\n options.lineNumbers &&\n meta.showLineNumbers &&\n (meta.showLineNumbers.lineRange.length === 0 ||\n numericRangeContains(meta.showLineNumbers.lineRange, lineIndex))\n ) {\n this.addClassToHast(\n line,\n options.lineNumbers.className ?? \"line-number\",\n );\n }\n\n if (\n options.commandLine &&\n meta.commandLine &&\n (meta.commandLine.commandRange.length === 0 ||\n numericRangeContains(meta.commandLine.commandRange, lineIndex))\n ) {\n this.addClassToHast(\n line,\n options.commandLine.className ?? \"line-command\",\n );\n }\n\n lineIndex++;\n }\n\n lineCount = lineIndex;\n\n if (meta.showLineNumbers) {\n if (typeof node.properties.style !== \"string\") {\n node.properties.style = \"\";\n }\n node.properties.style += `--${\n options.lineNumbers?.widthVariableName ?? \"line-number-width\"\n }:${1 + Math.floor(Math.log10(this.lines.length))}ch;`;\n }\n },\n });\n\n try {\n const hast = await codeToHast(sourceCode, hastOptions);\n\n // Insert highlighted code into parent\n const hastPre = hast.children[0] as HastElement;\n\n const properties: Properties = {\n ...hastPre.properties,\n dataFileName: meta.fileName,\n };\n if (options.includeDataAttributes) {\n if (options.includeDataAttributes.includes(\"language\")) {\n properties.dataLanguage = lang;\n }\n if (options.includeDataAttributes.includes(\"source-code\")) {\n properties.dataSourceCode = sourceCode;\n }\n if (options.includeDataAttributes.includes(\"line-count\")) {\n properties.dataLineCount = lineCount;\n }\n }\n\n parent.children.splice(index, 1, {\n ...hastPre,\n properties,\n });\n } catch (error) {\n if (error instanceof ShikiError) {\n if (\n error.message.includes(\n \"Language `math` is not included in this bundle\",\n )\n ) {\n // Skip if the block is math block.\n return;\n }\n }\n throw error;\n }\n }\n\n visit(\n tree,\n \"element\",\n (node: HastElement, index: number, parent: HastElement) => {\n promises.push(visitor(node, index, parent));\n },\n );\n\n await Promise.all(promises);\n\n return tree;\n };\n};\n\nfunction getNodeLanguage(node: HastElement): string | undefined {\n if (!Array.isArray(node.properties.className)) {\n return undefined;\n }\n const prefix = \"language-\";\n const language = node.properties.className.find(\n (name) => typeof name === \"string\" && name.startsWith(prefix),\n ) as string | undefined;\n if (typeof language === \"string\") {\n return language.slice(prefix.length);\n }\n return language;\n}\n"],"mappings":"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"}
@@ -1,18 +1,20 @@
1
- import type { Plugin } from "unified";
2
- import type { HastElement, HastNode } from "../../types";
3
- export type MdxTreeProcessor = (node: HastElement, index: number, parent: HastElement) => void | Promise<void>;
1
+ import { HastElement as Element, HastNode as Node } from "../../types.js";
2
+ import { Plugin } from "unified";
3
+
4
+ //#region src/plugins/tree-processor/plugin.d.ts
5
+ type MdxTreeProcessor = (node: Element, index: number, parent: Element) => void | Promise<void>;
4
6
  /**
5
7
  * Options for {@linkcode treeProcessorPlugin}.
6
8
  */
7
9
  type TreeProcessorPluginOptions = {
8
- /** The processor. */
9
- process: MdxTreeProcessor;
10
+ /** The processor. */
11
+ process: MdxTreeProcessor;
10
12
  };
11
13
  /**
12
14
  * A plugin that allows to process MDX tree nodes.
13
15
  * It can be async or sync.
14
16
  */
15
- export declare const treeProcessorPlugin: Plugin<[
16
- TreeProcessorPluginOptions?
17
- ], HastNode, HastNode>;
18
- export {};
17
+ declare const treeProcessorPlugin: Plugin<[TreeProcessorPluginOptions?], Node, Node>;
18
+ //#endregion
19
+ export { MdxTreeProcessor, treeProcessorPlugin };
20
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../../src/plugins/tree-processor/plugin.ts"],"sourcesContent":[],"mappings":";;;;KAKY,gBAAA,UACJ,gCAEE,mBACE;;AAJZ;;KASK,0BAAA,GANK;EACE;EAAO,OAAA,EAOR,gBAPQ;AAAO,CAAA;AAc1B;;;;AAAkC,cAArB,mBAAqB,EAAA,MAAA,CAAA,CAC/B,0BADqC,CAAA,GAEtC,MACA"}
@@ -1,19 +1,22 @@
1
1
  import { visit } from "unist-util-visit";
2
+
3
+ //#region src/plugins/tree-processor/plugin.ts
2
4
  /**
3
- * A plugin that allows to process MDX tree nodes.
4
- * It can be async or sync.
5
- */
6
- export const treeProcessorPlugin = ({ process } = { process: () => { } }) => {
7
- return async (tree) => {
8
- const promises = [];
9
- visit(tree, "element", function visitor(node, index, parent) {
10
- const p = process(node, index, parent);
11
- if (p instanceof Promise ||
12
- Object.prototype.toString.call(p) === "[object Promise]") {
13
- promises.push(p);
14
- }
15
- });
16
- await Promise.allSettled(promises);
17
- return tree;
18
- };
5
+ * A plugin that allows to process MDX tree nodes.
6
+ * It can be async or sync.
7
+ */
8
+ const treeProcessorPlugin = ({ process } = { process: () => {} }) => {
9
+ return async (tree) => {
10
+ const promises = [];
11
+ visit(tree, "element", function visitor(node, index, parent) {
12
+ const p = process(node, index, parent);
13
+ if (p instanceof Promise || Object.prototype.toString.call(p) === "[object Promise]") promises.push(p);
14
+ });
15
+ await Promise.allSettled(promises);
16
+ return tree;
17
+ };
19
18
  };
19
+
20
+ //#endregion
21
+ export { treeProcessorPlugin };
22
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","names":["treeProcessorPlugin: Plugin<\n [TreeProcessorPluginOptions?],\n HastNode,\n HastNode\n>","promises: Promise<void>[]"],"sources":["../../../src/plugins/tree-processor/plugin.ts"],"sourcesContent":["import { visit } from \"unist-util-visit\";\nimport type { Plugin } from \"unified\";\n\nimport type { HastElement, HastNode } from \"../../types\";\n\nexport type MdxTreeProcessor = (\n node: HastElement,\n index: number,\n parent: HastElement,\n) => void | Promise<void>;\n\n/**\n * Options for {@linkcode treeProcessorPlugin}.\n */\ntype TreeProcessorPluginOptions = {\n /** The processor. */\n process: MdxTreeProcessor;\n};\n\n/**\n * A plugin that allows to process MDX tree nodes.\n * It can be async or sync.\n */\nexport const treeProcessorPlugin: Plugin<\n [TreeProcessorPluginOptions?],\n HastNode,\n HastNode\n> = ({ process } = { process: () => {} }) => {\n return async (tree) => {\n const promises: Promise<void>[] = [];\n\n visit(\n tree,\n \"element\",\n function visitor(\n node: HastElement,\n index: number,\n parent: HastElement,\n ): void {\n const p = process(node, index, parent);\n if (\n p instanceof Promise ||\n Object.prototype.toString.call(p) === \"[object Promise]\"\n ) {\n promises.push(p as Promise<void>);\n }\n },\n );\n\n await Promise.allSettled(promises);\n\n return tree;\n };\n};\n"],"mappings":";;;;;;;AAuBA,MAAaA,uBAIR,EAAE,YAAY,EAAE,eAAe,IAAI,KAAK;AAC3C,QAAO,OAAO,SAAS;EACrB,MAAMC,WAA4B,EAAE;AAEpC,QACE,MACA,WACA,SAAS,QACP,MACA,OACA,QACM;GACN,MAAM,IAAI,QAAQ,MAAM,OAAO,OAAO;AACtC,OACE,aAAa,WACb,OAAO,UAAU,SAAS,KAAK,EAAE,KAAK,mBAEtC,UAAS,KAAK,EAAmB;IAGtC;AAED,QAAM,QAAQ,WAAW,SAAS;AAElC,SAAO"}
package/dist/types.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import type { Element, Node } from "hast";
2
- export type { Element as HastElement, Node as HastNode };
1
+ import { Element, Node } from "hast";
2
+ export { type Element as HastElement, type Node as HastNode };
package/dist/types.js CHANGED
@@ -1 +1 @@
1
- export {};
1
+ export { };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@temelj/mdx",
3
3
  "description": "MDX processing utilities.",
4
4
  "license": "MIT",
5
- "version": "0.8.0",
5
+ "version": "0.8.2",
6
6
  "type": "module",
7
7
  "main": "./dist/mod.js",
8
8
  "types": "./dist/mod.d.ts",
@@ -12,6 +12,9 @@
12
12
  "import": "./dist/mod.js"
13
13
  }
14
14
  },
15
+ "files": [
16
+ "dist"
17
+ ],
15
18
  "dependencies": {
16
19
  "@mdx-js/mdx": "^3.1.1",
17
20
  "@shikijs/transformers": "^3.15.0",
@@ -29,16 +32,18 @@
29
32
  "vfile": "^6.0.3",
30
33
  "vfile-matter": "^5.0.1",
31
34
  "zod": "^4.1.12",
32
- "@temelj/iterator": "0.8.0"
35
+ "@temelj/iterator": "0.8.2"
33
36
  },
34
37
  "devDependencies": {
35
38
  "@types/mdast": "^4.0.4",
36
- "@types/node": "24.10.0",
39
+ "@types/node": "24.10.1",
40
+ "tsdown": "^0.16.4",
41
+ "shx": "^0.4.0",
37
42
  "typescript": "^5.9.3",
38
43
  "vitest": "^4.0.8"
39
44
  },
40
45
  "scripts": {
41
- "build": "tsc",
46
+ "build": "tsdown",
42
47
  "test": "vitest run",
43
48
  "typecheck": "tsc --noEmit",
44
49
  "clean": "shx rm -rf dist"
@@ -1,4 +0,0 @@
1
-
2
- > @temelj/mdx@0.8.0 build /home/runner/work/temelj/temelj/packages/mdx
3
- > tsc
4
-
package/compiler.test.ts DELETED
@@ -1,117 +0,0 @@
1
- import { expect, test } from "vitest";
2
- import { z } from "zod";
3
-
4
- import { MdxCompiler } from "./compiler";
5
- import { headingIdPlugin } from "./plugins/heading-id/plugin";
6
- import { syntaxHighlightPlugin } from "./plugins/syntax-highlight/plugin";
7
- import { treeProcessorPlugin } from "./plugins/tree-processor/plugin";
8
-
9
- test("mdx - compile", async () => {
10
- let headingCount = 0;
11
- const compiler = new MdxCompiler()
12
- .withRehypePlugin(headingIdPlugin, {
13
- prefix: "h-",
14
- })
15
- .withRehypePlugin(treeProcessorPlugin, {
16
- process: (element) => {
17
- if (element.tagName.startsWith("h")) {
18
- headingCount++;
19
- }
20
- },
21
- })
22
- .withRehypePlugin(syntaxHighlightPlugin, {
23
- includeDataAttributes: ["language", "source-code", "line-count"],
24
- highlight: {
25
- transformer: { classActiveLine: "hl" },
26
- },
27
- lineNumbers: {},
28
- });
29
-
30
- const frontmatterSchema = z.object({
31
- title: z.string(),
32
- x: z.optional(z.number()),
33
- b: z.optional(z.boolean()).default(true),
34
- });
35
-
36
- const artifact = await compiler.compile(
37
- `
38
- ---
39
- title: Test
40
- x: 42
41
- ---
42
-
43
- # Hello
44
- ## Title 2
45
- ## Duplicate
46
- ## Duplicate
47
-
48
- \`\`\`ts {"highlight":"2", "showLineNumbers":true}
49
- type Mdx = never; // [!code highlight]
50
- const x1 = 1;
51
- \`\`\`
52
- `.trim(),
53
- {},
54
- frontmatterSchema,
55
- );
56
-
57
- expect(headingCount).toBe(4);
58
- expect(artifact.frontmatter.title).toBe("Test");
59
- expect(artifact.frontmatter.x).toBe(42);
60
- expect(artifact.frontmatter.b).toBe(true);
61
-
62
- await expect(() =>
63
- compiler.compile("", {}, frontmatterSchema),
64
- ).rejects.toThrow(z.ZodError);
65
-
66
- const value = artifact.compiled;
67
- expect(typeof value === "string").toBe(true);
68
- expect(value?.includes('"Hello"')).toBe(true);
69
- expect(value?.includes('"h-title-2"')).toBe(true);
70
-
71
- expect(value?.includes('"data-language": "ts"')).toBe(true);
72
- expect(value?.includes('"data-line-count": "2"')).toBe(true);
73
- expect(value?.includes('"data-source-code": "')).toBe(true);
74
-
75
- expect(
76
- value?.includes('"data-line": "1"') && value?.includes('"data-line": "2"'),
77
- ).toBe(true);
78
- expect(value?.includes('className: "line hl line-number"')).toBe(true);
79
- });
80
-
81
- test("mdx - parse frontmatter", async () => {
82
- async function compile<TFrontmatterSchema extends z.ZodSchema>(
83
- frontmatter: Record<string, unknown>,
84
- schema: TFrontmatterSchema,
85
- ): Promise<z.output<TFrontmatterSchema>> {
86
- const compiler = new MdxCompiler();
87
-
88
- const artifact = await compiler.compile(
89
- `
90
- ---
91
- ${JSON.stringify(frontmatter)}
92
- ---
93
- `.trim(),
94
- {},
95
- schema,
96
- );
97
- return artifact.frontmatter;
98
- }
99
-
100
- const fm1 = await compile(
101
- { x: 42 },
102
- z.object({
103
- x: z.number(),
104
- }),
105
- );
106
- const _fm1type: { x: number } = fm1;
107
- expect(fm1).toStrictEqual({ x: 42 });
108
-
109
- const fm2 = await compile(
110
- { s: "abc" },
111
- z.object({
112
- s: z.string(),
113
- }),
114
- );
115
- const _fm2type: { s: string } = fm2;
116
- expect(fm2).toStrictEqual({ s: "abc" });
117
- });