@aigne/doc-smith 0.8.12-beta.8 → 0.8.12
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 +15 -0
- package/agents/publish/index.yaml +4 -0
- package/agents/publish/publish-docs.mjs +77 -5
- package/agents/publish/translate-meta.mjs +103 -0
- package/agents/update/generate-document.yaml +30 -28
- package/agents/update/update-document-detail.yaml +3 -1
- package/agents/utils/update-branding.mjs +69 -0
- package/package.json +16 -2
- package/prompts/common/document/role-and-personality.md +3 -1
- package/prompts/detail/d2-diagram/guide.md +7 -1
- package/prompts/detail/d2-diagram/user-prompt.md +3 -0
- package/prompts/detail/generate/system-prompt.md +6 -7
- package/prompts/detail/generate/user-prompt.md +12 -3
- package/prompts/detail/update/user-prompt.md +0 -2
- package/prompts/structure/update/user-prompt.md +0 -4
- package/utils/file-utils.mjs +69 -24
- package/utils/markdown-checker.mjs +0 -20
- package/utils/request.mjs +7 -0
- package/utils/upload-files.mjs +231 -0
- package/utils/utils.mjs +11 -1
- package/.aigne/doc-smith/config.yaml +0 -77
- package/.aigne/doc-smith/history.yaml +0 -37
- package/.aigne/doc-smith/media-description.yaml +0 -91
- package/.aigne/doc-smith/output/structure-plan.json +0 -162
- package/.aigne/doc-smith/preferences.yml +0 -97
- package/.aigne/doc-smith/upload-cache.yaml +0 -1830
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
- package/.github/workflows/ci.yml +0 -54
- package/.github/workflows/create-release-pr.yaml +0 -21
- package/.github/workflows/publish-docs.yml +0 -65
- package/.github/workflows/release.yml +0 -49
- package/.github/workflows/reviewer.yml +0 -54
- package/.release-please-manifest.json +0 -3
- package/RELEASE.md +0 -9
- package/assets/screenshots/doc-complete-setup.png +0 -0
- package/assets/screenshots/doc-generate-docs.png +0 -0
- package/assets/screenshots/doc-generate.png +0 -0
- package/assets/screenshots/doc-generated-successfully.png +0 -0
- package/assets/screenshots/doc-publish.png +0 -0
- package/assets/screenshots/doc-regenerate.png +0 -0
- package/assets/screenshots/doc-translate-langs.png +0 -0
- package/assets/screenshots/doc-translate.png +0 -0
- package/assets/screenshots/doc-update.png +0 -0
- package/biome.json +0 -73
- package/codecov.yml +0 -15
- package/docs/_sidebar.md +0 -15
- package/docs/configuration-initial-setup.ja.md +0 -179
- package/docs/configuration-initial-setup.md +0 -198
- package/docs/configuration-initial-setup.zh-TW.md +0 -179
- package/docs/configuration-initial-setup.zh.md +0 -179
- package/docs/configuration-managing-preferences.ja.md +0 -100
- package/docs/configuration-managing-preferences.md +0 -100
- package/docs/configuration-managing-preferences.zh-TW.md +0 -100
- package/docs/configuration-managing-preferences.zh.md +0 -100
- package/docs/configuration.ja.md +0 -69
- package/docs/configuration.md +0 -69
- package/docs/configuration.zh-TW.md +0 -69
- package/docs/configuration.zh.md +0 -69
- package/docs/getting-started.ja.md +0 -107
- package/docs/getting-started.md +0 -107
- package/docs/getting-started.zh-TW.md +0 -107
- package/docs/getting-started.zh.md +0 -107
- package/docs/guides-cleaning-up.ja.md +0 -51
- package/docs/guides-cleaning-up.md +0 -52
- package/docs/guides-cleaning-up.zh-TW.md +0 -51
- package/docs/guides-cleaning-up.zh.md +0 -51
- package/docs/guides-evaluating-documents.ja.md +0 -66
- package/docs/guides-evaluating-documents.md +0 -107
- package/docs/guides-evaluating-documents.zh-TW.md +0 -66
- package/docs/guides-evaluating-documents.zh.md +0 -66
- package/docs/guides-generating-documentation.ja.md +0 -151
- package/docs/guides-generating-documentation.md +0 -89
- package/docs/guides-generating-documentation.zh-TW.md +0 -151
- package/docs/guides-generating-documentation.zh.md +0 -151
- package/docs/guides-interactive-chat.ja.md +0 -85
- package/docs/guides-interactive-chat.md +0 -93
- package/docs/guides-interactive-chat.zh-TW.md +0 -85
- package/docs/guides-interactive-chat.zh.md +0 -85
- package/docs/guides-managing-history.ja.md +0 -48
- package/docs/guides-managing-history.md +0 -53
- package/docs/guides-managing-history.zh-TW.md +0 -48
- package/docs/guides-managing-history.zh.md +0 -48
- package/docs/guides-publishing-your-docs.ja.md +0 -78
- package/docs/guides-publishing-your-docs.md +0 -83
- package/docs/guides-publishing-your-docs.zh-TW.md +0 -78
- package/docs/guides-publishing-your-docs.zh.md +0 -78
- package/docs/guides-translating-documentation.ja.md +0 -95
- package/docs/guides-translating-documentation.md +0 -100
- package/docs/guides-translating-documentation.zh-TW.md +0 -95
- package/docs/guides-translating-documentation.zh.md +0 -95
- package/docs/guides-updating-documentation.ja.md +0 -77
- package/docs/guides-updating-documentation.md +0 -79
- package/docs/guides-updating-documentation.zh-TW.md +0 -77
- package/docs/guides-updating-documentation.zh.md +0 -77
- package/docs/guides.ja.md +0 -32
- package/docs/guides.md +0 -32
- package/docs/guides.zh-TW.md +0 -32
- package/docs/guides.zh.md +0 -32
- package/docs/overview.ja.md +0 -61
- package/docs/overview.md +0 -61
- package/docs/overview.zh-TW.md +0 -61
- package/docs/overview.zh.md +0 -61
- package/docs/release-notes.ja.md +0 -255
- package/docs/release-notes.md +0 -288
- package/docs/release-notes.zh-TW.md +0 -255
- package/docs/release-notes.zh.md +0 -255
- package/prompts/common/afs/afs-tools-usage.md +0 -5
- package/prompts/common/afs/use-afs-instruction.md +0 -1
- package/release-please-config.json +0 -14
- package/tests/agents/chat/chat.test.mjs +0 -46
- package/tests/agents/clear/choose-contents.test.mjs +0 -284
- package/tests/agents/clear/clear-auth-tokens.test.mjs +0 -268
- package/tests/agents/clear/clear-document-config.test.mjs +0 -167
- package/tests/agents/clear/clear-document-structure.test.mjs +0 -380
- package/tests/agents/clear/clear-generated-docs.test.mjs +0 -222
- package/tests/agents/evaluate/code-snippet.test.mjs +0 -163
- package/tests/agents/evaluate/fixtures/api-services.md +0 -87
- package/tests/agents/evaluate/fixtures/js-sdk.md +0 -94
- package/tests/agents/evaluate/generate-report.test.mjs +0 -312
- package/tests/agents/generate/check-document-structure.test.mjs +0 -45
- package/tests/agents/generate/check-need-generate-structure.test.mjs +0 -279
- package/tests/agents/generate/document-structure-tools/add-document.test.mjs +0 -449
- package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +0 -410
- package/tests/agents/generate/document-structure-tools/generate-sub-structure.test.mjs +0 -277
- package/tests/agents/generate/document-structure-tools/move-document.test.mjs +0 -476
- package/tests/agents/generate/document-structure-tools/update-document.test.mjs +0 -548
- package/tests/agents/generate/generate-structure.test.mjs +0 -45
- package/tests/agents/generate/user-review-document-structure.test.mjs +0 -319
- package/tests/agents/history/view.test.mjs +0 -97
- package/tests/agents/init/init.test.mjs +0 -1657
- package/tests/agents/prefs/prefs.test.mjs +0 -431
- package/tests/agents/publish/publish-docs.test.mjs +0 -787
- package/tests/agents/translate/choose-language.test.mjs +0 -311
- package/tests/agents/translate/translate-document.test.mjs +0 -51
- package/tests/agents/update/check-document.test.mjs +0 -463
- package/tests/agents/update/check-update-is-single.test.mjs +0 -300
- package/tests/agents/update/document-tools/update-document-content.test.mjs +0 -329
- package/tests/agents/update/generate-document.test.mjs +0 -51
- package/tests/agents/update/save-and-translate-document.test.mjs +0 -369
- package/tests/agents/update/user-review-document.test.mjs +0 -582
- package/tests/agents/utils/action-success.test.mjs +0 -54
- package/tests/agents/utils/check-detail-result.test.mjs +0 -743
- package/tests/agents/utils/check-feedback-refiner.test.mjs +0 -478
- package/tests/agents/utils/choose-docs.test.mjs +0 -406
- package/tests/agents/utils/exit.test.mjs +0 -70
- package/tests/agents/utils/feedback-refiner.test.mjs +0 -51
- package/tests/agents/utils/find-item-by-path.test.mjs +0 -517
- package/tests/agents/utils/find-user-preferences-by-path.test.mjs +0 -382
- package/tests/agents/utils/format-document-structure.test.mjs +0 -364
- package/tests/agents/utils/fs.test.mjs +0 -267
- package/tests/agents/utils/load-sources.test.mjs +0 -1470
- package/tests/agents/utils/save-docs.test.mjs +0 -109
- package/tests/agents/utils/save-output.test.mjs +0 -315
- package/tests/agents/utils/save-single-doc.test.mjs +0 -364
- package/tests/agents/utils/transform-detail-datasources.test.mjs +0 -320
- package/tests/utils/auth-utils.test.mjs +0 -596
- package/tests/utils/blocklet.test.mjs +0 -336
- package/tests/utils/conflict-detector.test.mjs +0 -355
- package/tests/utils/constants.test.mjs +0 -295
- package/tests/utils/d2-utils.test.mjs +0 -437
- package/tests/utils/deploy.test.mjs +0 -399
- package/tests/utils/docs-finder-utils.test.mjs +0 -650
- package/tests/utils/file-utils.test.mjs +0 -521
- package/tests/utils/history-utils.test.mjs +0 -206
- package/tests/utils/kroki-utils.test.mjs +0 -646
- package/tests/utils/linter/fixtures/css/keyword-error.css +0 -1
- package/tests/utils/linter/fixtures/css/missing-semicolon.css +0 -1
- package/tests/utils/linter/fixtures/css/syntax-error.css +0 -1
- package/tests/utils/linter/fixtures/css/undeclare-variable.css +0 -1
- package/tests/utils/linter/fixtures/css/unused-variable.css +0 -2
- package/tests/utils/linter/fixtures/css/valid-code.css +0 -1
- package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +0 -1
- package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +0 -2
- package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +0 -2
- package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +0 -1
- package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +0 -1
- package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +0 -2
- package/tests/utils/linter/fixtures/go/keyword-error.go +0 -5
- package/tests/utils/linter/fixtures/go/missing-semicolon.go +0 -5
- package/tests/utils/linter/fixtures/go/syntax-error.go +0 -6
- package/tests/utils/linter/fixtures/go/undeclare-variable.go +0 -5
- package/tests/utils/linter/fixtures/go/unused-variable.go +0 -5
- package/tests/utils/linter/fixtures/go/valid-code.go +0 -7
- package/tests/utils/linter/fixtures/js/keyword-error.js +0 -3
- package/tests/utils/linter/fixtures/js/missing-semicolon.js +0 -6
- package/tests/utils/linter/fixtures/js/syntax-error.js +0 -4
- package/tests/utils/linter/fixtures/js/undeclare-variable.js +0 -3
- package/tests/utils/linter/fixtures/js/unused-variable.js +0 -7
- package/tests/utils/linter/fixtures/js/valid-code.js +0 -15
- package/tests/utils/linter/fixtures/json/keyword-error.json +0 -1
- package/tests/utils/linter/fixtures/json/missing-semicolon.json +0 -1
- package/tests/utils/linter/fixtures/json/syntax-error.json +0 -1
- package/tests/utils/linter/fixtures/json/undeclare-variable.json +0 -1
- package/tests/utils/linter/fixtures/json/unused-variable.json +0 -1
- package/tests/utils/linter/fixtures/json/valid-code.json +0 -1
- package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +0 -5
- package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +0 -5
- package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +0 -5
- package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +0 -5
- package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +0 -4
- package/tests/utils/linter/fixtures/jsx/valid-code.jsx +0 -5
- package/tests/utils/linter/fixtures/python/keyword-error.py +0 -3
- package/tests/utils/linter/fixtures/python/missing-semicolon.py +0 -2
- package/tests/utils/linter/fixtures/python/syntax-error.py +0 -3
- package/tests/utils/linter/fixtures/python/undeclare-variable.py +0 -3
- package/tests/utils/linter/fixtures/python/unused-variable.py +0 -6
- package/tests/utils/linter/fixtures/python/valid-code.py +0 -12
- package/tests/utils/linter/fixtures/ruby/keyword-error.rb +0 -2
- package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +0 -1
- package/tests/utils/linter/fixtures/ruby/syntax-error.rb +0 -2
- package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +0 -1
- package/tests/utils/linter/fixtures/ruby/unused-variable.rb +0 -2
- package/tests/utils/linter/fixtures/ruby/valid-code.rb +0 -1
- package/tests/utils/linter/fixtures/sass/keyword-error.sass +0 -2
- package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +0 -3
- package/tests/utils/linter/fixtures/sass/syntax-error.sass +0 -3
- package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +0 -2
- package/tests/utils/linter/fixtures/sass/unused-variable.sass +0 -4
- package/tests/utils/linter/fixtures/sass/valid-code.sass +0 -2
- package/tests/utils/linter/fixtures/scss/keyword-error.scss +0 -1
- package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +0 -1
- package/tests/utils/linter/fixtures/scss/syntax-error.scss +0 -1
- package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +0 -1
- package/tests/utils/linter/fixtures/scss/unused-variable.scss +0 -2
- package/tests/utils/linter/fixtures/scss/valid-code.scss +0 -1
- package/tests/utils/linter/fixtures/shell/keyword-error.sh +0 -5
- package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +0 -3
- package/tests/utils/linter/fixtures/shell/syntax-error.sh +0 -4
- package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +0 -3
- package/tests/utils/linter/fixtures/shell/unused-variable.sh +0 -4
- package/tests/utils/linter/fixtures/shell/valid-code.sh +0 -3
- package/tests/utils/linter/fixtures/ts/keyword-error.ts +0 -1
- package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +0 -1
- package/tests/utils/linter/fixtures/ts/syntax-error.ts +0 -1
- package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +0 -1
- package/tests/utils/linter/fixtures/ts/unused-variable.ts +0 -3
- package/tests/utils/linter/fixtures/ts/valid-code.ts +0 -3
- package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +0 -5
- package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +0 -5
- package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +0 -5
- package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +0 -6
- package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +0 -6
- package/tests/utils/linter/fixtures/tsx/valid-code.tsx +0 -5
- package/tests/utils/linter/fixtures/vue/keyword-error.vue +0 -6
- package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +0 -6
- package/tests/utils/linter/fixtures/vue/syntax-error.vue +0 -6
- package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +0 -6
- package/tests/utils/linter/fixtures/vue/unused-variable.vue +0 -7
- package/tests/utils/linter/fixtures/vue/valid-code.vue +0 -6
- package/tests/utils/linter/fixtures/yaml/keyword-error.yml +0 -1
- package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +0 -2
- package/tests/utils/linter/fixtures/yaml/syntax-error.yml +0 -1
- package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +0 -1
- package/tests/utils/linter/fixtures/yaml/unused-variable.yml +0 -2
- package/tests/utils/linter/fixtures/yaml/valid-code.yml +0 -3
- package/tests/utils/linter/index.test.mjs +0 -440
- package/tests/utils/linter/scan-results.mjs +0 -42
- package/tests/utils/load-config.test.mjs +0 -141
- package/tests/utils/markdown/index.test.mjs +0 -478
- package/tests/utils/mermaid-validator.test.mjs +0 -541
- package/tests/utils/mock-chat-model.mjs +0 -12
- package/tests/utils/preferences-utils.test.mjs +0 -465
- package/tests/utils/save-value-to-config.test.mjs +0 -483
- package/tests/utils/utils.test.mjs +0 -941
|
@@ -1,478 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { getMarkdownAst, traverseMarkdownAst } from "../../../utils/markdown/index.mjs";
|
|
3
|
-
|
|
4
|
-
describe("markdown utilities", () => {
|
|
5
|
-
describe("getMarkdownAst", () => {
|
|
6
|
-
test("should parse simple markdown text", () => {
|
|
7
|
-
const markdown = "# Hello World\n\nThis is a paragraph.";
|
|
8
|
-
const ast = getMarkdownAst({ markdown });
|
|
9
|
-
|
|
10
|
-
expect(ast).toBeDefined();
|
|
11
|
-
expect(ast.type).toBe("root");
|
|
12
|
-
expect(ast.children).toHaveLength(2);
|
|
13
|
-
expect(ast.children[0].type).toBe("heading");
|
|
14
|
-
expect(ast.children[0].depth).toBe(1);
|
|
15
|
-
expect(ast.children[1].type).toBe("paragraph");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test("should parse markdown with code blocks", () => {
|
|
19
|
-
const markdown = `
|
|
20
|
-
# Title
|
|
21
|
-
|
|
22
|
-
\`\`\`javascript
|
|
23
|
-
console.log("Hello World");
|
|
24
|
-
\`\`\`
|
|
25
|
-
|
|
26
|
-
Inline \`code\` here.
|
|
27
|
-
`;
|
|
28
|
-
const ast = getMarkdownAst({ markdown });
|
|
29
|
-
|
|
30
|
-
expect(ast).toBeDefined();
|
|
31
|
-
expect(ast.children).toHaveLength(3);
|
|
32
|
-
expect(ast.children[1].type).toBe("code");
|
|
33
|
-
expect(ast.children[1].lang).toBe("javascript");
|
|
34
|
-
expect(ast.children[2].children[1].type).toBe("inlineCode");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("should parse markdown with lists", () => {
|
|
38
|
-
const markdown = `
|
|
39
|
-
# List Example
|
|
40
|
-
|
|
41
|
-
- Item 1
|
|
42
|
-
- Item 2
|
|
43
|
-
- Nested item
|
|
44
|
-
- Item 3
|
|
45
|
-
|
|
46
|
-
1. Ordered item 1
|
|
47
|
-
2. Ordered item 2
|
|
48
|
-
`;
|
|
49
|
-
const ast = getMarkdownAst({ markdown });
|
|
50
|
-
|
|
51
|
-
expect(ast).toBeDefined();
|
|
52
|
-
expect(ast.children).toHaveLength(3);
|
|
53
|
-
expect(ast.children[1].type).toBe("list");
|
|
54
|
-
expect(ast.children[1].ordered).toBe(false);
|
|
55
|
-
expect(ast.children[2].type).toBe("list");
|
|
56
|
-
expect(ast.children[2].ordered).toBe(true);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("should parse markdown with links and images", () => {
|
|
60
|
-
const markdown = `
|
|
61
|
-
# Links and Images
|
|
62
|
-
|
|
63
|
-
[Example Link](https://example.com)
|
|
64
|
-
|
|
65
|
-

|
|
66
|
-
`;
|
|
67
|
-
const ast = getMarkdownAst({ markdown });
|
|
68
|
-
|
|
69
|
-
expect(ast).toBeDefined();
|
|
70
|
-
expect(ast.children[1].children[0].type).toBe("link");
|
|
71
|
-
expect(ast.children[1].children[0].url).toBe("https://example.com");
|
|
72
|
-
expect(ast.children[2].children[0].type).toBe("image");
|
|
73
|
-
expect(ast.children[2].children[0].url).toBe("image.png");
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("should parse GitHub Flavored Markdown features", () => {
|
|
77
|
-
const markdown = `
|
|
78
|
-
# GFM Features
|
|
79
|
-
|
|
80
|
-
| Header 1 | Header 2 |
|
|
81
|
-
|----------|----------|
|
|
82
|
-
| Cell 1 | Cell 2 |
|
|
83
|
-
|
|
84
|
-
~~Strikethrough text~~
|
|
85
|
-
|
|
86
|
-
- [x] Completed task
|
|
87
|
-
- [ ] Incomplete task
|
|
88
|
-
`;
|
|
89
|
-
const ast = getMarkdownAst({ markdown });
|
|
90
|
-
|
|
91
|
-
expect(ast).toBeDefined();
|
|
92
|
-
// Check for table
|
|
93
|
-
const table = ast.children.find((child) => child.type === "table");
|
|
94
|
-
expect(table).toBeDefined();
|
|
95
|
-
expect(table.children).toHaveLength(2); // header + row
|
|
96
|
-
|
|
97
|
-
// Check for strikethrough
|
|
98
|
-
const strikethrough = ast.children.find((child) =>
|
|
99
|
-
child.children?.some((c) => c.type === "delete"),
|
|
100
|
-
);
|
|
101
|
-
expect(strikethrough).toBeDefined();
|
|
102
|
-
|
|
103
|
-
// Check for task list
|
|
104
|
-
const taskList = ast.children.find((child) => child.type === "list");
|
|
105
|
-
expect(taskList).toBeDefined();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("should handle empty markdown", () => {
|
|
109
|
-
const markdown = "";
|
|
110
|
-
expect(() => getMarkdownAst({ markdown })).toThrow(
|
|
111
|
-
"Invalid markdown input: must be a non-empty string",
|
|
112
|
-
);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test("should handle null markdown", () => {
|
|
116
|
-
expect(() => getMarkdownAst({ markdown: null })).toThrow(
|
|
117
|
-
"Invalid markdown input: must be a non-empty string",
|
|
118
|
-
);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test("should handle undefined markdown", () => {
|
|
122
|
-
expect(() => getMarkdownAst({ markdown: undefined })).toThrow(
|
|
123
|
-
"Invalid markdown input: must be a non-empty string",
|
|
124
|
-
);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test("should handle non-string markdown", () => {
|
|
128
|
-
expect(() => getMarkdownAst({ markdown: 123 })).toThrow(
|
|
129
|
-
"Invalid markdown input: must be a non-empty string",
|
|
130
|
-
);
|
|
131
|
-
expect(() => getMarkdownAst({ markdown: {} })).toThrow(
|
|
132
|
-
"Invalid markdown input: must be a non-empty string",
|
|
133
|
-
);
|
|
134
|
-
expect(() => getMarkdownAst({ markdown: [] })).toThrow(
|
|
135
|
-
"Invalid markdown input: must be a non-empty string",
|
|
136
|
-
);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test("should handle markdown with special characters", () => {
|
|
140
|
-
const markdown = `
|
|
141
|
-
# 中文标题
|
|
142
|
-
|
|
143
|
-
This has **bold** and *italic* text.
|
|
144
|
-
|
|
145
|
-
> This is a blockquote with 🚀 emoji.
|
|
146
|
-
|
|
147
|
-
\`\`\`
|
|
148
|
-
Code with special chars: @#$%^&*()
|
|
149
|
-
\`\`\`
|
|
150
|
-
`;
|
|
151
|
-
const ast = getMarkdownAst({ markdown });
|
|
152
|
-
|
|
153
|
-
expect(ast).toBeDefined();
|
|
154
|
-
expect(ast.children).toHaveLength(4);
|
|
155
|
-
expect(ast.children[0].type).toBe("heading");
|
|
156
|
-
expect(ast.children[2].type).toBe("blockquote");
|
|
157
|
-
expect(ast.children[3].type).toBe("code");
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
test("should handle malformed markdown gracefully", () => {
|
|
161
|
-
// This should still parse without throwing
|
|
162
|
-
const markdown = `
|
|
163
|
-
# Incomplete heading [
|
|
164
|
-
|
|
165
|
-
[Incomplete link](
|
|
166
|
-
|
|
167
|
-
\`\`\`incomplete
|
|
168
|
-
code block without closing
|
|
169
|
-
`;
|
|
170
|
-
const ast = getMarkdownAst({ markdown });
|
|
171
|
-
|
|
172
|
-
expect(ast).toBeDefined();
|
|
173
|
-
expect(ast.type).toBe("root");
|
|
174
|
-
expect(ast.children.length).toBeGreaterThan(0);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
test("should handle processor parsing errors", () => {
|
|
178
|
-
// Since we've successfully tested all other paths, we need to test the error handling
|
|
179
|
-
// Let's create a direct test of the catch block logic
|
|
180
|
-
|
|
181
|
-
// Simulate the exact function but with a controlled error
|
|
182
|
-
function testErrorHandling() {
|
|
183
|
-
try {
|
|
184
|
-
// Simulate what happens in the try block when processor.parse fails
|
|
185
|
-
throw new Error("Simulated parser crash");
|
|
186
|
-
} catch (error) {
|
|
187
|
-
// This is the exact code from lines 23-24
|
|
188
|
-
throw new Error(`Failed to parse markdown: ${error.message}`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Test that our error wrapping logic works correctly
|
|
193
|
-
expect(() => testErrorHandling()).toThrow("Failed to parse markdown: Simulated parser crash");
|
|
194
|
-
|
|
195
|
-
// Also test with various error types to ensure the wrapping works for different scenarios
|
|
196
|
-
function testErrorWrapping(originalError) {
|
|
197
|
-
try {
|
|
198
|
-
throw originalError;
|
|
199
|
-
} catch (error) {
|
|
200
|
-
throw new Error(`Failed to parse markdown: ${error.message}`);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
expect(() => testErrorWrapping(new TypeError("Type error"))).toThrow(
|
|
205
|
-
"Failed to parse markdown: Type error",
|
|
206
|
-
);
|
|
207
|
-
expect(() => testErrorWrapping(new ReferenceError("Reference error"))).toThrow(
|
|
208
|
-
"Failed to parse markdown: Reference error",
|
|
209
|
-
);
|
|
210
|
-
expect(() => testErrorWrapping(new Error("Generic error"))).toThrow(
|
|
211
|
-
"Failed to parse markdown: Generic error",
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
// Test edge case where error has no message
|
|
215
|
-
const errorWithoutMessage = new Error();
|
|
216
|
-
errorWithoutMessage.message = "";
|
|
217
|
-
expect(() => testErrorWrapping(errorWithoutMessage)).toThrow("Failed to parse markdown: ");
|
|
218
|
-
|
|
219
|
-
// Test with extremely large markdown (this might actually succeed, which is fine)
|
|
220
|
-
const largeMarkdown = "#".repeat(1000000); // 1MB of hash symbols
|
|
221
|
-
try {
|
|
222
|
-
const result = getMarkdownAst({ markdown: largeMarkdown });
|
|
223
|
-
expect(result).toBeDefined();
|
|
224
|
-
} catch (error) {
|
|
225
|
-
expect(error.message).toMatch(/Failed to parse markdown:|Invalid markdown input:/);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
describe("traverseMarkdownAst", () => {
|
|
231
|
-
test("should traverse AST and visit all nodes", () => {
|
|
232
|
-
const markdown = "# Heading\n\nParagraph with **bold** text.";
|
|
233
|
-
const ast = getMarkdownAst({ markdown });
|
|
234
|
-
|
|
235
|
-
const visitedNodes = [];
|
|
236
|
-
traverseMarkdownAst({
|
|
237
|
-
ast,
|
|
238
|
-
test: () => true, // Visit all nodes
|
|
239
|
-
visitor: (node) => {
|
|
240
|
-
visitedNodes.push(node.type);
|
|
241
|
-
},
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
expect(visitedNodes.length).toBeGreaterThan(0);
|
|
245
|
-
expect(visitedNodes).toContain("heading");
|
|
246
|
-
expect(visitedNodes).toContain("paragraph");
|
|
247
|
-
expect(visitedNodes).toContain("text");
|
|
248
|
-
expect(visitedNodes).toContain("strong");
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
test("should visit only specific node types", () => {
|
|
252
|
-
const markdown = `
|
|
253
|
-
# Heading 1
|
|
254
|
-
## Heading 2
|
|
255
|
-
### Heading 3
|
|
256
|
-
|
|
257
|
-
Paragraph text.
|
|
258
|
-
`;
|
|
259
|
-
const ast = getMarkdownAst({ markdown });
|
|
260
|
-
|
|
261
|
-
const headings = [];
|
|
262
|
-
traverseMarkdownAst({
|
|
263
|
-
ast,
|
|
264
|
-
test: "heading", // Only visit heading nodes
|
|
265
|
-
visitor: (node) => {
|
|
266
|
-
headings.push({ depth: node.depth, text: node.children[0].value });
|
|
267
|
-
},
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
expect(headings).toHaveLength(3);
|
|
271
|
-
expect(headings[0].depth).toBe(1);
|
|
272
|
-
expect(headings[1].depth).toBe(2);
|
|
273
|
-
expect(headings[2].depth).toBe(3);
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
test("should visit nodes based on custom test function", () => {
|
|
277
|
-
const markdown = `
|
|
278
|
-
# Main Title
|
|
279
|
-
|
|
280
|
-
## Section 1
|
|
281
|
-
Content here.
|
|
282
|
-
|
|
283
|
-
## Section 2
|
|
284
|
-
More content.
|
|
285
|
-
|
|
286
|
-
### Subsection
|
|
287
|
-
Even more content.
|
|
288
|
-
`;
|
|
289
|
-
const ast = getMarkdownAst({ markdown });
|
|
290
|
-
|
|
291
|
-
const level2Headings = [];
|
|
292
|
-
traverseMarkdownAst({
|
|
293
|
-
ast,
|
|
294
|
-
test: (node) => node.type === "heading" && node.depth === 2,
|
|
295
|
-
visitor: (node) => {
|
|
296
|
-
level2Headings.push(node.children[0].value);
|
|
297
|
-
},
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
expect(level2Headings).toHaveLength(2);
|
|
301
|
-
expect(level2Headings).toContain("Section 1");
|
|
302
|
-
expect(level2Headings).toContain("Section 2");
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
test("should collect code blocks with language information", () => {
|
|
306
|
-
const markdown = `
|
|
307
|
-
\`\`\`javascript
|
|
308
|
-
console.log("JS code");
|
|
309
|
-
\`\`\`
|
|
310
|
-
|
|
311
|
-
\`\`\`python
|
|
312
|
-
print("Python code")
|
|
313
|
-
\`\`\`
|
|
314
|
-
|
|
315
|
-
\`\`\`
|
|
316
|
-
Plain code block
|
|
317
|
-
\`\`\`
|
|
318
|
-
`;
|
|
319
|
-
const ast = getMarkdownAst({ markdown });
|
|
320
|
-
|
|
321
|
-
const codeBlocks = [];
|
|
322
|
-
traverseMarkdownAst({
|
|
323
|
-
ast,
|
|
324
|
-
test: "code",
|
|
325
|
-
visitor: (node) => {
|
|
326
|
-
codeBlocks.push({
|
|
327
|
-
lang: node.lang || "none",
|
|
328
|
-
value: node.value,
|
|
329
|
-
});
|
|
330
|
-
},
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
expect(codeBlocks).toHaveLength(3);
|
|
334
|
-
expect(codeBlocks[0].lang).toBe("javascript");
|
|
335
|
-
expect(codeBlocks[1].lang).toBe("python");
|
|
336
|
-
expect(codeBlocks[2].lang).toBe("none");
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
test("should extract all links from markdown", () => {
|
|
340
|
-
const markdown = `
|
|
341
|
-
# Links Example
|
|
342
|
-
|
|
343
|
-
[Example](https://example.com)
|
|
344
|
-
[GitHub](https://github.com)
|
|
345
|
-
|
|
346
|
-

|
|
347
|
-
|
|
348
|
-
Reference link: [Google][1]
|
|
349
|
-
|
|
350
|
-
[1]: https://google.com
|
|
351
|
-
`;
|
|
352
|
-
const ast = getMarkdownAst({ markdown });
|
|
353
|
-
|
|
354
|
-
const links = [];
|
|
355
|
-
traverseMarkdownAst({
|
|
356
|
-
ast,
|
|
357
|
-
test: (node) => node.type === "link" || node.type === "linkReference",
|
|
358
|
-
visitor: (node) => {
|
|
359
|
-
if (node.type === "link") {
|
|
360
|
-
links.push({ text: node.children[0].value, url: node.url });
|
|
361
|
-
} else if (node.type === "linkReference") {
|
|
362
|
-
links.push({ text: node.children[0].value, ref: node.identifier });
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
expect(links.length).toBeGreaterThan(0);
|
|
368
|
-
expect(links.some((link) => link.text === "Example")).toBe(true);
|
|
369
|
-
expect(links.some((link) => link.text === "GitHub")).toBe(true);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
test("should handle visitor that modifies nodes", () => {
|
|
373
|
-
const markdown = "# Original Title\n\nSome content.";
|
|
374
|
-
const ast = getMarkdownAst({ markdown });
|
|
375
|
-
|
|
376
|
-
// Modify heading text
|
|
377
|
-
traverseMarkdownAst({
|
|
378
|
-
ast,
|
|
379
|
-
test: "heading",
|
|
380
|
-
visitor: (node) => {
|
|
381
|
-
if (node.children?.[0] && node.children[0].type === "text") {
|
|
382
|
-
node.children[0].value = "Modified Title";
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
// Verify the modification
|
|
388
|
-
const headingNode = ast.children.find((child) => child.type === "heading");
|
|
389
|
-
expect(headingNode.children[0].value).toBe("Modified Title");
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
test("should throw error when ast parameter is missing", () => {
|
|
393
|
-
expect(() => {
|
|
394
|
-
traverseMarkdownAst({
|
|
395
|
-
test: "heading",
|
|
396
|
-
visitor: () => {},
|
|
397
|
-
});
|
|
398
|
-
}).toThrow("Required parameters missing: ast, test, and visitor must be provided");
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
test("should throw error when test parameter is missing", () => {
|
|
402
|
-
const ast = { type: "root", children: [] };
|
|
403
|
-
expect(() => {
|
|
404
|
-
traverseMarkdownAst({
|
|
405
|
-
ast,
|
|
406
|
-
visitor: () => {},
|
|
407
|
-
});
|
|
408
|
-
}).toThrow("Required parameters missing: ast, test, and visitor must be provided");
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
test("should throw error when visitor parameter is missing", () => {
|
|
412
|
-
const ast = { type: "root", children: [] };
|
|
413
|
-
expect(() => {
|
|
414
|
-
traverseMarkdownAst({
|
|
415
|
-
ast,
|
|
416
|
-
test: "heading",
|
|
417
|
-
});
|
|
418
|
-
}).toThrow("Required parameters missing: ast, test, and visitor must be provided");
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
test("should throw error when all parameters are missing", () => {
|
|
422
|
-
expect(() => {
|
|
423
|
-
traverseMarkdownAst({});
|
|
424
|
-
}).toThrow("Required parameters missing: ast, test, and visitor must be provided");
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
test("should handle empty AST", () => {
|
|
428
|
-
const ast = { type: "root", children: [] };
|
|
429
|
-
const visitedNodes = [];
|
|
430
|
-
|
|
431
|
-
traverseMarkdownAst({
|
|
432
|
-
ast,
|
|
433
|
-
test: () => true,
|
|
434
|
-
visitor: (node) => {
|
|
435
|
-
visitedNodes.push(node.type);
|
|
436
|
-
},
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
// Should only visit the root node
|
|
440
|
-
expect(visitedNodes).toEqual(["root"]);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
test("should handle complex nested structures", () => {
|
|
444
|
-
const markdown = `
|
|
445
|
-
# Main Heading
|
|
446
|
-
|
|
447
|
-
1. First item
|
|
448
|
-
- Sub item with **bold** text
|
|
449
|
-
- Another sub item with [link](https://example.com)
|
|
450
|
-
2. Second item
|
|
451
|
-
> Blockquote with *italic* text
|
|
452
|
-
|
|
453
|
-
| Col 1 | Col 2 |
|
|
454
|
-
|-------|-------|
|
|
455
|
-
| Data | More |
|
|
456
|
-
`;
|
|
457
|
-
const ast = getMarkdownAst({ markdown });
|
|
458
|
-
|
|
459
|
-
const nodeTypes = new Set();
|
|
460
|
-
traverseMarkdownAst({
|
|
461
|
-
ast,
|
|
462
|
-
test: () => true,
|
|
463
|
-
visitor: (node) => {
|
|
464
|
-
nodeTypes.add(node.type);
|
|
465
|
-
},
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
expect(nodeTypes.has("heading")).toBe(true);
|
|
469
|
-
expect(nodeTypes.has("list")).toBe(true);
|
|
470
|
-
expect(nodeTypes.has("listItem")).toBe(true);
|
|
471
|
-
expect(nodeTypes.has("blockquote")).toBe(true);
|
|
472
|
-
expect(nodeTypes.has("table")).toBe(true);
|
|
473
|
-
expect(nodeTypes.has("strong")).toBe(true);
|
|
474
|
-
expect(nodeTypes.has("emphasis")).toBe(true);
|
|
475
|
-
expect(nodeTypes.has("link")).toBe(true);
|
|
476
|
-
});
|
|
477
|
-
});
|
|
478
|
-
});
|