@xyd-js/content 0.0.0-build

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 (97) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/ISSUES.md +1 -0
  3. package/LICENSE +21 -0
  4. package/README.md +3 -0
  5. package/TODO.md +2 -0
  6. package/dist/index.d.ts +28 -0
  7. package/dist/index.js +1625 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/md.d.ts +72 -0
  10. package/dist/md.js +23508 -0
  11. package/dist/md.js.map +1 -0
  12. package/dist/mdToc-NBBxMJ4l.d.ts +12 -0
  13. package/dist/vite.d.ts +1066 -0
  14. package/dist/vite.js +20156 -0
  15. package/dist/vite.js.map +1 -0
  16. package/package.json +67 -0
  17. package/packages/md/index.ts +25 -0
  18. package/packages/md/plugins/component-directives/index.ts +3 -0
  19. package/packages/md/plugins/component-directives/mdComponentDirective.ts +577 -0
  20. package/packages/md/plugins/component-directives/types.ts +1 -0
  21. package/packages/md/plugins/component-directives/utils.ts +27 -0
  22. package/packages/md/plugins/composer/__fixtures__/1.single-example/input.md +7 -0
  23. package/packages/md/plugins/composer/__fixtures__/1.single-example/output.json +63 -0
  24. package/packages/md/plugins/composer/__fixtures__/2.single-example-with-name/input.md +7 -0
  25. package/packages/md/plugins/composer/__fixtures__/2.single-example-with-name/output.json +63 -0
  26. package/packages/md/plugins/composer/__fixtures__/3.multiple-examples/input.md +15 -0
  27. package/packages/md/plugins/composer/__fixtures__/3.multiple-examples/output.json +122 -0
  28. package/packages/md/plugins/composer/__fixtures__/4.example-groups/input.md +23 -0
  29. package/packages/md/plugins/composer/__fixtures__/4.example-groups/output.json +184 -0
  30. package/packages/md/plugins/composer/__tests__/mdComposer.test.ts +41 -0
  31. package/packages/md/plugins/composer/__tests__/testHelpers.ts +48 -0
  32. package/packages/md/plugins/composer/index.ts +1 -0
  33. package/packages/md/plugins/composer/mdComposer.ts +146 -0
  34. package/packages/md/plugins/developer-writing/index.ts +3 -0
  35. package/packages/md/plugins/developer-writing/mdCodeRehype.ts +81 -0
  36. package/packages/md/plugins/functions/__fixtures__/external.ts +4 -0
  37. package/packages/md/plugins/functions/__fixtures__/test-include.md +31 -0
  38. package/packages/md/plugins/functions/__fixtures__/test.js +11 -0
  39. package/packages/md/plugins/functions/__fixtures__/test.py +9 -0
  40. package/packages/md/plugins/functions/__fixtures__/test.ts +18 -0
  41. package/packages/md/plugins/functions/__tests__/mdFunctionImportCode.test.ts +314 -0
  42. package/packages/md/plugins/functions/__tests__/mdFunctionInclude.test.ts +44 -0
  43. package/packages/md/plugins/functions/__tests__/parseFunctionCall.test.ts +70 -0
  44. package/packages/md/plugins/functions/__tests__/testHelpers.ts +95 -0
  45. package/packages/md/plugins/functions/index.ts +15 -0
  46. package/packages/md/plugins/functions/mdFunctionChangelog.ts +135 -0
  47. package/packages/md/plugins/functions/mdFunctionImportCode.ts +92 -0
  48. package/packages/md/plugins/functions/mdFunctionInclude.ts +119 -0
  49. package/packages/md/plugins/functions/mdFunctionUniform.ts +79 -0
  50. package/packages/md/plugins/functions/types.ts +9 -0
  51. package/packages/md/plugins/functions/uniformProcessor.ts +349 -0
  52. package/packages/md/plugins/functions/utils.ts +457 -0
  53. package/packages/md/plugins/index.ts +125 -0
  54. package/packages/md/plugins/mdCode.ts +16 -0
  55. package/packages/md/plugins/mdHeadingId.ts +47 -0
  56. package/packages/md/plugins/mdImage.test.ts +59 -0
  57. package/packages/md/plugins/mdImage.ts +55 -0
  58. package/packages/md/plugins/mdImageRehype.ts +13 -0
  59. package/packages/md/plugins/mdPage.ts +35 -0
  60. package/packages/md/plugins/mdThemeSettings.ts +34 -0
  61. package/packages/md/plugins/mdToc.ts +229 -0
  62. package/packages/md/plugins/meta/index.ts +1 -0
  63. package/packages/md/plugins/meta/mdMeta.ts +198 -0
  64. package/packages/md/plugins/output-variables/__fixtures__/1.simple/input.md +22 -0
  65. package/packages/md/plugins/output-variables/__fixtures__/1.simple/output.json +191 -0
  66. package/packages/md/plugins/output-variables/__fixtures__/2.multiple-vars/input.md +21 -0
  67. package/packages/md/plugins/output-variables/__fixtures__/2.multiple-vars/output.json +127 -0
  68. package/packages/md/plugins/output-variables/__tests__/index.test.ts +28 -0
  69. package/packages/md/plugins/output-variables/__tests__/testHelpers.ts +36 -0
  70. package/packages/md/plugins/output-variables/index.ts +1 -0
  71. package/packages/md/plugins/output-variables/lib/const.ts +4 -0
  72. package/packages/md/plugins/output-variables/lib/factoryAttributes.ts +350 -0
  73. package/packages/md/plugins/output-variables/lib/factoryLabel.ts +135 -0
  74. package/packages/md/plugins/output-variables/lib/factoryName.ts +59 -0
  75. package/packages/md/plugins/output-variables/lib/index.ts +21 -0
  76. package/packages/md/plugins/output-variables/lib/outputVarsContainer.ts +328 -0
  77. package/packages/md/plugins/output-variables/lib/util.ts +494 -0
  78. package/packages/md/plugins/output-variables/remarkOutputVars.ts +22 -0
  79. package/packages/md/plugins/recmaOverrideComponents.ts +74 -0
  80. package/packages/md/plugins/rehypeHeading.ts +58 -0
  81. package/packages/md/plugins/types.ts +15 -0
  82. package/packages/md/plugins/utils/componentLike.ts +76 -0
  83. package/packages/md/plugins/utils/index.ts +2 -0
  84. package/packages/md/plugins/utils/injectCodeMeta.ts +59 -0
  85. package/packages/md/plugins/utils/mdParameters.test.ts +114 -0
  86. package/packages/md/plugins/utils/mdParameters.ts +249 -0
  87. package/packages/md/plugins/utils/mdastTypes.ts +42 -0
  88. package/packages/md/search/index.ts +257 -0
  89. package/packages/md/search/types.ts +36 -0
  90. package/packages/vite/index.ts +20 -0
  91. package/src/fs.ts +81 -0
  92. package/src/index.ts +7 -0
  93. package/src/navigation.ts +147 -0
  94. package/src/types.ts +8 -0
  95. package/tsconfig.json +49 -0
  96. package/tsup.config.ts +32 -0
  97. package/vitest.config.ts +17 -0
