@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.
Files changed (264) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/agents/publish/index.yaml +4 -0
  3. package/agents/publish/publish-docs.mjs +77 -5
  4. package/agents/publish/translate-meta.mjs +103 -0
  5. package/agents/update/generate-document.yaml +30 -28
  6. package/agents/update/update-document-detail.yaml +3 -1
  7. package/agents/utils/update-branding.mjs +69 -0
  8. package/package.json +16 -2
  9. package/prompts/common/document/role-and-personality.md +3 -1
  10. package/prompts/detail/d2-diagram/guide.md +7 -1
  11. package/prompts/detail/d2-diagram/user-prompt.md +3 -0
  12. package/prompts/detail/generate/system-prompt.md +6 -7
  13. package/prompts/detail/generate/user-prompt.md +12 -3
  14. package/prompts/detail/update/user-prompt.md +0 -2
  15. package/prompts/structure/update/user-prompt.md +0 -4
  16. package/utils/file-utils.mjs +69 -24
  17. package/utils/markdown-checker.mjs +0 -20
  18. package/utils/request.mjs +7 -0
  19. package/utils/upload-files.mjs +231 -0
  20. package/utils/utils.mjs +11 -1
  21. package/.aigne/doc-smith/config.yaml +0 -77
  22. package/.aigne/doc-smith/history.yaml +0 -37
  23. package/.aigne/doc-smith/media-description.yaml +0 -91
  24. package/.aigne/doc-smith/output/structure-plan.json +0 -162
  25. package/.aigne/doc-smith/preferences.yml +0 -97
  26. package/.aigne/doc-smith/upload-cache.yaml +0 -1830
  27. package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
  28. package/.github/workflows/ci.yml +0 -54
  29. package/.github/workflows/create-release-pr.yaml +0 -21
  30. package/.github/workflows/publish-docs.yml +0 -65
  31. package/.github/workflows/release.yml +0 -49
  32. package/.github/workflows/reviewer.yml +0 -54
  33. package/.release-please-manifest.json +0 -3
  34. package/RELEASE.md +0 -9
  35. package/assets/screenshots/doc-complete-setup.png +0 -0
  36. package/assets/screenshots/doc-generate-docs.png +0 -0
  37. package/assets/screenshots/doc-generate.png +0 -0
  38. package/assets/screenshots/doc-generated-successfully.png +0 -0
  39. package/assets/screenshots/doc-publish.png +0 -0
  40. package/assets/screenshots/doc-regenerate.png +0 -0
  41. package/assets/screenshots/doc-translate-langs.png +0 -0
  42. package/assets/screenshots/doc-translate.png +0 -0
  43. package/assets/screenshots/doc-update.png +0 -0
  44. package/biome.json +0 -73
  45. package/codecov.yml +0 -15
  46. package/docs/_sidebar.md +0 -15
  47. package/docs/configuration-initial-setup.ja.md +0 -179
  48. package/docs/configuration-initial-setup.md +0 -198
  49. package/docs/configuration-initial-setup.zh-TW.md +0 -179
  50. package/docs/configuration-initial-setup.zh.md +0 -179
  51. package/docs/configuration-managing-preferences.ja.md +0 -100
  52. package/docs/configuration-managing-preferences.md +0 -100
  53. package/docs/configuration-managing-preferences.zh-TW.md +0 -100
  54. package/docs/configuration-managing-preferences.zh.md +0 -100
  55. package/docs/configuration.ja.md +0 -69
  56. package/docs/configuration.md +0 -69
  57. package/docs/configuration.zh-TW.md +0 -69
  58. package/docs/configuration.zh.md +0 -69
  59. package/docs/getting-started.ja.md +0 -107
  60. package/docs/getting-started.md +0 -107
  61. package/docs/getting-started.zh-TW.md +0 -107
  62. package/docs/getting-started.zh.md +0 -107
  63. package/docs/guides-cleaning-up.ja.md +0 -51
  64. package/docs/guides-cleaning-up.md +0 -52
  65. package/docs/guides-cleaning-up.zh-TW.md +0 -51
  66. package/docs/guides-cleaning-up.zh.md +0 -51
  67. package/docs/guides-evaluating-documents.ja.md +0 -66
  68. package/docs/guides-evaluating-documents.md +0 -107
  69. package/docs/guides-evaluating-documents.zh-TW.md +0 -66
  70. package/docs/guides-evaluating-documents.zh.md +0 -66
  71. package/docs/guides-generating-documentation.ja.md +0 -151
  72. package/docs/guides-generating-documentation.md +0 -89
  73. package/docs/guides-generating-documentation.zh-TW.md +0 -151
  74. package/docs/guides-generating-documentation.zh.md +0 -151
  75. package/docs/guides-interactive-chat.ja.md +0 -85
  76. package/docs/guides-interactive-chat.md +0 -93
  77. package/docs/guides-interactive-chat.zh-TW.md +0 -85
  78. package/docs/guides-interactive-chat.zh.md +0 -85
  79. package/docs/guides-managing-history.ja.md +0 -48
  80. package/docs/guides-managing-history.md +0 -53
  81. package/docs/guides-managing-history.zh-TW.md +0 -48
  82. package/docs/guides-managing-history.zh.md +0 -48
  83. package/docs/guides-publishing-your-docs.ja.md +0 -78
  84. package/docs/guides-publishing-your-docs.md +0 -83
  85. package/docs/guides-publishing-your-docs.zh-TW.md +0 -78
  86. package/docs/guides-publishing-your-docs.zh.md +0 -78
  87. package/docs/guides-translating-documentation.ja.md +0 -95
  88. package/docs/guides-translating-documentation.md +0 -100
  89. package/docs/guides-translating-documentation.zh-TW.md +0 -95
  90. package/docs/guides-translating-documentation.zh.md +0 -95
  91. package/docs/guides-updating-documentation.ja.md +0 -77
  92. package/docs/guides-updating-documentation.md +0 -79
  93. package/docs/guides-updating-documentation.zh-TW.md +0 -77
  94. package/docs/guides-updating-documentation.zh.md +0 -77
  95. package/docs/guides.ja.md +0 -32
  96. package/docs/guides.md +0 -32
  97. package/docs/guides.zh-TW.md +0 -32
  98. package/docs/guides.zh.md +0 -32
  99. package/docs/overview.ja.md +0 -61
  100. package/docs/overview.md +0 -61
  101. package/docs/overview.zh-TW.md +0 -61
  102. package/docs/overview.zh.md +0 -61
  103. package/docs/release-notes.ja.md +0 -255
  104. package/docs/release-notes.md +0 -288
  105. package/docs/release-notes.zh-TW.md +0 -255
  106. package/docs/release-notes.zh.md +0 -255
  107. package/prompts/common/afs/afs-tools-usage.md +0 -5
  108. package/prompts/common/afs/use-afs-instruction.md +0 -1
  109. package/release-please-config.json +0 -14
  110. package/tests/agents/chat/chat.test.mjs +0 -46
  111. package/tests/agents/clear/choose-contents.test.mjs +0 -284
  112. package/tests/agents/clear/clear-auth-tokens.test.mjs +0 -268
  113. package/tests/agents/clear/clear-document-config.test.mjs +0 -167
  114. package/tests/agents/clear/clear-document-structure.test.mjs +0 -380
  115. package/tests/agents/clear/clear-generated-docs.test.mjs +0 -222
  116. package/tests/agents/evaluate/code-snippet.test.mjs +0 -163
  117. package/tests/agents/evaluate/fixtures/api-services.md +0 -87
  118. package/tests/agents/evaluate/fixtures/js-sdk.md +0 -94
  119. package/tests/agents/evaluate/generate-report.test.mjs +0 -312
  120. package/tests/agents/generate/check-document-structure.test.mjs +0 -45
  121. package/tests/agents/generate/check-need-generate-structure.test.mjs +0 -279
  122. package/tests/agents/generate/document-structure-tools/add-document.test.mjs +0 -449
  123. package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +0 -410
  124. package/tests/agents/generate/document-structure-tools/generate-sub-structure.test.mjs +0 -277
  125. package/tests/agents/generate/document-structure-tools/move-document.test.mjs +0 -476
  126. package/tests/agents/generate/document-structure-tools/update-document.test.mjs +0 -548
  127. package/tests/agents/generate/generate-structure.test.mjs +0 -45
  128. package/tests/agents/generate/user-review-document-structure.test.mjs +0 -319
  129. package/tests/agents/history/view.test.mjs +0 -97
  130. package/tests/agents/init/init.test.mjs +0 -1657
  131. package/tests/agents/prefs/prefs.test.mjs +0 -431
  132. package/tests/agents/publish/publish-docs.test.mjs +0 -787
  133. package/tests/agents/translate/choose-language.test.mjs +0 -311
  134. package/tests/agents/translate/translate-document.test.mjs +0 -51
  135. package/tests/agents/update/check-document.test.mjs +0 -463
  136. package/tests/agents/update/check-update-is-single.test.mjs +0 -300
  137. package/tests/agents/update/document-tools/update-document-content.test.mjs +0 -329
  138. package/tests/agents/update/generate-document.test.mjs +0 -51
  139. package/tests/agents/update/save-and-translate-document.test.mjs +0 -369
  140. package/tests/agents/update/user-review-document.test.mjs +0 -582
  141. package/tests/agents/utils/action-success.test.mjs +0 -54
  142. package/tests/agents/utils/check-detail-result.test.mjs +0 -743
  143. package/tests/agents/utils/check-feedback-refiner.test.mjs +0 -478
  144. package/tests/agents/utils/choose-docs.test.mjs +0 -406
  145. package/tests/agents/utils/exit.test.mjs +0 -70
  146. package/tests/agents/utils/feedback-refiner.test.mjs +0 -51
  147. package/tests/agents/utils/find-item-by-path.test.mjs +0 -517
  148. package/tests/agents/utils/find-user-preferences-by-path.test.mjs +0 -382
  149. package/tests/agents/utils/format-document-structure.test.mjs +0 -364
  150. package/tests/agents/utils/fs.test.mjs +0 -267
  151. package/tests/agents/utils/load-sources.test.mjs +0 -1470
  152. package/tests/agents/utils/save-docs.test.mjs +0 -109
  153. package/tests/agents/utils/save-output.test.mjs +0 -315
  154. package/tests/agents/utils/save-single-doc.test.mjs +0 -364
  155. package/tests/agents/utils/transform-detail-datasources.test.mjs +0 -320
  156. package/tests/utils/auth-utils.test.mjs +0 -596
  157. package/tests/utils/blocklet.test.mjs +0 -336
  158. package/tests/utils/conflict-detector.test.mjs +0 -355
  159. package/tests/utils/constants.test.mjs +0 -295
  160. package/tests/utils/d2-utils.test.mjs +0 -437
  161. package/tests/utils/deploy.test.mjs +0 -399
  162. package/tests/utils/docs-finder-utils.test.mjs +0 -650
  163. package/tests/utils/file-utils.test.mjs +0 -521
  164. package/tests/utils/history-utils.test.mjs +0 -206
  165. package/tests/utils/kroki-utils.test.mjs +0 -646
  166. package/tests/utils/linter/fixtures/css/keyword-error.css +0 -1
  167. package/tests/utils/linter/fixtures/css/missing-semicolon.css +0 -1
  168. package/tests/utils/linter/fixtures/css/syntax-error.css +0 -1
  169. package/tests/utils/linter/fixtures/css/undeclare-variable.css +0 -1
  170. package/tests/utils/linter/fixtures/css/unused-variable.css +0 -2
  171. package/tests/utils/linter/fixtures/css/valid-code.css +0 -1
  172. package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +0 -1
  173. package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +0 -2
  174. package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +0 -2
  175. package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +0 -1
  176. package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +0 -1
  177. package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +0 -2
  178. package/tests/utils/linter/fixtures/go/keyword-error.go +0 -5
  179. package/tests/utils/linter/fixtures/go/missing-semicolon.go +0 -5
  180. package/tests/utils/linter/fixtures/go/syntax-error.go +0 -6
  181. package/tests/utils/linter/fixtures/go/undeclare-variable.go +0 -5
  182. package/tests/utils/linter/fixtures/go/unused-variable.go +0 -5
  183. package/tests/utils/linter/fixtures/go/valid-code.go +0 -7
  184. package/tests/utils/linter/fixtures/js/keyword-error.js +0 -3
  185. package/tests/utils/linter/fixtures/js/missing-semicolon.js +0 -6
  186. package/tests/utils/linter/fixtures/js/syntax-error.js +0 -4
  187. package/tests/utils/linter/fixtures/js/undeclare-variable.js +0 -3
  188. package/tests/utils/linter/fixtures/js/unused-variable.js +0 -7
  189. package/tests/utils/linter/fixtures/js/valid-code.js +0 -15
  190. package/tests/utils/linter/fixtures/json/keyword-error.json +0 -1
  191. package/tests/utils/linter/fixtures/json/missing-semicolon.json +0 -1
  192. package/tests/utils/linter/fixtures/json/syntax-error.json +0 -1
  193. package/tests/utils/linter/fixtures/json/undeclare-variable.json +0 -1
  194. package/tests/utils/linter/fixtures/json/unused-variable.json +0 -1
  195. package/tests/utils/linter/fixtures/json/valid-code.json +0 -1
  196. package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +0 -5
  197. package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +0 -5
  198. package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +0 -5
  199. package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +0 -5
  200. package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +0 -4
  201. package/tests/utils/linter/fixtures/jsx/valid-code.jsx +0 -5
  202. package/tests/utils/linter/fixtures/python/keyword-error.py +0 -3
  203. package/tests/utils/linter/fixtures/python/missing-semicolon.py +0 -2
  204. package/tests/utils/linter/fixtures/python/syntax-error.py +0 -3
  205. package/tests/utils/linter/fixtures/python/undeclare-variable.py +0 -3
  206. package/tests/utils/linter/fixtures/python/unused-variable.py +0 -6
  207. package/tests/utils/linter/fixtures/python/valid-code.py +0 -12
  208. package/tests/utils/linter/fixtures/ruby/keyword-error.rb +0 -2
  209. package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +0 -1
  210. package/tests/utils/linter/fixtures/ruby/syntax-error.rb +0 -2
  211. package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +0 -1
  212. package/tests/utils/linter/fixtures/ruby/unused-variable.rb +0 -2
  213. package/tests/utils/linter/fixtures/ruby/valid-code.rb +0 -1
  214. package/tests/utils/linter/fixtures/sass/keyword-error.sass +0 -2
  215. package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +0 -3
  216. package/tests/utils/linter/fixtures/sass/syntax-error.sass +0 -3
  217. package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +0 -2
  218. package/tests/utils/linter/fixtures/sass/unused-variable.sass +0 -4
  219. package/tests/utils/linter/fixtures/sass/valid-code.sass +0 -2
  220. package/tests/utils/linter/fixtures/scss/keyword-error.scss +0 -1
  221. package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +0 -1
  222. package/tests/utils/linter/fixtures/scss/syntax-error.scss +0 -1
  223. package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +0 -1
  224. package/tests/utils/linter/fixtures/scss/unused-variable.scss +0 -2
  225. package/tests/utils/linter/fixtures/scss/valid-code.scss +0 -1
  226. package/tests/utils/linter/fixtures/shell/keyword-error.sh +0 -5
  227. package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +0 -3
  228. package/tests/utils/linter/fixtures/shell/syntax-error.sh +0 -4
  229. package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +0 -3
  230. package/tests/utils/linter/fixtures/shell/unused-variable.sh +0 -4
  231. package/tests/utils/linter/fixtures/shell/valid-code.sh +0 -3
  232. package/tests/utils/linter/fixtures/ts/keyword-error.ts +0 -1
  233. package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +0 -1
  234. package/tests/utils/linter/fixtures/ts/syntax-error.ts +0 -1
  235. package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +0 -1
  236. package/tests/utils/linter/fixtures/ts/unused-variable.ts +0 -3
  237. package/tests/utils/linter/fixtures/ts/valid-code.ts +0 -3
  238. package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +0 -5
  239. package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +0 -5
  240. package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +0 -5
  241. package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +0 -6
  242. package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +0 -6
  243. package/tests/utils/linter/fixtures/tsx/valid-code.tsx +0 -5
  244. package/tests/utils/linter/fixtures/vue/keyword-error.vue +0 -6
  245. package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +0 -6
  246. package/tests/utils/linter/fixtures/vue/syntax-error.vue +0 -6
  247. package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +0 -6
  248. package/tests/utils/linter/fixtures/vue/unused-variable.vue +0 -7
  249. package/tests/utils/linter/fixtures/vue/valid-code.vue +0 -6
  250. package/tests/utils/linter/fixtures/yaml/keyword-error.yml +0 -1
  251. package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +0 -2
  252. package/tests/utils/linter/fixtures/yaml/syntax-error.yml +0 -1
  253. package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +0 -1
  254. package/tests/utils/linter/fixtures/yaml/unused-variable.yml +0 -2
  255. package/tests/utils/linter/fixtures/yaml/valid-code.yml +0 -3
  256. package/tests/utils/linter/index.test.mjs +0 -440
  257. package/tests/utils/linter/scan-results.mjs +0 -42
  258. package/tests/utils/load-config.test.mjs +0 -141
  259. package/tests/utils/markdown/index.test.mjs +0 -478
  260. package/tests/utils/mermaid-validator.test.mjs +0 -541
  261. package/tests/utils/mock-chat-model.mjs +0 -12
  262. package/tests/utils/preferences-utils.test.mjs +0 -465
  263. package/tests/utils/save-value-to-config.test.mjs +0 -483
  264. package/tests/utils/utils.test.mjs +0 -941
