@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.
- package/CHANGELOG.md +14 -0
- package/ISSUES.md +1 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/TODO.md +2 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +1625 -0
- package/dist/index.js.map +1 -0
- package/dist/md.d.ts +72 -0
- package/dist/md.js +23508 -0
- package/dist/md.js.map +1 -0
- package/dist/mdToc-NBBxMJ4l.d.ts +12 -0
- package/dist/vite.d.ts +1066 -0
- package/dist/vite.js +20156 -0
- package/dist/vite.js.map +1 -0
- package/package.json +67 -0
- package/packages/md/index.ts +25 -0
- package/packages/md/plugins/component-directives/index.ts +3 -0
- package/packages/md/plugins/component-directives/mdComponentDirective.ts +577 -0
- package/packages/md/plugins/component-directives/types.ts +1 -0
- package/packages/md/plugins/component-directives/utils.ts +27 -0
- package/packages/md/plugins/composer/__fixtures__/1.single-example/input.md +7 -0
- package/packages/md/plugins/composer/__fixtures__/1.single-example/output.json +63 -0
- package/packages/md/plugins/composer/__fixtures__/2.single-example-with-name/input.md +7 -0
- package/packages/md/plugins/composer/__fixtures__/2.single-example-with-name/output.json +63 -0
- package/packages/md/plugins/composer/__fixtures__/3.multiple-examples/input.md +15 -0
- package/packages/md/plugins/composer/__fixtures__/3.multiple-examples/output.json +122 -0
- package/packages/md/plugins/composer/__fixtures__/4.example-groups/input.md +23 -0
- package/packages/md/plugins/composer/__fixtures__/4.example-groups/output.json +184 -0
- package/packages/md/plugins/composer/__tests__/mdComposer.test.ts +41 -0
- package/packages/md/plugins/composer/__tests__/testHelpers.ts +48 -0
- package/packages/md/plugins/composer/index.ts +1 -0
- package/packages/md/plugins/composer/mdComposer.ts +146 -0
- package/packages/md/plugins/developer-writing/index.ts +3 -0
- package/packages/md/plugins/developer-writing/mdCodeRehype.ts +81 -0
- package/packages/md/plugins/functions/__fixtures__/external.ts +4 -0
- package/packages/md/plugins/functions/__fixtures__/test-include.md +31 -0
- package/packages/md/plugins/functions/__fixtures__/test.js +11 -0
- package/packages/md/plugins/functions/__fixtures__/test.py +9 -0
- package/packages/md/plugins/functions/__fixtures__/test.ts +18 -0
- package/packages/md/plugins/functions/__tests__/mdFunctionImportCode.test.ts +314 -0
- package/packages/md/plugins/functions/__tests__/mdFunctionInclude.test.ts +44 -0
- package/packages/md/plugins/functions/__tests__/parseFunctionCall.test.ts +70 -0
- package/packages/md/plugins/functions/__tests__/testHelpers.ts +95 -0
- package/packages/md/plugins/functions/index.ts +15 -0
- package/packages/md/plugins/functions/mdFunctionChangelog.ts +135 -0
- package/packages/md/plugins/functions/mdFunctionImportCode.ts +92 -0
- package/packages/md/plugins/functions/mdFunctionInclude.ts +119 -0
- package/packages/md/plugins/functions/mdFunctionUniform.ts +79 -0
- package/packages/md/plugins/functions/types.ts +9 -0
- package/packages/md/plugins/functions/uniformProcessor.ts +349 -0
- package/packages/md/plugins/functions/utils.ts +457 -0
- package/packages/md/plugins/index.ts +125 -0
- package/packages/md/plugins/mdCode.ts +16 -0
- package/packages/md/plugins/mdHeadingId.ts +47 -0
- package/packages/md/plugins/mdImage.test.ts +59 -0
- package/packages/md/plugins/mdImage.ts +55 -0
- package/packages/md/plugins/mdImageRehype.ts +13 -0
- package/packages/md/plugins/mdPage.ts +35 -0
- package/packages/md/plugins/mdThemeSettings.ts +34 -0
- package/packages/md/plugins/mdToc.ts +229 -0
- package/packages/md/plugins/meta/index.ts +1 -0
- package/packages/md/plugins/meta/mdMeta.ts +198 -0
- package/packages/md/plugins/output-variables/__fixtures__/1.simple/input.md +22 -0
- package/packages/md/plugins/output-variables/__fixtures__/1.simple/output.json +191 -0
- package/packages/md/plugins/output-variables/__fixtures__/2.multiple-vars/input.md +21 -0
- package/packages/md/plugins/output-variables/__fixtures__/2.multiple-vars/output.json +127 -0
- package/packages/md/plugins/output-variables/__tests__/index.test.ts +28 -0
- package/packages/md/plugins/output-variables/__tests__/testHelpers.ts +36 -0
- package/packages/md/plugins/output-variables/index.ts +1 -0
- package/packages/md/plugins/output-variables/lib/const.ts +4 -0
- package/packages/md/plugins/output-variables/lib/factoryAttributes.ts +350 -0
- package/packages/md/plugins/output-variables/lib/factoryLabel.ts +135 -0
- package/packages/md/plugins/output-variables/lib/factoryName.ts +59 -0
- package/packages/md/plugins/output-variables/lib/index.ts +21 -0
- package/packages/md/plugins/output-variables/lib/outputVarsContainer.ts +328 -0
- package/packages/md/plugins/output-variables/lib/util.ts +494 -0
- package/packages/md/plugins/output-variables/remarkOutputVars.ts +22 -0
- package/packages/md/plugins/recmaOverrideComponents.ts +74 -0
- package/packages/md/plugins/rehypeHeading.ts +58 -0
- package/packages/md/plugins/types.ts +15 -0
- package/packages/md/plugins/utils/componentLike.ts +76 -0
- package/packages/md/plugins/utils/index.ts +2 -0
- package/packages/md/plugins/utils/injectCodeMeta.ts +59 -0
- package/packages/md/plugins/utils/mdParameters.test.ts +114 -0
- package/packages/md/plugins/utils/mdParameters.ts +249 -0
- package/packages/md/plugins/utils/mdastTypes.ts +42 -0
- package/packages/md/search/index.ts +257 -0
- package/packages/md/search/types.ts +36 -0
- package/packages/vite/index.ts +20 -0
- package/src/fs.ts +81 -0
- package/src/index.ts +7 -0
- package/src/navigation.ts +147 -0
- package/src/types.ts +8 -0
- package/tsconfig.json +49 -0
- package/tsup.config.ts +32 -0
- 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,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,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,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
|
+
});
|