@@ -0,0 +1,146 @@
1
+ import { Plugin } from "unified";
2
+ import { visit } from "unist-util-visit";
3
+ import { Node as UnistNode } from "unist";
4
+ import { highlight } from "codehike/code";
5
+ import type { HighlightedCode, Token } from "codehike/code";
6
+
7
+ import { Settings } from "@xyd-js/core";
8
+
9
+ import { SymbolxVfile } from "../types";
10
+
11
+ type Whitespace = string;
12
+
13
+ // Define types for our examples structure
14
+ export interface ExampleCode {
15
+ language: string;
16
+ title?: string;
17
+ tokens: (Token | Whitespace)[];
18
+ }
19
+
20
+ // TODO: ISSUES IF MULTIPLE OUTPUT VARS - PROBLEMS WITH ASYNC + currentQueue/currentVarName
21
+ // Main plugin function
22
+ export function mdComposer(settings?: Settings): Plugin {
23
+ return () => async (tree: UnistNode, file: SymbolxVfile<any>) => {
24
+ console.time('plugin:mdComposer');
25
+ const varQueue: Record<string, any> = {}
26
+
27
+ // Create a context object to store the current state for each variable
28
+ const contexts: Record<string, {
29
+ queue: (HighlightedCode | any[])[];
30
+ promises: Promise<any>[];
31
+ }> = {}
32
+
33
+ visit(tree, "outputVars", (node: any) => {
34
+ const varName = node.name
35
+ varQueue[varName] = []
36
+ contexts[varName] = {
37
+ queue: varQueue[varName],
38
+ promises: []
39
+ }
40
+
41
+ throughNodes(node, contexts[varName])
42
+ })
43
+
44
+ visit(tree, 'containerDirective', (node: any) => {
45
+ if (node.name !== "code-group") {
46
+ return
47
+ }
48
+
49
+ // Find the most recently created context
50
+ const lastVarName = Object.keys(contexts).pop()
51
+ if (!lastVarName) return
52
+
53
+ const context = contexts[lastVarName]
54
+ const group = [node.attributes.title]
55
+ context.queue.push(group)
56
+
57
+ if (node.data?.hName === "DirectiveCodeGroupFINISH") {
58
+ const codeblocksJSON = node.data.hProperties.codeblocks
59
+
60
+ if (codeblocksJSON) {
61
+ const codeblocks = JSON.parse(codeblocksJSON)
62
+
63
+ if (!codeblocks || !codeblocks.length) {
64
+ return
65
+ }
66
+
67
+ for (const codeblock of codeblocks) {
68
+ group.push(codeblock)
69
+ }
70
+ }
71
+
72
+ return
73
+ }
74
+
75
+ for (const child of node.children) {
76
+ if (child.type !== 'code') {
77
+ continue
78
+ }
79
+
80
+ highlightCode(child, group, context)
81
+ }
82
+ });
83
+
84
+ async function highlightCode(node: any, group?: any[], context?: typeof contexts[string]) {
85
+ const highlighted = await highlight({
86
+ value: node.value,
87
+ lang: node.lang,
88
+ meta: node.meta,
89
+ }, settings?.theme?.coder?.syntaxHighlight || "github-dark")
90
+
91
+ if (group && context) {
92
+ group.push(highlighted)
93
+ return
94
+ }
95
+
96
+ if (context) {
97
+ context.queue.push(highlighted)
98
+ }
99
+ }
100
+
101
+ function nodeCode(node: any, context: typeof contexts[string]) {
102
+ context.promises.push(highlightCode(node, undefined, context))
103
+ }
104
+
105
+ function nodeList(node: any, context: typeof contexts[string]) {
106
+ for (const item of node.children) {
107
+ if (item.type != "listItem") {
108
+ continue
109
+ }
110
+
111
+ throughNodes(item, context)
112
+ }
113
+ }
114
+
115
+ function throughNodes(node: any, context: typeof contexts[string]) {
116
+ for (const child of node.children) {
117
+ switch (child.type) {
118
+ case "code": {
119
+ nodeCode(child, context)
120
+ break
121
+ }
122
+ case "list": {
123
+ nodeList(child, context)
124
+ break
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ // Wait for all promises from all contexts to resolve
131
+ await Promise.all(Object.values(contexts).flatMap(ctx => ctx.promises))
132
+ console.timeEnd('plugin:mdComposer');
133
+
134
+ // Process the results to create the final output structure
135
+ const outputVars = {}
136
+
137
+ for (const [varName, value] of Object.entries(varQueue)) {
138
+ outputVars[varName] = value
139
+ }
140
+
141
+ file.data.outputVars = {
142
+ ...(file.data.outputVars || {}),
143
+ ...outputVars
144
+ }
145
+ };
146
+ }
@@ -0,0 +1,3 @@
1
+ export {
2
+ mdCodeRehype
3
+ } from "./mdCodeRehype"
@@ -0,0 +1,81 @@
1
+ import { visit } from "unist-util-visit";
2
+ import { highlight } from "codehike/code"
3
+
4
+ import { Settings } from "@xyd-js/core";
5
+ import { ContentFS } from "../../../../src";
6
+ import { defaultRemarkPlugins } from "..";
7
+
8
+ export function mdCodeRehype(settings?: Settings) {
9
+ const maxTocDepth = settings?.theme?.writer?.maxTocDepth || 2
10
+ const remarkPlugins = [...defaultRemarkPlugins({
11
+ maxDepth: maxTocDepth,
12
+ }, settings)]
13
+
14
+ const contentFs = new ContentFS(settings || {}, remarkPlugins, [])
15
+
16
+ return function () {
17
+ return async (tree: any) => {
18
+ console.time('plugin:mdServerHighlight');
19
+ const promises: Promise<any>[] = []
20
+
21
+ visit(tree, 'element', (node) => {
22
+ if (node.tagName === 'pre') {
23
+ const code = node.children[0].children[0].value
24
+ const lang = node.children?.[0]?.properties?.className?.[0]?.replace("language-", "")
25
+
26
+ // const regions = node.children?.[0]?.properties?.regions // TODO: in the future
27
+ const lineRanges = (node.children?.[0]?.properties?.lineRanges
28
+ ? JSON.parse(node.children?.[0]?.properties?.lineRanges)
29
+ : []) as { start?: number, end?: number }[]
30
+
31
+ const lineNumbers = node.children?.[0]?.properties?.lineNumbers
32
+ const size = node.children?.[0]?.properties?.size
33
+ const descriptionHead = node.children?.[0]?.properties?.descriptionHead
34
+ const descriptionContent = node.children?.[0]?.properties?.descriptionContent
35
+ const descriptionIcon = node.children?.[0]?.properties?.descriptionIcon
36
+ const meta = node.children?.[0]?.properties?.meta
37
+ const title = node.children?.[0]?.properties?.title
38
+
39
+ const promise = (async () => {
40
+ let descriptionContentCode = ""
41
+
42
+ if (descriptionContent) {
43
+ descriptionContentCode = await contentFs.compileContent(descriptionContent)
44
+ }
45
+
46
+ const highlighted = await highlight({
47
+ value: code,
48
+ lang: lang,
49
+ meta: meta || lang || "",
50
+ }, settings?.theme?.coder?.syntaxHighlight || "github-dark")
51
+
52
+ if (lineRanges && lineRanges.length) {
53
+ highlighted.annotations = lineRanges.map((range) => ({
54
+ name: "mark",
55
+ query: "",
56
+ fromLineNumber: range.start || 0,
57
+ toLineNumber: range.end || 0,
58
+ }))
59
+ }
60
+
61
+ node.properties = {
62
+ ...node.properties,
63
+ title,
64
+ highlighted: JSON.stringify(highlighted),
65
+ lineNumbers,
66
+ size,
67
+ descriptionHead,
68
+ descriptionContent: descriptionContentCode,
69
+ descriptionIcon
70
+ };
71
+ })()
72
+
73
+ promises.push(promise)
74
+ }
75
+ });
76
+
77
+ await Promise.all(promises)
78
+ console.timeEnd('plugin:mdServerHighlight');
79
+ };
80
+ }
81
+ }
@@ -0,0 +1,4 @@
1
+ // This is a mock external file for testing
2
+ export function externalFunction() {
3
+ return "Hello from external";
4
+ }
@@ -0,0 +1,31 @@
1
+ # Included Content
2
+
3
+ This is some content that will be included by the `@include` function.
4
+
5
+ ## Features
6
+
7
+ - **Bold text** for emphasis
8
+ - *Italic text* for subtle emphasis
9
+ - `Code snippets` for technical content
10
+ - [Links](https://example.com) for references
11
+
12
+ ## Code Example
13
+
14
+ ```typescript
15
+ function includedFunction() {
16
+ return "This function is included from another file";
17
+ }
18
+ ```
19
+
20
+ ## Lists
21
+
22
+ 1. First item
23
+ 2. Second item
24
+ 3. Third item
25
+
26
+ - Unordered item
27
+ - Another unordered item
28
+
29
+ :::callout{kind="warning"}
30
+ Hello World
31
+ :::
@@ -0,0 +1,11 @@
1
+ // This is a JavaScript test file
2
+ export function testFunction() {
3
+ return 'Hello, world!';
4
+ }
5
+
6
+ // This is a region for testing
7
+ // #region testRegion
8
+ export function regionFunction() {
9
+ return 'This is from a region';
10
+ }
11
+ // #endregion testRegion
@@ -0,0 +1,9 @@
1
+ # This is a Python test file
2
+ def test_function():
3
+ return "Hello, world!"
4
+
5
+ # This is a region for testing
6
+ # #region testRegion
7
+ def region_function():
8
+ return "This is from a region"
9
+ # #endregion testRegion
@@ -0,0 +1,18 @@
1
+ // This is a test file for the mdFunctionImport plugin
2
+ export function testFunction(): string {
3
+ return 'Hello, world!';
4
+ }
5
+
6
+ // This is a region for testing
7
+ // #region testRegion
8
+ export function regionFunction(): string {
9
+ return 'This is from a region';
10
+ }
11
+ // #endregion testRegion
12
+
13
+ // This is another region for testing
14
+ // #region anotherRegion
15
+ export function anotherRegionFunction(): string {
16
+ return 'This is from another region';
17
+ }
18
+ // #endregion anotherRegion
@@ -0,0 +1,314 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+
4
+ import { describe, it, expect } from 'vitest';
5
+
6
+ import { mdFunctionImportCode } from '../mdFunctionImportCode';
7
+ import { parseImportPath, processContent, detectLanguage } from '../utils';
8
+
9
+ import {
10
+ importCodeMockTree,
11
+ createMockFile,
12
+ createMockFetch,
13
+ restoreFetch,
14
+ importCodeMockTreeAlternative,
15
+ importCodeMdAttributesMockTree
16
+ } from './testHelpers';
17
+
18
+ // Get the absolute path to the fixtures directory
19
+ const fixturesDir = path.resolve(__dirname, '../__fixtures__');
20
+
21
+ describe('mdFunctionImportCode', () => {
22
+ describe('parseImportPath', () => {
23
+ it('should parse a simple file path', () => {
24
+ const result = parseImportPath('path/to/file.ts');
25
+ expect(result).toEqual({
26
+ filePath: 'path/to/file.ts',
27
+ regions: [],
28
+ lineRanges: []
29
+ });
30
+ });
31
+
32
+ it('should parse a file path with regions', () => {
33
+ const result = parseImportPath('path/to/file.ts#region1,region2');
34
+ expect(result).toEqual({
35
+ filePath: 'path/to/file.ts',
36
+ regions: [
37
+ { name: 'region1' },
38
+ { name: 'region2' }
39
+ ],
40
+ lineRanges: []
41
+ });
42
+ });
43
+
44
+
45
+ it('relative parent folder', () => {
46
+ const result = parseImportPath('../relative/parents#Theme');
47
+ expect(result).toEqual({
48
+ filePath: '../relative/parents',
49
+ regions: [
50
+ { name: 'Theme' }
51
+ ],
52
+ lineRanges: []
53
+ });
54
+ });
55
+
56
+ it('should handle curly braces in region names for openapi like', () => {
57
+ const result = parseImportPath('~/api/rest/openapi.yaml#PUT /alerts/{id}');
58
+ expect(result).toEqual({
59
+ filePath: '~/api/rest/openapi.yaml',
60
+ regions: [
61
+ { name: 'PUT /alerts/{id}' }
62
+ ],
63
+ lineRanges: []
64
+ });
65
+ });
66
+
67
+ it('should parse a file path with line ranges', () => {
68
+ const result = parseImportPath('path/to/file.ts{1,2-4,8:,:10}');
69
+ expect(result).toEqual({
70
+ filePath: 'path/to/file.ts',
71
+ regions: [],
72
+ lineRanges: [
73
+ { start: 1, end: 1 },
74
+ { start: 2, end: 4 },
75
+ { start: 8 },
76
+ { end: 10 }
77
+ ]
78
+ });
79
+ });
80
+
81
+ it('should parse a file path with both regions and line ranges', () => {
82
+ const result = parseImportPath('path/to/file.ts#region1{1-5}');
83
+ expect(result).toEqual({
84
+ filePath: 'path/to/file.ts',
85
+ regions: [
86
+ { name: 'region1' }
87
+ ],
88
+ lineRanges: [
89
+ { start: 1, end: 5 }
90
+ ]
91
+ });
92
+ });
93
+ });
94
+
95
+ describe('processContent', () => {
96
+ const sampleContent = `line1
97
+ // #region test
98
+ line2
99
+ line3
100
+ // #endregion test
101
+ line4
102
+ line5
103
+ // #region another
104
+ line6
105
+ // #endregion another
106
+ line7
107
+ line8
108
+ line9
109
+ `;
110
+
111
+ it('should return the original content when no regions or line ranges are specified', () => {
112
+ const result = processContent(sampleContent, [], []);
113
+ expect(result).toBe(sampleContent);
114
+ });
115
+
116
+ it('should extract content from specified regions', () => {
117
+ const result = processContent(sampleContent, [{ name: 'test' }], []);
118
+ expect(result).toBe('line2\nline3');
119
+ });
120
+
121
+ it('should extract content from multiple regions', () => {
122
+ const result = processContent(sampleContent, [{ name: 'test' }, { name: 'another' }], []);
123
+ expect(result).toBe('line2\nline3\nline6');
124
+ });
125
+
126
+ it('should extract content based on line ranges', () => {
127
+ const result = processContent(sampleContent, [], [{ start: 3, end: 4 }]);
128
+ expect(result).toBe('line2\nline3');
129
+ });
130
+
131
+ it('should handle multiple line ranges', () => {
132
+ const result = processContent(sampleContent, [], [
133
+ { start: 1, end: 1 },
134
+ { start: 7, end: 7 }
135
+ ]);
136
+ expect(result).toBe('line1\nline5');
137
+ });
138
+
139
+ it('should handle open-ended ranges', () => {
140
+ const result = processContent(sampleContent, [], [{ start: 11 }]);
141
+ expect(result).toBe('line7\nline8\nline9\n');
142
+ });
143
+
144
+ it('should handle ranges from start', () => {
145
+ const result = processContent(sampleContent, [], [{ end: 1 }]);
146
+ expect(result).toBe('line1');
147
+ });
148
+ });
149
+
150
+ describe('detectLanguage', () => {
151
+ it('should detect JavaScript language', () => {
152
+ expect(detectLanguage('file.js')).toBe('javascript');
153
+ });
154
+
155
+ it('should detect TypeScript language', () => {
156
+ expect(detectLanguage('file.ts')).toBe('typescript');
157
+ });
158
+
159
+ it('should detect Python language', () => {
160
+ expect(detectLanguage('file.py')).toBe('python');
161
+ });
162
+
163
+ it('should return empty string for unknown extension', () => {
164
+ expect(detectLanguage('file.xyz')).toBe('xyz');
165
+ });
166
+
167
+ it('should handle uppercase extensions', () => {
168
+ expect(detectLanguage('file.TS')).toBe('typescript');
169
+ });
170
+ });
171
+
172
+ describe('function call', () => {
173
+ it('should call @importCode statements to code blocks', async () => {
174
+ const fnCall = mdFunctionImportCode()();
175
+ const tree = importCodeMockTree('test.ts');
176
+ const file = createMockFile();
177
+
178
+ await fnCall(tree, file);
179
+
180
+ expect(tree.children[0].type).toBe('code');
181
+ expect((tree.children[0] as any).lang).toBe('typescript');
182
+ expect((tree.children[0] as any).value).toContain('export function testFunction');
183
+ });
184
+
185
+ it('should handle non-existent files gracefully', async () => {
186
+ const fnCall = mdFunctionImportCode()();
187
+ const tree = importCodeMockTree('nonexistent.ts');
188
+ const file = createMockFile();
189
+
190
+ // Mock console.error to prevent test output pollution
191
+ const originalConsoleError = console.error;
192
+ console.error = () => {
193
+ };
194
+
195
+ await fnCall(tree, file);
196
+
197
+ // Restore console.error
198
+ console.error = originalConsoleError;
199
+
200
+ // The node should remain unchanged
201
+ expect(tree.children[0].type).toBe('paragraph');
202
+ });
203
+
204
+ it('should handle custom resolveFrom option', async () => {
205
+ const customFixturesDir = path.resolve(__dirname, '../__fixtures__');
206
+ const fnCall = mdFunctionImportCode()({ resolveFrom: customFixturesDir });
207
+ const tree = importCodeMockTree('test.ts');
208
+ const file = createMockFile('/some/other/directory');
209
+
210
+ await fnCall(tree, file);
211
+
212
+ expect(tree.children[0].type).toBe('code');
213
+ expect((tree.children[0] as any).lang).toBe('typescript');
214
+ expect((tree.children[0] as any).value).toContain('export function testFunction');
215
+ });
216
+
217
+ it('should handle complex import paths with regions and line ranges', async () => {
218
+ const fnCall = mdFunctionImportCode()()
219
+ const tree = importCodeMockTree('test.ts#testRegion{1-3}');
220
+ const file = createMockFile();
221
+
222
+ await fnCall(tree, file);
223
+
224
+ expect(tree.children[0].type).toBe('code');
225
+ expect((tree.children[0] as any).lang).toBe('typescript');
226
+ expect((tree.children[0] as any).value).toContain('export function regionFunction');
227
+ });
228
+
229
+ it('should handle import paths with only regions', async () => {
230
+ const fnCall = mdFunctionImportCode()()
231
+ const tree = importCodeMockTree('test.ts#testRegion');
232
+ const file = createMockFile();
233
+
234
+ await fnCall(tree, file);
235
+
236
+ expect(tree.children[0].type).toBe('code');
237
+ expect((tree.children[0] as any).lang).toBe('typescript');
238
+ expect((tree.children[0] as any).value).toContain('export function regionFunction');
239
+ });
240
+
241
+ it('should handle import paths with only line ranges', async () => {
242
+ const fnCall = mdFunctionImportCode()()
243
+ const tree = importCodeMockTree('test.ts{1-3}');
244
+ const file = createMockFile();
245
+
246
+ await fnCall(tree, file);
247
+
248
+ expect(tree.children[0].type).toBe('code');
249
+ expect((tree.children[0] as any).lang).toBe('typescript');
250
+ expect((tree.children[0] as any).value).toContain('export function testFunction');
251
+ });
252
+
253
+ it('should handle external URLs', async () => {
254
+ // Create mock fetch with our fixture content
255
+ const externalUrl = 'https://example.com/test.ts';
256
+ const fixtureContent = fs.readFileSync(path.resolve(fixturesDir, 'external.ts'), 'utf8');
257
+ const originalFetch = createMockFetch({
258
+ [externalUrl]: fixtureContent
259
+ });
260
+
261
+ const fnCall = mdFunctionImportCode()()
262
+ const tree = importCodeMockTree(externalUrl);
263
+ const file = createMockFile();
264
+
265
+ await fnCall(tree, file);
266
+
267
+ // Restore original fetch
268
+ restoreFetch(originalFetch);
269
+
270
+ expect(tree.children[0].type).toBe('code');
271
+ expect((tree.children[0] as any).lang).toBe('typescript');
272
+ expect((tree.children[0] as any).value).toContain('export function externalFunction');
273
+ });
274
+
275
+ it('should debug visit function', async () => {
276
+ const fnCall = mdFunctionImportCode()()
277
+ const tree = importCodeMockTree('test.ts');
278
+
279
+ const file = createMockFile();
280
+
281
+ // Create a promise that resolves when the transformer is done
282
+ await fnCall(tree, file);
283
+
284
+ expect(tree.children[0].type).toBe('code');
285
+ });
286
+
287
+ it('should handle alternative syntax like @importCode("test.ts")', async () => {
288
+ const fnCall = mdFunctionImportCode()()
289
+ const tree = importCodeMockTreeAlternative('test.ts');
290
+ const file = createMockFile();
291
+
292
+ await fnCall(tree, file);
293
+
294
+ expect(tree.children[0].type).toBe('code');
295
+ expect((tree.children[0] as any).lang).toBe('typescript');
296
+ expect((tree.children[0] as any).value).toContain('export function testFunction');
297
+ });
298
+
299
+ it('should handle md attributes', async () => {
300
+ const fnCall = mdFunctionImportCode()()
301
+ const tree = importCodeMdAttributesMockTree('test.ts', {
302
+ descHead: 'Tip',
303
+ desc: 'You can use md attributes!'
304
+ });
305
+ const file = createMockFile();
306
+
307
+ await fnCall(tree, file);
308
+
309
+ expect(tree.children[0].type).toBe('code');
310
+ expect((tree.children[0] as any).lang).toBe('typescript');
311
+ expect((tree.children[0] as any).value).toContain('export function testFunction');
312
+ });
313
+ });
314
+ });
@@ -0,0 +1,44 @@
1
+ import path from 'node:path';
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+
5
+ import {mdFunctionInclude} from '../mdFunctionInclude';
6
+
7
+ import {createMockFile} from './testHelpers';
8
+
9
+ describe('mdFunctionInclude', () => {
10
+ it('should transform @include statements to include markdown content', async () => {
11
+ const transformer = mdFunctionInclude()();
12
+ const tree = {
13
+ type: 'root',
14
+ children: [
15
+ {
16
+ type: 'paragraph',
17
+ children: [
18
+ {
19
+ type: 'text',
20
+ value: '@include "test-include.md"'
21
+ }
22
+ ]
23
+ }
24
+ ]
25
+ };
26
+ const file = createMockFile();
27
+
28
+ await transformer(tree, file);
29
+
30
+ expect(tree.children.map(c => c.type)).toEqual([
31
+ "heading",
32
+ "paragraph",
33
+ "heading",
34
+ "list",
35
+ "heading",
36
+ "code",
37
+ "heading",
38
+ "list",
39
+ "list",
40
+ "mdxJsxFlowElement"
41
+ ]
42
+ );
43
+ });
44
+ });