@@ -1,646 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import { existsSync, mkdtemp, rmdir } from "node:fs";
3
- import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
4
- import { tmpdir } from "node:os";
5
- import path from "node:path";
6
-
7
- import Debug from "debug";
8
-
9
- import { DOC_SMITH_DIR, TMP_ASSETS_DIR, TMP_DIR } from "../../utils/constants/index.mjs";
10
- import {
11
- beforePublishHook,
12
- checkD2Content,
13
- ensureTmpDir,
14
- getChart,
15
- getD2Svg,
16
- saveD2Assets,
17
- } from "../../utils/kroki-utils.mjs";
18
-
19
- describe.skip("kroki-utils", () => {
20
- let tempDir;
21
-
22
- beforeEach(async () => {
23
- tempDir = await new Promise((resolve, reject) => {
24
- mkdtemp(path.join(tmpdir(), "kroki-test-"), (err, dir) => {
25
- if (err) reject(err);
26
- else resolve(dir);
27
- });
28
- });
29
- });
30
-
31
- afterEach(async () => {
32
- if (tempDir && existsSync(tempDir)) {
33
- await new Promise((resolve) => {
34
- rmdir(tempDir, { recursive: true }, () => resolve());
35
- });
36
- }
37
- });
38
-
39
- describe("getChart", () => {
40
- test("should fetch chart with default parameters", async () => {
41
- const content = "A -> B";
42
- const result = await getChart({ content });
43
-
44
- // Should either return SVG content or null (if network fails)
45
- expect(result === null || typeof result === "string").toBe(true);
46
-
47
- if (result !== null) {
48
- // Basic SVG validation
49
- expect(result).toContain("svg");
50
- }
51
- }, 15000);
52
-
53
- test("should handle different chart types", async () => {
54
- const content = "A -> B";
55
- const result = await getChart({
56
- chart: "d2",
57
- format: "svg",
58
- content,
59
- });
60
-
61
- expect(result === null || typeof result === "string").toBe(true);
62
- }, 15000);
63
-
64
- test("should handle different formats", async () => {
65
- const content = "A -> B";
66
- const result = await getChart({
67
- chart: "d2",
68
- format: "png",
69
- content,
70
- });
71
-
72
- expect(result === null || typeof result === "string").toBe(true);
73
- }, 15000);
74
-
75
- test("should handle network errors gracefully when strict=false", async () => {
76
- // Test with invalid base URL by mocking fetch
77
- const originalFetch = global.fetch;
78
- global.fetch = () => Promise.reject(new Error("Network error"));
79
-
80
- try {
81
- const result = await getChart({ content: "A -> B", strict: false });
82
- expect(result).toBe(null);
83
- } finally {
84
- global.fetch = originalFetch;
85
- }
86
- });
87
-
88
- test("should throw error when strict=true and request fails", async () => {
89
- // Mock fetch to return error response
90
- const originalFetch = global.fetch;
91
- global.fetch = () =>
92
- Promise.resolve({
93
- ok: false,
94
- status: 400,
95
- statusText: "Bad Request",
96
- text: () => Promise.resolve("Error response"),
97
- });
98
-
99
- try {
100
- await expect(getChart({ content: "invalid content", strict: true })).rejects.toThrow(
101
- "Failed to fetch chart: 400 Bad Request",
102
- );
103
- } finally {
104
- global.fetch = originalFetch;
105
- }
106
- });
107
-
108
- test("should handle empty content", async () => {
109
- const result = await getChart({ content: "" });
110
- expect(result === null || typeof result === "string").toBe(true);
111
- }, 10000);
112
-
113
- test("should handle malformed content", async () => {
114
- const malformedContent = "A -> B -> C -> [invalid syntax";
115
- const result = await getChart({ content: malformedContent, strict: false });
116
-
117
- // Should not crash, may return error SVG or null
118
- expect(result === null || typeof result === "string").toBe(true);
119
- }, 10000);
120
- });
121
-
122
- describe("getD2Svg", () => {
123
- test("should generate D2 SVG content", async () => {
124
- const content = "A -> B: label";
125
- const result = await getD2Svg({ content });
126
-
127
- expect(result === null || typeof result === "string").toBe(true);
128
-
129
- if (result !== null) {
130
- expect(result).toContain("svg");
131
- }
132
- }, 15000);
133
-
134
- test("should handle strict mode correctly", async () => {
135
- // Mock fetch to simulate server error
136
- const originalFetch = global.fetch;
137
- global.fetch = () =>
138
- Promise.resolve({
139
- ok: false,
140
- status: 500,
141
- statusText: "Internal Server Error",
142
- text: () => Promise.resolve("Error response"),
143
- });
144
-
145
- try {
146
- // Should throw in strict mode
147
- await expect(getD2Svg({ content: "A -> B", strict: true })).rejects.toThrow();
148
-
149
- // Should return error response (not null) in non-strict mode
150
- const result = await getD2Svg({ content: "A -> B", strict: false });
151
- expect(result).toBe("Error response");
152
- } finally {
153
- global.fetch = originalFetch;
154
- }
155
- });
156
- });
157
-
158
- describe("saveD2Assets", () => {
159
- test("should process markdown with D2 code blocks", async () => {
160
- const docsDir = path.join(tempDir, "docs");
161
- await mkdir(docsDir, { recursive: true });
162
-
163
- const markdown = `# Test Document
164
-
165
- This is a test document with D2 diagram:
166
-
167
- \`\`\`d2
168
- A -> B: connection
169
- B -> C: another connection
170
- \`\`\`
171
-
172
- Some more content here.
173
- `;
174
-
175
- const result = await saveD2Assets({ markdown, docsDir });
176
-
177
- expect(typeof result).toBe("string");
178
- expect(result).toContain("# Test Document");
179
- expect(result).toContain("Some more content here");
180
-
181
- // Should replace D2 code block with image reference
182
- expect(result).not.toContain("```d2");
183
- expect(result).toContain("![](../assets/d2/");
184
- expect(result).toContain(".svg)");
185
- });
186
-
187
- test("should handle markdown without D2 blocks", async () => {
188
- const docsDir = path.join(tempDir, "docs");
189
- await mkdir(docsDir, { recursive: true });
190
-
191
- const markdown = `# Test Document
192
-
193
- This document has no D2 diagrams.
194
-
195
- \`\`\`javascript
196
- console.log('hello world');
197
- \`\`\`
198
- `;
199
-
200
- const result = await saveD2Assets({ markdown, docsDir });
201
-
202
- expect(result).toBe(markdown); // Should remain unchanged
203
- });
204
-
205
- test("should handle multiple D2 blocks", async () => {
206
- const docsDir = path.join(tempDir, "docs");
207
- await mkdir(docsDir, { recursive: true });
208
-
209
- const markdown = `# Test
210
-
211
- \`\`\`d2
212
- A -> B
213
- \`\`\`
214
-
215
- Some text.
216
-
217
- \`\`\`d2
218
- C -> D
219
- E -> F
220
- \`\`\`
221
- `;
222
-
223
- const result = await saveD2Assets({ markdown, docsDir });
224
-
225
- expect(typeof result).toBe("string");
226
- expect(result).not.toContain("```d2");
227
-
228
- // Should have two image references
229
- const imageMatches = result.match(/!\[\]\(\.\.\/assets\/d2\/.*\.svg\)/g);
230
- expect(imageMatches).toBeTruthy();
231
- expect(imageMatches.length).toBe(2);
232
- });
233
-
234
- test("should handle empty markdown", async () => {
235
- const docsDir = path.join(tempDir, "docs");
236
- await mkdir(docsDir, { recursive: true });
237
-
238
- const result = await saveD2Assets({ markdown: "", docsDir });
239
- expect(result).toBe("");
240
- });
241
-
242
- test("should skip generation if SVG file already exists", async () => {
243
- const docsDir = path.join(tempDir, "docs");
244
- await mkdir(docsDir, { recursive: true });
245
- const markdown = `\`\`\`d2\nA -> B\n\`\`\``;
246
-
247
- // 1. First run to generate the file
248
- await saveD2Assets({ markdown, docsDir });
249
-
250
- // 2. Second run to check if cache is used
251
- const debugLogs = [];
252
- const originalWrite = process.stderr.write;
253
- process.stderr.write = (chunk) => {
254
- debugLogs.push(chunk.toString());
255
- return true;
256
- };
257
- Debug.enable("doc-smith");
258
-
259
- try {
260
- const result = await saveD2Assets({ markdown, docsDir });
261
-
262
- expect(typeof result).toBe("string");
263
- expect(result).toContain(`![](${path.posix.join("..", TMP_ASSETS_DIR, "d2")}`);
264
- } finally {
265
- process.stderr.write = originalWrite;
266
- Debug.disable();
267
- }
268
- });
269
-
270
- test("should handle D2 generation errors gracefully", async () => {
271
- const docsDir = path.join(tempDir, "docs");
272
- await mkdir(docsDir, { recursive: true });
273
-
274
- const markdown = `\`\`\`d2\nA -> B\n\`\`\``;
275
-
276
- // Mock getD2Svg to throw error
277
- const originalFetch = global.fetch;
278
- global.fetch = () => Promise.reject(new Error("Network error"));
279
-
280
- try {
281
- const result = await saveD2Assets({ markdown, docsDir });
282
-
283
- // TODO: When retry still fails, it will use a non-existent image
284
- expect(result).toContain("![](../assets/d2/");
285
- } finally {
286
- global.fetch = originalFetch;
287
- }
288
- });
289
-
290
- test("should write .d2 file when debug is enabled", async () => {
291
- const docsDir = path.join(tempDir, "docs");
292
- await mkdir(docsDir, { recursive: true });
293
-
294
- const markdown = `\`\`\`d2\nA -> B\n\`\`\``;
295
-
296
- // Enable debug mode
297
- Debug.enable("doc-smith");
298
-
299
- try {
300
- await saveD2Assets({ markdown, docsDir });
301
-
302
- const assetDir = path.join(docsDir, "../", TMP_ASSETS_DIR, "d2");
303
- const files = await readdir(assetDir);
304
- const d2File = files.find((file) => file.endsWith(".d2"));
305
- expect(d2File).toBeDefined();
306
- } finally {
307
- // Restore debug mode
308
- Debug.disable();
309
- }
310
- });
311
- });
312
-
313
- describe("beforePublishHook", () => {
314
- test("should process all markdown files in directory", async () => {
315
- const docsDir = path.join(tempDir, "docs");
316
- await mkdir(docsDir, { recursive: true });
317
-
318
- // Create test markdown files
319
- const file1Content = `# Doc 1\n\`\`\`d2\nA -> B\n\`\`\``;
320
- const file2Content = `# Doc 2\n\`\`\`d2\nC -> D\n\`\`\``;
321
- const file3Content = `# Doc 3\nNo diagrams here.`;
322
-
323
- await writeFile(path.join(docsDir, "doc1.md"), file1Content);
324
- await writeFile(path.join(docsDir, "doc2.md"), file2Content);
325
- await writeFile(path.join(docsDir, "doc3.md"), file3Content);
326
-
327
- await beforePublishHook({ docsDir });
328
-
329
- // Check that files were processed
330
- const processedFile1 = await readFile(path.join(docsDir, "doc1.md"), "utf8");
331
- const processedFile2 = await readFile(path.join(docsDir, "doc2.md"), "utf8");
332
- const processedFile3 = await readFile(path.join(docsDir, "doc3.md"), "utf8");
333
-
334
- expect(processedFile1).not.toContain("```d2");
335
- expect(processedFile2).not.toContain("```d2");
336
- expect(processedFile3).toBe(file3Content); // Unchanged
337
-
338
- expect(processedFile1).toContain("![](../assets/d2/");
339
- expect(processedFile2).toContain("![](../assets/d2/");
340
- });
341
-
342
- test("should handle nested directories", async () => {
343
- const docsDir = path.join(tempDir, "docs");
344
- const subDir = path.join(docsDir, "subdir");
345
- await mkdir(subDir, { recursive: true });
346
-
347
- const fileContent = `# Nested Doc\n\`\`\`d2\nA -> B\n\`\`\``;
348
- await writeFile(path.join(subDir, "nested.md"), fileContent);
349
-
350
- await beforePublishHook({ docsDir });
351
-
352
- const processedFile = await readFile(path.join(subDir, "nested.md"), "utf8");
353
- expect(processedFile).not.toContain("```d2");
354
- expect(processedFile).toContain("![](../assets/d2/");
355
- });
356
-
357
- test("should handle empty docs directory", async () => {
358
- const docsDir = path.join(tempDir, "empty-docs");
359
- await mkdir(docsDir, { recursive: true });
360
-
361
- // Should not throw error
362
- await expect(beforePublishHook({ docsDir })).resolves.toBeUndefined();
363
- });
364
-
365
- test("should handle non-existent directory", async () => {
366
- const nonExistentDir = path.join(tempDir, "non-existent");
367
-
368
- const result = await beforePublishHook({ docsDir: nonExistentDir });
369
-
370
- expect(result).toBeUndefined();
371
- });
372
- });
373
-
374
- describe("checkD2Content", () => {
375
- test("should generate and cache D2 SVG", async () => {
376
- const content = "A -> B: test connection";
377
-
378
- // Should not throw in normal operation
379
- await expect(checkD2Content({ content })).resolves.toBeUndefined();
380
- });
381
-
382
- test("should use cached file when available", async () => {
383
- const content = "A -> B: cached test";
384
-
385
- // First call should generate
386
- await checkD2Content({ content });
387
-
388
- // Second call should use cache
389
- const debugLogs = [];
390
- const originalWrite = process.stderr.write;
391
- process.stderr.write = (chunk) => {
392
- debugLogs.push(chunk.toString());
393
- return true;
394
- };
395
- Debug.enable("doc-smith");
396
-
397
- try {
398
- const startTime = Date.now();
399
- await checkD2Content({ content });
400
- const endTime = Date.now();
401
-
402
- // Cache hit should be very fast (< 100ms)
403
- expect(endTime - startTime).toBeLessThan(100);
404
- expect(
405
- debugLogs.some((log) => log.includes("Found assets cache, skipping generation")),
406
- ).toBe(true);
407
- } finally {
408
- process.stderr.write = originalWrite;
409
- Debug.disable();
410
- }
411
- });
412
-
413
- test("should handle generation errors in strict mode", async () => {
414
- // Mock fetch to simulate server error
415
- const originalFetch = global.fetch;
416
- global.fetch = () =>
417
- Promise.resolve({
418
- ok: false,
419
- status: 500,
420
- statusText: "Internal Server Error",
421
- text: () => Promise.resolve("Error response"),
422
- });
423
-
424
- try {
425
- await expect(checkD2Content({ content: "A -> B" })).rejects.toThrow();
426
- } finally {
427
- global.fetch = originalFetch;
428
- }
429
- });
430
-
431
- test("should handle empty content", async () => {
432
- await expect(checkD2Content({ content: "" })).resolves.toBeUndefined();
433
- });
434
-
435
- test("should handle malformed D2 content", async () => {
436
- const malformedContent = "A -> B -> [invalid";
437
-
438
- // May throw depending on D2 server validation
439
- try {
440
- await checkD2Content({ content: malformedContent });
441
- } catch (error) {
442
- expect(error).toBeDefined();
443
- }
444
- });
445
-
446
- test("should write .d2 file when debug is enabled", async () => {
447
- const content = "A -> B: debug test";
448
-
449
- Debug.enable("doc-smith");
450
-
451
- try {
452
- await checkD2Content({ content });
453
-
454
- const assetDir = path.join(process.cwd(), DOC_SMITH_DIR, TMP_DIR, TMP_ASSETS_DIR, "d2");
455
- const files = await readdir(assetDir);
456
- const d2File = files.find((file) => file.endsWith(".d2"));
457
- expect(d2File).toBeDefined();
458
- } finally {
459
- Debug.disable();
460
- }
461
- });
462
- });
463
-
464
- describe("ensureTmpDir", () => {
465
- test("should create tmp directory structure", async () => {
466
- // Change to temp directory for testing
467
- const originalCwd = process.cwd();
468
- process.chdir(tempDir);
469
-
470
- try {
471
- await ensureTmpDir();
472
-
473
- const tmpDir = path.join(tempDir, DOC_SMITH_DIR, TMP_DIR);
474
- const gitignorePath = path.join(tmpDir, ".gitignore");
475
-
476
- expect(existsSync(tmpDir)).toBe(true);
477
- expect(existsSync(gitignorePath)).toBe(true);
478
-
479
- const gitignoreContent = await readFile(gitignorePath, "utf8");
480
- expect(gitignoreContent).toBe("**/*");
481
- } finally {
482
- process.chdir(originalCwd);
483
- }
484
- });
485
-
486
- test("should not recreate if already exists", async () => {
487
- const originalCwd = process.cwd();
488
- process.chdir(tempDir);
489
-
490
- try {
491
- // First call
492
- await ensureTmpDir();
493
-
494
- const tmpDir = path.join(tempDir, DOC_SMITH_DIR, TMP_DIR);
495
- const gitignorePath = path.join(tmpDir, ".gitignore");
496
-
497
- // Modify .gitignore to test if it gets overwritten
498
- await writeFile(gitignorePath, "modified content");
499
-
500
- // Second call
501
- await ensureTmpDir();
502
-
503
- const gitignoreContent = await readFile(gitignorePath, "utf8");
504
- expect(gitignoreContent).toBe("modified content"); // Should not be overwritten
505
- } finally {
506
- process.chdir(originalCwd);
507
- }
508
- });
509
-
510
- test("should handle directory creation errors", async () => {
511
- // Try to create in a read-only location
512
- const originalCwd = process.cwd();
513
-
514
- try {
515
- process.chdir("/root"); // Typically read-only
516
- await expect(ensureTmpDir()).rejects.toThrow();
517
- } catch (error) {
518
- // Expected to fail in read-only directory
519
- expect(error).toBeDefined();
520
- } finally {
521
- process.chdir(originalCwd);
522
- }
523
- });
524
- });
525
-
526
- describe("edge cases and error handling", () => {
527
- test("should handle very large D2 content", async () => {
528
- // Generate large D2 content
529
- const largeContent = Array.from({ length: 1000 }, (_, i) => `Node${i} -> Node${i + 1}`).join(
530
- "\n",
531
- );
532
-
533
- const result = await getD2Svg({ content: largeContent, strict: false });
534
- expect(result === null || typeof result === "string").toBe(true);
535
- }, 20000);
536
-
537
- test("should handle special characters in D2 content", async () => {
538
- const specialContent = `
539
- "Node with spaces" -> "Node with 中文"
540
- "Node with emoji 🎉" -> "Node with symbols @#$%"
541
- `;
542
-
543
- const result = await getD2Svg({ content: specialContent, strict: false });
544
- expect(result === null || typeof result === "string").toBe(true);
545
- }, 15000);
546
-
547
- test("should handle concurrent D2 processing", async () => {
548
- const contents = ["A -> B", "C -> D", "E -> F", "G -> H", "I -> J"];
549
-
550
- // Process multiple D2 contents concurrently
551
- const promises = contents.map((content) => getD2Svg({ content, strict: false }));
552
- const results = await Promise.all(promises);
553
-
554
- expect(results).toHaveLength(5);
555
- results.forEach((result) => {
556
- expect(result === null || typeof result === "string").toBe(true);
557
- });
558
- }, 20000);
559
-
560
- test("should handle malformed regex in saveD2Assets", async () => {
561
- const docsDir = path.join(tempDir, "docs");
562
- await mkdir(docsDir, { recursive: true });
563
-
564
- // Test with malformed D2 blocks (unclosed)
565
- const malformedMarkdown = `# Test\n\`\`\`d2\nA -> B\n\`\`\`\n\`\`\`d2\nUnclosed block`;
566
-
567
- const result = await saveD2Assets({ markdown: malformedMarkdown, docsDir });
568
-
569
- // Should handle gracefully and process what it can
570
- expect(typeof result).toBe("string");
571
- });
572
-
573
- test("should handle empty markdown input", async () => {
574
- const docsDir = path.join(tempDir, "docs");
575
- await mkdir(docsDir, { recursive: true });
576
-
577
- const result = await saveD2Assets({ markdown: "", docsDir });
578
- expect(result).toBe("");
579
- });
580
-
581
- test("should handle null and undefined markdown input", async () => {
582
- const docsDir = path.join(tempDir, "docs");
583
- await mkdir(docsDir, { recursive: true });
584
-
585
- const result1 = await saveD2Assets({ markdown: null, docsDir });
586
- expect(result1).toBe(null);
587
-
588
- const result2 = await saveD2Assets({ markdown: undefined, docsDir });
589
- expect(result2).toBe(undefined);
590
- });
591
-
592
- test("should handle getChart network errors in non-strict mode", async () => {
593
- // Mock fetch to return error response
594
- const originalFetch = global.fetch;
595
- global.fetch = () =>
596
- Promise.resolve({
597
- ok: false,
598
- status: 500,
599
- statusText: "Internal Server Error",
600
- text: () => Promise.resolve("Error response"),
601
- });
602
-
603
- try {
604
- const result = await getChart({ content: "A -> B", strict: false });
605
- // Should handle error gracefully in non-strict mode
606
- expect(result === null || typeof result === "string").toBe(true);
607
- } finally {
608
- global.fetch = originalFetch;
609
- }
610
- });
611
-
612
- test("should preserve line endings and whitespace in saveD2Assets", async () => {
613
- const docsDir = path.join(tempDir, "docs");
614
- await mkdir(docsDir, { recursive: true });
615
-
616
- const markdown = `# Title\r\n\r\n\`\`\`d2\nA -> B\n\`\`\`\r\n\r\nEnd`;
617
-
618
- const result = await saveD2Assets({ markdown, docsDir });
619
-
620
- // Should preserve original line endings and spacing
621
- expect(result).toContain("\r\n\r\n");
622
- expect(result.split("\n").length).toBeGreaterThan(3);
623
- });
624
-
625
- test("should handle multiple concurrent calls to ensureTmpDir", async () => {
626
- const originalCwd = process.cwd();
627
- process.chdir(tempDir);
628
-
629
- try {
630
- // Multiple concurrent calls should not interfere
631
- const promises = Array.from({ length: 5 }, () => ensureTmpDir());
632
- await Promise.all(promises);
633
-
634
- // Should only create directory once
635
- const tmpDir = path.join(tempDir, DOC_SMITH_DIR, TMP_DIR);
636
- expect(existsSync(tmpDir)).toBe(true);
637
-
638
- // .gitignore should be created properly
639
- const gitignoreContent = await readFile(path.join(tmpDir, ".gitignore"), "utf8");
640
- expect(gitignoreContent).toBe("**/*");
641
- } finally {
642
- process.chdir(originalCwd);
643
- }
644
- });
645
- });
646
- });
@@ -1 +0,0 @@
1
- body { color: }
@@ -1 +0,0 @@
1
- body { color: red }
@@ -1 +0,0 @@
1
- body { color: red;
@@ -1 +0,0 @@
1
- body { content: var(--undefined); }
@@ -1,2 +0,0 @@
1
- /* unused variable via custom property */
2
- :root { --unused: 1; }
@@ -1 +0,0 @@
1
- body { color: red; }
@@ -1,2 +0,0 @@
1
- FROM alpine
2
- RUN echo hi
@@ -1,2 +0,0 @@
1
- FROM alpine
2
- RUN if [ 1 -eq 1 ] then echo hi
@@ -1,2 +0,0 @@
1
- FROM alpine:3.18
2
- CMD ["echo","ok"]
@@ -1,5 +0,0 @@
1
- package main
2
-
3
- func main() {
4
- var := 1
5
- }
@@ -1,5 +0,0 @@
1
- package main
2
-
3
- func main() {
4
- fmt.Println("hi")
5
- }