@aigne/doc-smith 0.9.10 → 0.9.11-beta
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 +12 -0
- package/README.md +189 -219
- package/README.zh.md +270 -0
- package/agents/bash-executor/index.mjs +347 -0
- package/agents/clear/ai/intent.md +142 -0
- package/agents/clear/choose-contents.mjs +13 -65
- package/agents/clear/clear-auth-tokens.mjs +17 -21
- package/agents/clear/clear-deployment-config.mjs +33 -24
- package/agents/clear/index.yaml +1 -9
- package/agents/content-checker/ai/intent.md +209 -0
- package/agents/content-checker/clean-invalid-docs.mjs +254 -0
- package/agents/content-checker/index.mjs +191 -0
- package/agents/content-checker/validate-content.mjs +983 -0
- package/agents/generate-images/generate-image.yaml +75 -0
- package/agents/generate-images/generate-summary.mjs +213 -0
- package/agents/generate-images/index.yaml +39 -0
- package/agents/generate-images/prepare-generation.mjs +286 -0
- package/agents/generate-images/prepare-image-generation.mjs +130 -0
- package/{prompts/detail/diagram/generate-image-system.md → agents/generate-images/prompts/system.md} +22 -56
- package/agents/generate-images/prompts/user.md +85 -0
- package/agents/generate-images/save-image-result.mjs +247 -0
- package/agents/generate-images/scan-image-slots.mjs +247 -0
- package/agents/localize/index.yaml +19 -42
- package/{prompts/translate → agents/localize/prompts}/translate-document.md +0 -139
- package/agents/localize/translate-documents/generate-summary.mjs +163 -0
- package/agents/localize/translate-documents/load-glossary.mjs +52 -0
- package/agents/localize/translate-documents/prepare-translation.mjs +249 -0
- package/agents/localize/translate-documents/save-translation.mjs +171 -0
- package/agents/localize/translate-documents/translate-document-to-language.mjs +209 -0
- package/agents/localize/translate-documents/translate-document.yaml +23 -0
- package/agents/localize/translate-documents/translate-to-languages.yaml +10 -0
- package/agents/localize/translate-images/check-image-translation.mjs +225 -0
- package/agents/localize/translate-images/detect-text/detect-and-update-shared.mjs +148 -0
- package/agents/localize/translate-images/detect-text/detect-image-text.yaml +44 -0
- package/agents/localize/translate-images/detect-text/detect-images-text.yaml +21 -0
- package/agents/localize/translate-images/detect-text/prompts/detect-image-text-system.md +43 -0
- package/agents/localize/translate-images/detect-text/prompts/detect-image-text-user.md +14 -0
- package/agents/localize/translate-images/detect-text/save-text-detection.mjs +105 -0
- package/agents/localize/translate-images/prepare-image-input.mjs +124 -0
- package/agents/localize/translate-images/save-image-translation.mjs +172 -0
- package/agents/localize/translate-images/scan-doc-images.mjs +165 -0
- package/agents/localize/translate-images/translate-doc-images.yaml +24 -0
- package/agents/localize/{translate-diagram.yaml → translate-images/translate-image.yaml} +25 -14
- package/agents/publish/ai/intent.md +182 -0
- package/agents/publish/check.mjs +107 -0
- package/agents/publish/index.yaml +9 -14
- package/agents/publish/publish-docs.mjs +81 -61
- package/agents/publish/translate-meta.mjs +79 -58
- package/agents/save-document/index.mjs +260 -0
- package/agents/structure-checker/index.mjs +307 -0
- package/agents/structure-checker/validate-structure.mjs +477 -0
- package/agents/update-image/analyze-feedback.yaml +37 -0
- package/agents/update-image/index.yaml +78 -0
- package/agents/update-image/load-existing-image.mjs +211 -0
- package/agents/update-image/prompts/analyze-feedback-system.md +43 -0
- package/agents/update-image/prompts/analyze-feedback-user.md +15 -0
- package/aigne.yaml +26 -139
- package/package.json +16 -48
- package/scripts/README.md +90 -0
- package/scripts/install.sh +86 -0
- package/scripts/uninstall.sh +52 -0
- package/skills/doc-smith/SKILL.md +285 -0
- package/skills/doc-smith/ai/intent/sources-improve.md +290 -0
- package/skills/doc-smith/references/changeset-guide.md +171 -0
- package/skills/doc-smith/references/document-content-guide.md +214 -0
- package/skills/doc-smith/references/document-structure-schema.md +138 -0
- package/skills/doc-smith/references/patch-guide.md +96 -0
- package/skills/doc-smith/references/structure-confirmation-guide.md +133 -0
- package/skills/doc-smith/references/structure-planning-guide.md +149 -0
- package/skills/doc-smith/references/update-workflow.md +108 -0
- package/skills/doc-smith/references/user-intent-guide.md +175 -0
- package/skills/doc-smith/references/workspace-initialization.md +376 -0
- package/skills/doc-smith-docs-detail/SKILL.md +356 -0
- package/skills/doc-smith-docs-detail/ai/intent.md +271 -0
- package/skills-entry/doc-smith/ai/intent.md +260 -0
- package/skills-entry/doc-smith/index.mjs +66 -0
- package/skills-entry/doc-smith/prompt.md +57 -0
- package/skills-entry/doc-smith/utils.mjs +27 -0
- package/skills-entry/doc-smith-docs-detail/batch.yaml +56 -0
- package/skills-entry/doc-smith-docs-detail/index.mjs +95 -0
- package/skills-entry/doc-smith-docs-detail/prompt.md +64 -0
- package/utils/afs-factory.mjs +183 -0
- package/utils/agent-constants.mjs +97 -0
- package/utils/{auth-utils.mjs → auth.mjs} +6 -9
- package/{agents/utils/update-branding.mjs → utils/branding.mjs} +3 -4
- package/utils/config.mjs +261 -0
- package/utils/constants.mjs +32 -0
- package/utils/deploy.mjs +3 -3
- package/utils/docs-converter.mjs +454 -0
- package/utils/docs.mjs +212 -0
- package/utils/document-paths.mjs +172 -0
- package/utils/files.mjs +74 -0
- package/utils/git.mjs +65 -0
- package/utils/{blocklet.mjs → http.mjs} +18 -0
- package/utils/image-slots.mjs +57 -0
- package/utils/image-utils.mjs +114 -0
- package/utils/project.mjs +95 -0
- package/utils/sources-path-resolver.mjs +76 -0
- package/utils/{upload-files.mjs → upload.mjs} +3 -3
- package/utils/workspace.mjs +371 -0
- package/agents/chat/chat-system.md +0 -38
- package/agents/chat/index.mjs +0 -59
- package/agents/chat/skills/generate-document.yaml +0 -15
- package/agents/chat/skills/list-documents.mjs +0 -15
- package/agents/chat/skills/update-document.yaml +0 -24
- package/agents/clear/clear-document-config.mjs +0 -36
- package/agents/clear/clear-document-structure.mjs +0 -102
- package/agents/clear/clear-generated-docs.mjs +0 -142
- package/agents/clear/clear-media-description.mjs +0 -129
- package/agents/create/aggregate-document-structure.mjs +0 -21
- package/agents/create/analyze-diagram-type-llm.yaml +0 -159
- package/agents/create/analyze-diagram-type.mjs +0 -455
- package/agents/create/check-document-structure.yaml +0 -30
- package/agents/create/check-need-generate-structure.mjs +0 -138
- package/agents/create/document-structure-tools/add-document.mjs +0 -85
- package/agents/create/document-structure-tools/delete-document.mjs +0 -116
- package/agents/create/document-structure-tools/move-document.mjs +0 -109
- package/agents/create/document-structure-tools/update-document.mjs +0 -84
- package/agents/create/generate-diagram-image.yaml +0 -91
- package/agents/create/generate-structure.yaml +0 -106
- package/agents/create/index.yaml +0 -45
- package/agents/create/refine-document-structure.yaml +0 -12
- package/agents/create/replace-d2-with-image.mjs +0 -610
- package/agents/create/update-document-structure.yaml +0 -54
- package/agents/create/user-add-document/add-documents-to-structure.mjs +0 -90
- package/agents/create/user-add-document/find-documents-to-add-links.yaml +0 -47
- package/agents/create/user-add-document/index.yaml +0 -46
- package/agents/create/user-add-document/prepare-documents-to-translate.mjs +0 -22
- package/agents/create/user-add-document/print-add-document-summary.mjs +0 -63
- package/agents/create/user-add-document/review-documents-with-new-links.mjs +0 -110
- package/agents/create/user-remove-document/find-documents-with-invalid-links.mjs +0 -78
- package/agents/create/user-remove-document/index.yaml +0 -40
- package/agents/create/user-remove-document/prepare-documents-to-translate.mjs +0 -22
- package/agents/create/user-remove-document/print-remove-document-summary.mjs +0 -53
- package/agents/create/user-remove-document/remove-documents-from-structure.mjs +0 -99
- package/agents/create/user-remove-document/review-documents-with-invalid-links.mjs +0 -115
- package/agents/create/user-review-document-structure.mjs +0 -139
- package/agents/create/utils/init-current-content.mjs +0 -34
- package/agents/create/utils/merge-document-structures.mjs +0 -36
- package/agents/evaluate/code-snippet.mjs +0 -97
- package/agents/evaluate/document-structure.yaml +0 -67
- package/agents/evaluate/document.yaml +0 -82
- package/agents/evaluate/generate-report.mjs +0 -85
- package/agents/evaluate/index.yaml +0 -46
- package/agents/history/index.yaml +0 -6
- package/agents/history/view.mjs +0 -78
- package/agents/init/check.mjs +0 -16
- package/agents/init/index.mjs +0 -643
- package/agents/init/validate.mjs +0 -16
- package/agents/localize/choose-language.mjs +0 -107
- package/agents/localize/record-translation-history.mjs +0 -23
- package/agents/localize/save-doc-translation-or-skip.mjs +0 -18
- package/agents/localize/set-review-content.mjs +0 -58
- package/agents/localize/translate-document-wrapper.mjs +0 -34
- package/agents/localize/translate-document.yaml +0 -24
- package/agents/localize/translate-multilingual.yaml +0 -57
- package/agents/localize/translate-or-skip-diagram.mjs +0 -52
- package/agents/media/batch-generate-media-description.yaml +0 -46
- package/agents/media/generate-media-description.yaml +0 -50
- package/agents/media/load-media-description.mjs +0 -454
- package/agents/prefs/index.mjs +0 -203
- package/agents/schema/document-structure-item.yaml +0 -26
- package/agents/schema/document-structure-refine-item.yaml +0 -23
- package/agents/schema/document-structure.yaml +0 -29
- package/agents/update/batch-generate-document.yaml +0 -27
- package/agents/update/batch-update-document.yaml +0 -7
- package/agents/update/check-diagram-flag.mjs +0 -116
- package/agents/update/check-document.mjs +0 -162
- package/agents/update/check-generate-diagram.mjs +0 -106
- package/agents/update/check-update-is-single.mjs +0 -53
- package/agents/update/document-tools/update-document-content.mjs +0 -303
- package/agents/update/generate-diagram.yaml +0 -80
- package/agents/update/generate-document.yaml +0 -70
- package/agents/update/handle-document-update.yaml +0 -103
- package/agents/update/index.yaml +0 -69
- package/agents/update/pre-check-generate-diagram.yaml +0 -44
- package/agents/update/save-and-translate-document.mjs +0 -80
- package/agents/update/update-document-detail.yaml +0 -71
- package/agents/update/update-single/update-single-document-detail.mjs +0 -322
- package/agents/update/update-single-document.yaml +0 -7
- package/agents/update/user-review-document.mjs +0 -272
- package/agents/utils/action-success.mjs +0 -16
- package/agents/utils/analyze-document-feedback-intent.yaml +0 -32
- package/agents/utils/analyze-feedback-intent.mjs +0 -253
- package/agents/utils/analyze-structure-feedback-intent.yaml +0 -29
- package/agents/utils/check-detail-result.mjs +0 -51
- package/agents/utils/check-feedback-refiner.mjs +0 -81
- package/agents/utils/choose-docs.mjs +0 -251
- package/agents/utils/document-icon-generate.yaml +0 -52
- package/agents/utils/document-title-streamline.yaml +0 -48
- package/agents/utils/ensure-document-icons.mjs +0 -129
- package/agents/utils/exit.mjs +0 -6
- package/agents/utils/feedback-refiner.yaml +0 -50
- package/agents/utils/find-item-by-path.mjs +0 -114
- package/agents/utils/find-user-preferences-by-path.mjs +0 -37
- package/agents/utils/format-document-structure.mjs +0 -35
- package/agents/utils/generate-document-or-skip.mjs +0 -41
- package/agents/utils/handle-diagram-operations.mjs +0 -263
- package/agents/utils/load-all-document-content.mjs +0 -30
- package/agents/utils/load-document-all-content.mjs +0 -96
- package/agents/utils/load-sources.mjs +0 -405
- package/agents/utils/map-reasoning-effort-level.mjs +0 -15
- package/agents/utils/post-generate.mjs +0 -133
- package/agents/utils/read-current-document-content.mjs +0 -46
- package/agents/utils/save-doc-translation.mjs +0 -30
- package/agents/utils/save-doc.mjs +0 -54
- package/agents/utils/save-output.mjs +0 -26
- package/agents/utils/save-sidebar.mjs +0 -38
- package/agents/utils/skip-if-content-exists.mjs +0 -27
- package/agents/utils/streamline-document-titles-if-needed.mjs +0 -88
- package/agents/utils/transform-detail-data-sources.mjs +0 -45
- package/assets/report-template/report.html +0 -198
- package/docs-mcp/analyze-content-relevance.yaml +0 -50
- package/docs-mcp/analyze-docs-relevance.yaml +0 -59
- package/docs-mcp/docs-search.yaml +0 -42
- package/docs-mcp/get-docs-detail.mjs +0 -41
- package/docs-mcp/get-docs-structure.mjs +0 -16
- package/docs-mcp/read-doc-content.mjs +0 -119
- package/prompts/common/document/content-rules-core.md +0 -20
- package/prompts/common/document/markdown-syntax-rules.md +0 -65
- package/prompts/common/document/media-file-list-usage-rules.md +0 -18
- package/prompts/common/document/openapi-usage-rules.md +0 -189
- package/prompts/common/document/role-and-personality.md +0 -16
- package/prompts/common/document/user-preferences.md +0 -9
- package/prompts/common/document-structure/conflict-resolution-guidance.md +0 -16
- package/prompts/common/document-structure/document-icon-generate.md +0 -116
- package/prompts/common/document-structure/document-structure-rules.md +0 -43
- package/prompts/common/document-structure/document-title-streamline.md +0 -86
- package/prompts/common/document-structure/glossary.md +0 -7
- package/prompts/common/document-structure/intj-traits.md +0 -5
- package/prompts/common/document-structure/openapi-usage-rules.md +0 -28
- package/prompts/common/document-structure/output-constraints.md +0 -18
- package/prompts/common/document-structure/user-locale-rules.md +0 -10
- package/prompts/common/document-structure/user-preferences.md +0 -9
- package/prompts/detail/custom/admonition-usage-rules.md +0 -94
- package/prompts/detail/custom/code-block-usage-rules.md +0 -163
- package/prompts/detail/custom/custom-components/x-card-usage-rules.md +0 -63
- package/prompts/detail/custom/custom-components/x-cards-usage-rules.md +0 -83
- package/prompts/detail/custom/custom-components/x-field-desc-usage-rules.md +0 -120
- package/prompts/detail/custom/custom-components/x-field-group-usage-rules.md +0 -80
- package/prompts/detail/custom/custom-components/x-field-usage-rules.md +0 -189
- package/prompts/detail/custom/custom-components-usage-rules.md +0 -18
- package/prompts/detail/diagram/generate-image-user.md +0 -81
- package/prompts/detail/diagram/guide.md +0 -29
- package/prompts/detail/diagram/official-examples.md +0 -712
- package/prompts/detail/diagram/pre-check.md +0 -23
- package/prompts/detail/diagram/role-and-personality.md +0 -2
- package/prompts/detail/diagram/rules.md +0 -46
- package/prompts/detail/diagram/system-prompt.md +0 -1139
- package/prompts/detail/diagram/user-prompt.md +0 -43
- package/prompts/detail/generate/detail-example.md +0 -457
- package/prompts/detail/generate/document-rules.md +0 -45
- package/prompts/detail/generate/system-prompt.md +0 -61
- package/prompts/detail/generate/user-prompt.md +0 -99
- package/prompts/detail/jsx/rules.md +0 -6
- package/prompts/detail/update/system-prompt.md +0 -121
- package/prompts/detail/update/user-prompt.md +0 -41
- package/prompts/evaluate/document-structure.md +0 -93
- package/prompts/evaluate/document.md +0 -149
- package/prompts/media/media-description/system-prompt.md +0 -43
- package/prompts/media/media-description/user-prompt.md +0 -17
- package/prompts/structure/check-document-structure.md +0 -93
- package/prompts/structure/document-rules.md +0 -21
- package/prompts/structure/find-documents-to-add-links.md +0 -52
- package/prompts/structure/generate/system-prompt.md +0 -13
- package/prompts/structure/generate/user-prompt.md +0 -137
- package/prompts/structure/review/structure-review-system.md +0 -81
- package/prompts/structure/structure-example.md +0 -89
- package/prompts/structure/structure-getting-started.md +0 -10
- package/prompts/structure/update/system-prompt.md +0 -93
- package/prompts/structure/update/user-prompt.md +0 -43
- package/prompts/translate/admonition.md +0 -20
- package/prompts/translate/code-block.md +0 -33
- package/prompts/utils/analyze-document-feedback-intent.md +0 -54
- package/prompts/utils/analyze-structure-feedback-intent.md +0 -43
- package/prompts/utils/feedback-refiner.md +0 -105
- package/types/document-schema.mjs +0 -55
- package/types/document-structure-schema.mjs +0 -261
- package/utils/check-document-has-diagram.mjs +0 -95
- package/utils/conflict-detector.mjs +0 -149
- package/utils/constants/index.mjs +0 -620
- package/utils/constants/linter.mjs +0 -102
- package/utils/d2-utils.mjs +0 -205
- package/utils/debug.mjs +0 -3
- package/utils/delete-diagram-images.mjs +0 -99
- package/utils/diagram-version-utils.mjs +0 -14
- package/utils/docs-finder-utils.mjs +0 -548
- package/utils/evaluate/report-utils.mjs +0 -132
- package/utils/extract-api.mjs +0 -32
- package/utils/file-utils.mjs +0 -960
- package/utils/history-utils.mjs +0 -203
- package/utils/icon-map.mjs +0 -26
- package/utils/image-compress.mjs +0 -154
- package/utils/kroki-utils.mjs +0 -173
- package/utils/linter/index.mjs +0 -50
- package/utils/load-config.mjs +0 -78
- package/utils/markdown/index.mjs +0 -26
- package/utils/markdown-checker.mjs +0 -694
- package/utils/mermaid-validator.mjs +0 -140
- package/utils/mermaid-worker-pool.mjs +0 -250
- package/utils/mermaid-worker.mjs +0 -233
- package/utils/openapi/index.mjs +0 -28
- package/utils/preferences-utils.mjs +0 -175
- package/utils/request.mjs +0 -10
- package/utils/sync-diagram-to-translations.mjs +0 -272
- package/utils/translate-diagram-images.mjs +0 -807
- package/utils/utils.mjs +0 -1354
- /package/{prompts/translate → agents/localize/prompts}/glossary.md +0 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import { join, relative, dirname, basename } from "node:path";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import { parse as yamlParse } from "yaml";
|
|
4
|
+
import { parseSlots } from "./image-slots.mjs";
|
|
5
|
+
import { findImageWithFallback } from "./image-utils.mjs";
|
|
6
|
+
import { PATHS } from "./agent-constants.mjs";
|
|
7
|
+
import {
|
|
8
|
+
isSourcesAbsolutePath,
|
|
9
|
+
parseSourcesPath,
|
|
10
|
+
resolveSourcesPath,
|
|
11
|
+
} from "./sources-path-resolver.mjs";
|
|
12
|
+
import { loadConfigFromFile } from "./config.mjs";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Scan document directory and identify all document directories containing .meta.yaml
|
|
16
|
+
* @param {string} docsDir - Document root directory path
|
|
17
|
+
* @returns {Promise<Array>} Document list, each containing {dirPath, dirName, locale, content, depth}
|
|
18
|
+
*/
|
|
19
|
+
export async function scanDocuments(docsDir) {
|
|
20
|
+
const documents = [];
|
|
21
|
+
|
|
22
|
+
async function scanDir(currentPath) {
|
|
23
|
+
const entries = await fs.readdir(currentPath, { withFileTypes: true });
|
|
24
|
+
|
|
25
|
+
// Check if directory contains .meta.yaml
|
|
26
|
+
const hasMetaFile = entries.some((entry) => entry.isFile() && entry.name === ".meta.yaml");
|
|
27
|
+
|
|
28
|
+
if (hasMetaFile) {
|
|
29
|
+
// This is a document directory, read all language files
|
|
30
|
+
const markdownFiles = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md"));
|
|
31
|
+
|
|
32
|
+
// Calculate document depth (level relative to docs/)
|
|
33
|
+
const relativePath = relative(docsDir, currentPath);
|
|
34
|
+
const depth = relativePath === "" ? 0 : relativePath.split("/").length;
|
|
35
|
+
|
|
36
|
+
const dirName = basename(currentPath);
|
|
37
|
+
|
|
38
|
+
for (const file of markdownFiles) {
|
|
39
|
+
const locale = file.name.replace(".md", "");
|
|
40
|
+
const filePath = join(currentPath, file.name);
|
|
41
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
42
|
+
|
|
43
|
+
documents.push({
|
|
44
|
+
dirPath: currentPath,
|
|
45
|
+
dirName,
|
|
46
|
+
locale,
|
|
47
|
+
content,
|
|
48
|
+
depth,
|
|
49
|
+
relativePath,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Recursively scan subdirectories
|
|
55
|
+
const subDirs = entries.filter((entry) => entry.isDirectory());
|
|
56
|
+
for (const subDir of subDirs) {
|
|
57
|
+
await scanDir(join(currentPath, subDir.name));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await scanDir(docsDir);
|
|
62
|
+
return documents;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Calculate target path based on document depth and language
|
|
67
|
+
* @param {string} relativePath - Path relative to docs/
|
|
68
|
+
* @param {string} dirName - Document directory name
|
|
69
|
+
* @param {string} locale - Language code
|
|
70
|
+
* @param {number} depth - Document depth
|
|
71
|
+
* @returns {string} Target file path (relative to target directory)
|
|
72
|
+
*/
|
|
73
|
+
export function getTargetPath(relativePath, dirName, locale, depth) {
|
|
74
|
+
// English documents have no language suffix, other languages have suffix
|
|
75
|
+
const suffix = locale === "en" ? ".md" : `.${locale}.md`;
|
|
76
|
+
const fileName = `${dirName}${suffix}`;
|
|
77
|
+
|
|
78
|
+
if (depth === 1) {
|
|
79
|
+
// Single level path: move file to root directory
|
|
80
|
+
return fileName;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Multi-level path: keep parent directory, use directory name as file name
|
|
84
|
+
const parentPath = dirname(relativePath);
|
|
85
|
+
return join(parentPath, fileName);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Add .md suffix to internal links
|
|
90
|
+
* @param {string} content - Document content
|
|
91
|
+
* @returns {string} Processed content
|
|
92
|
+
*/
|
|
93
|
+
export function addMarkdownSuffixToLinks(content) {
|
|
94
|
+
// Match Markdown links: [text](path)
|
|
95
|
+
// But not images: 
|
|
96
|
+
// Not external links (http:// or https://)
|
|
97
|
+
// Not links that already have .md suffix
|
|
98
|
+
// Not media file links (images, videos, etc.)
|
|
99
|
+
|
|
100
|
+
// Media file extensions
|
|
101
|
+
const mediaExtensions = /\.(jpg|jpeg|png|gif|webp|svg|mp4|webm|mov|avi|pdf)$/i;
|
|
102
|
+
|
|
103
|
+
return content.replace(/(?<!!)\[([^\]]+)\]\(([^)]+)\)/g, (match, text, url) => {
|
|
104
|
+
// Skip external links
|
|
105
|
+
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
106
|
+
return match;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Skip links that already have .md suffix
|
|
110
|
+
if (url.includes(".md")) {
|
|
111
|
+
return match;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Skip non-document links (such as mailto:, #anchor, etc.)
|
|
115
|
+
if (url.includes(":") || url.startsWith("#")) {
|
|
116
|
+
return match;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Skip media file links (images, videos, PDFs, etc.)
|
|
120
|
+
if (mediaExtensions.test(url)) {
|
|
121
|
+
return match;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Separate path and anchor
|
|
125
|
+
const hashIndex = url.indexOf("#");
|
|
126
|
+
if (hashIndex !== -1) {
|
|
127
|
+
const path = url.substring(0, hashIndex);
|
|
128
|
+
const hash = url.substring(hashIndex);
|
|
129
|
+
return `[${text}](${path}.md${hash})`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Add .md suffix
|
|
133
|
+
return `[${text}](${url}.md)`;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Adjust image paths (based on document depth)
|
|
139
|
+
* @param {string} content - Document content
|
|
140
|
+
* @param {number} depth - Document depth
|
|
141
|
+
* @returns {string} Processed content
|
|
142
|
+
*/
|
|
143
|
+
export function adjustImagePaths(content, depth) {
|
|
144
|
+
// Documents at depth 1 move up one level, need to remove one ../
|
|
145
|
+
// Documents at depth 2+ keep paths unchanged
|
|
146
|
+
|
|
147
|
+
if (depth !== 1) {
|
|
148
|
+
return content;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Match image links: 
|
|
152
|
+
return content.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, path) => {
|
|
153
|
+
// Only process relative paths (paths containing ../)
|
|
154
|
+
if (!path.startsWith("../")) {
|
|
155
|
+
return match;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Remove one ../
|
|
159
|
+
const newPath = path.replace(/^\.\.\//, "");
|
|
160
|
+
return ``;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Read primary language from config file
|
|
166
|
+
* @returns {Promise<string|null>} - Primary language code, returns null if read fails
|
|
167
|
+
*/
|
|
168
|
+
async function loadMainLocale() {
|
|
169
|
+
try {
|
|
170
|
+
const configPath = PATHS.CONFIG;
|
|
171
|
+
if (!(await fs.pathExists(configPath))) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
const content = await fs.readFile(configPath, "utf8");
|
|
175
|
+
const config = yamlParse(content);
|
|
176
|
+
return config?.locale || null;
|
|
177
|
+
} catch (_error) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Replace AFS image slots in document with actual image references
|
|
184
|
+
* @param {string} content - Document content
|
|
185
|
+
* @param {string} docPath - Document path (for calculating relative paths and generating keys)
|
|
186
|
+
* @param {string} locale - Current document language
|
|
187
|
+
* @param {string} mainLocale - Primary language
|
|
188
|
+
* @param {number} depth - Document depth (for calculating relative paths)
|
|
189
|
+
* @param {string} assetsDir - Assets directory path
|
|
190
|
+
* @returns {Promise<string>} - Content with replacements
|
|
191
|
+
*/
|
|
192
|
+
async function replaceImageSlots(
|
|
193
|
+
content,
|
|
194
|
+
docPath,
|
|
195
|
+
locale,
|
|
196
|
+
mainLocale,
|
|
197
|
+
depth,
|
|
198
|
+
assetsDir = PATHS.ASSETS_DIR,
|
|
199
|
+
) {
|
|
200
|
+
// Parse all slots
|
|
201
|
+
const slots = parseSlots(content, docPath);
|
|
202
|
+
|
|
203
|
+
if (slots.length === 0) {
|
|
204
|
+
return content;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Replace each slot
|
|
208
|
+
let result = content;
|
|
209
|
+
for (const slot of slots) {
|
|
210
|
+
const { key, desc, raw } = slot;
|
|
211
|
+
|
|
212
|
+
// Find image
|
|
213
|
+
const imagePath = await findImageWithFallback(key, locale, mainLocale, assetsDir);
|
|
214
|
+
|
|
215
|
+
if (imagePath) {
|
|
216
|
+
// Calculate relative path prefix
|
|
217
|
+
// Target document is at least in targetDir root, needs at least 1 ../ to access assets/ parallel to targetDir
|
|
218
|
+
// depth 0/1: ../assets/{key}/images/{lang}.jpg (from tmp-docs/overview.md to assets/)
|
|
219
|
+
// depth 2: ../../assets/{key}/images/{lang}.jpg (from tmp-docs/api/auth.md to assets/)
|
|
220
|
+
// depth N: N ../'s (minimum 1)
|
|
221
|
+
const pathPrefix = "../".repeat(Math.max(depth, 1));
|
|
222
|
+
const imageRef = `${pathPrefix}assets/${imagePath}`;
|
|
223
|
+
|
|
224
|
+
// Replace slot with image reference
|
|
225
|
+
const imageMarkdown = ``;
|
|
226
|
+
result = result.replace(raw, imageMarkdown);
|
|
227
|
+
}
|
|
228
|
+
// If image doesn't exist, keep slot unchanged (or could choose to remove)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Process single /sources/... image path
|
|
236
|
+
* @param {string} imagePath - Image path
|
|
237
|
+
* @param {number} depth - Document depth
|
|
238
|
+
* @param {string} targetDir - Target directory
|
|
239
|
+
* @param {Array} sourcesConfig - Sources configuration
|
|
240
|
+
* @param {string} workspaceBase - Workspace base path
|
|
241
|
+
* @param {Set} processedImages - Set of processed images
|
|
242
|
+
* @returns {Promise<{newPath: string, copied: boolean} | null>} - New path and whether file was copied
|
|
243
|
+
*/
|
|
244
|
+
async function processSourcesImagePath(
|
|
245
|
+
imagePath,
|
|
246
|
+
depth,
|
|
247
|
+
targetDir,
|
|
248
|
+
sourcesConfig,
|
|
249
|
+
workspaceBase,
|
|
250
|
+
processedImages,
|
|
251
|
+
) {
|
|
252
|
+
if (!isSourcesAbsolutePath(imagePath)) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Parse path, get relative path portion
|
|
257
|
+
const relativePath = parseSourcesPath(imagePath);
|
|
258
|
+
if (!relativePath) {
|
|
259
|
+
console.warn(`⚠️ Invalid sources path format: ${imagePath}`);
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Get physical path (automatically search in each source)
|
|
264
|
+
const resolved = await resolveSourcesPath(imagePath, sourcesConfig, workspaceBase);
|
|
265
|
+
if (!resolved) {
|
|
266
|
+
console.warn(`⚠️ Cannot find image in any source: ${imagePath}`);
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const { physicalPath } = resolved;
|
|
271
|
+
|
|
272
|
+
// Copy to sources subdirectory in temp directory
|
|
273
|
+
// Maintain same path structure as execution layer: targetDir/../sources/<relativePath>
|
|
274
|
+
const targetImagePath = join(dirname(targetDir), "sources", relativePath);
|
|
275
|
+
|
|
276
|
+
let copied = false;
|
|
277
|
+
if (!processedImages.has(targetImagePath)) {
|
|
278
|
+
await fs.ensureDir(dirname(targetImagePath));
|
|
279
|
+
await fs.copy(physicalPath, targetImagePath);
|
|
280
|
+
processedImages.add(targetImagePath);
|
|
281
|
+
copied = true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Calculate relative path
|
|
285
|
+
// depth 0/1: ../sources/path/to/image.png
|
|
286
|
+
// depth 2: ../../sources/path/to/image.png
|
|
287
|
+
const pathPrefix = "../".repeat(Math.max(depth, 1));
|
|
288
|
+
const newPath = `${pathPrefix}sources/${relativePath}`;
|
|
289
|
+
|
|
290
|
+
return { newPath, copied };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Process /sources/... absolute path images in document
|
|
295
|
+
* Supports two formats:
|
|
296
|
+
* - Markdown: 
|
|
297
|
+
* - HTML: <img src="/sources/path/to/image.png" ... />
|
|
298
|
+
* @param {string} content - Document content
|
|
299
|
+
* @param {number} depth - Document depth (for calculating relative paths)
|
|
300
|
+
* @param {string} targetDir - Target directory (temp directory)
|
|
301
|
+
* @param {Array} sourcesConfig - sources configuration from config.yaml
|
|
302
|
+
* @param {string} workspaceBase - Workspace base path
|
|
303
|
+
* @returns {Promise<{content: string, copiedCount: number}>} - Processed content and count of copied images
|
|
304
|
+
*/
|
|
305
|
+
async function processSourcesImages(content, depth, targetDir, sourcesConfig, workspaceBase) {
|
|
306
|
+
let result = content;
|
|
307
|
+
const processedImages = new Set();
|
|
308
|
+
let copiedCount = 0;
|
|
309
|
+
|
|
310
|
+
// 1. Process Markdown format images: 
|
|
311
|
+
const markdownImageRegex = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
312
|
+
const markdownMatches = [...content.matchAll(markdownImageRegex)];
|
|
313
|
+
|
|
314
|
+
for (const match of markdownMatches) {
|
|
315
|
+
const [fullMatch, alt, imagePath] = match;
|
|
316
|
+
|
|
317
|
+
const processResult = await processSourcesImagePath(
|
|
318
|
+
imagePath,
|
|
319
|
+
depth,
|
|
320
|
+
targetDir,
|
|
321
|
+
sourcesConfig,
|
|
322
|
+
workspaceBase,
|
|
323
|
+
processedImages,
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
if (processResult) {
|
|
327
|
+
const { newPath, copied } = processResult;
|
|
328
|
+
if (copied) copiedCount++;
|
|
329
|
+
result = result.replace(fullMatch, ``);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 2. Process HTML img tags: <img src="/sources/path/to/image.png" ... />
|
|
334
|
+
const htmlImgRegex = /<img\s+([^>]*?)src=["']([^"']+)["']([^>]*?)\/?>/gi;
|
|
335
|
+
const htmlMatches = [...result.matchAll(htmlImgRegex)];
|
|
336
|
+
|
|
337
|
+
for (const match of htmlMatches) {
|
|
338
|
+
const [fullMatch, beforeSrc, imagePath, afterSrc] = match;
|
|
339
|
+
|
|
340
|
+
const processResult = await processSourcesImagePath(
|
|
341
|
+
imagePath,
|
|
342
|
+
depth,
|
|
343
|
+
targetDir,
|
|
344
|
+
sourcesConfig,
|
|
345
|
+
workspaceBase,
|
|
346
|
+
processedImages,
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
if (processResult) {
|
|
350
|
+
const { newPath, copied } = processResult;
|
|
351
|
+
if (copied) copiedCount++;
|
|
352
|
+
// Rebuild img tag, keeping other attributes unchanged
|
|
353
|
+
const newImgTag = `<img ${beforeSrc}src="${newPath}"${afterSrc}/>`;
|
|
354
|
+
result = result.replace(fullMatch, newImgTag);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return { content: result, copiedCount };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Copy documents to temp directory and perform conversion
|
|
363
|
+
* @param {string} sourceDir - Source document directory
|
|
364
|
+
* @param {string} targetDir - Target directory
|
|
365
|
+
* @returns {Promise<Object>} Conversion statistics
|
|
366
|
+
*/
|
|
367
|
+
export async function copyDocumentsToTemp(sourceDir, targetDir) {
|
|
368
|
+
// Scan all documents
|
|
369
|
+
const documents = await scanDocuments(sourceDir);
|
|
370
|
+
|
|
371
|
+
if (documents.length === 0) {
|
|
372
|
+
console.warn("⚠️ No documents found to convert.");
|
|
373
|
+
return { total: 0, converted: 0 };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Read primary language (for image fallback)
|
|
377
|
+
const mainLocale = await loadMainLocale();
|
|
378
|
+
|
|
379
|
+
// Load sources config (for processing /sources/... absolute paths)
|
|
380
|
+
const config = await loadConfigFromFile();
|
|
381
|
+
const sourcesConfig = config?.sources || [];
|
|
382
|
+
|
|
383
|
+
const stats = {
|
|
384
|
+
total: documents.length,
|
|
385
|
+
converted: 0,
|
|
386
|
+
depth1: 0,
|
|
387
|
+
depth2Plus: 0,
|
|
388
|
+
slotsReplaced: 0,
|
|
389
|
+
sourcesCopied: 0,
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// Process each document
|
|
393
|
+
for (const doc of documents) {
|
|
394
|
+
const { relativePath, dirName, locale, content, depth } = doc;
|
|
395
|
+
|
|
396
|
+
// Calculate target path
|
|
397
|
+
const targetPath = getTargetPath(relativePath, dirName, locale, depth);
|
|
398
|
+
const fullTargetPath = join(targetDir, targetPath);
|
|
399
|
+
|
|
400
|
+
// Process content
|
|
401
|
+
let processedContent = content;
|
|
402
|
+
|
|
403
|
+
// 1. Adjust image paths in original document (must be before replaceImageSlots to avoid processing newly generated paths)
|
|
404
|
+
processedContent = adjustImagePaths(processedContent, depth);
|
|
405
|
+
|
|
406
|
+
// 2. Replace AFS image slots with actual image references
|
|
407
|
+
// Use relativePath as docPath (need to add leading /)
|
|
408
|
+
const docPath = relativePath ? `/${relativePath}` : `/${dirName}`;
|
|
409
|
+
const contentBeforeSlotReplace = processedContent;
|
|
410
|
+
processedContent = await replaceImageSlots(
|
|
411
|
+
processedContent,
|
|
412
|
+
docPath,
|
|
413
|
+
locale,
|
|
414
|
+
mainLocale,
|
|
415
|
+
depth,
|
|
416
|
+
PATHS.ASSETS_DIR,
|
|
417
|
+
);
|
|
418
|
+
// Count replaced slots
|
|
419
|
+
if (contentBeforeSlotReplace !== processedContent) {
|
|
420
|
+
const slotsBefore = (contentBeforeSlotReplace.match(/<!--\s*afs:image/g) || []).length;
|
|
421
|
+
const slotsAfter = (processedContent.match(/<!--\s*afs:image/g) || []).length;
|
|
422
|
+
stats.slotsReplaced += slotsBefore - slotsAfter;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// 3. Process /sources/... absolute path images
|
|
426
|
+
if (sourcesConfig.length > 0) {
|
|
427
|
+
const sourcesResult = await processSourcesImages(
|
|
428
|
+
processedContent,
|
|
429
|
+
depth,
|
|
430
|
+
targetDir,
|
|
431
|
+
sourcesConfig,
|
|
432
|
+
PATHS.WORKSPACE_BASE,
|
|
433
|
+
);
|
|
434
|
+
processedContent = sourcesResult.content;
|
|
435
|
+
stats.sourcesCopied += sourcesResult.copiedCount;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// 4. Add .md suffix to internal links
|
|
439
|
+
processedContent = addMarkdownSuffixToLinks(processedContent);
|
|
440
|
+
|
|
441
|
+
// Create target directory and write file
|
|
442
|
+
await fs.ensureDir(dirname(fullTargetPath));
|
|
443
|
+
await fs.writeFile(fullTargetPath, processedContent, "utf8");
|
|
444
|
+
|
|
445
|
+
stats.converted++;
|
|
446
|
+
if (depth === 1) {
|
|
447
|
+
stats.depth1++;
|
|
448
|
+
} else {
|
|
449
|
+
stats.depth2Plus++;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return stats;
|
|
454
|
+
}
|
package/utils/docs.mjs
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { readFile, access, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parse as yamlParse } from "yaml";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if a path exists
|
|
7
|
+
* @param {string} path - Path to check
|
|
8
|
+
* @returns {Promise<boolean>} - True if path exists
|
|
9
|
+
*/
|
|
10
|
+
async function pathExists(path) {
|
|
11
|
+
try {
|
|
12
|
+
await access(path);
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Convert YAML structure to array structure
|
|
21
|
+
* @param {Object} yamlData - Parsed YAML data
|
|
22
|
+
* @returns {Array} - Array of document structure items
|
|
23
|
+
*/
|
|
24
|
+
function convertYamlToStructure(yamlData) {
|
|
25
|
+
if (!yamlData || !yamlData.documents) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const result = [];
|
|
30
|
+
const processNode = (node, parentId = null) => {
|
|
31
|
+
const item = {
|
|
32
|
+
path: node.path,
|
|
33
|
+
title: node.title,
|
|
34
|
+
parentId,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (node.icon) {
|
|
38
|
+
item.icon = node.icon;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
result.push(item);
|
|
42
|
+
|
|
43
|
+
if (node.children && Array.isArray(node.children)) {
|
|
44
|
+
for (const child of node.children) {
|
|
45
|
+
processNode(child, node.path);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
for (const doc of yamlData.documents) {
|
|
51
|
+
processNode(doc);
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Load document structure from output directory
|
|
58
|
+
* @param {string} outputDir - Output directory path
|
|
59
|
+
* @returns {Promise<Array|null>} - Document structure array or null
|
|
60
|
+
*/
|
|
61
|
+
export async function loadDocumentStructure(outputDir) {
|
|
62
|
+
if (!outputDir) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Try loading document-structure.yaml as fallback
|
|
67
|
+
try {
|
|
68
|
+
const yamlPath = join(outputDir, "document-structure.yaml");
|
|
69
|
+
const yamlExists = await pathExists(yamlPath);
|
|
70
|
+
|
|
71
|
+
if (yamlExists) {
|
|
72
|
+
const yamlContent = await readFile(yamlPath, "utf8");
|
|
73
|
+
if (yamlContent?.trim()) {
|
|
74
|
+
try {
|
|
75
|
+
const parsed = yamlParse(yamlContent);
|
|
76
|
+
if (parsed?.documents) {
|
|
77
|
+
return convertYamlToStructure(parsed);
|
|
78
|
+
}
|
|
79
|
+
} catch (parseError) {
|
|
80
|
+
console.error(`Failed to parse document-structure.yaml: ${parseError.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch (readError) {
|
|
85
|
+
if (readError.code !== "ENOENT") {
|
|
86
|
+
console.warn(`Error reading document-structure.yaml: ${readError.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build a tree structure from a flat document structure array
|
|
95
|
+
* @param {Array} documentStructure - Flat document structure array
|
|
96
|
+
* @returns {Object} - Object with rootNodes array and nodeMap
|
|
97
|
+
*/
|
|
98
|
+
export function buildDocumentTree(documentStructure) {
|
|
99
|
+
const nodeMap = new Map();
|
|
100
|
+
const rootNodes = [];
|
|
101
|
+
|
|
102
|
+
documentStructure.forEach((node) => {
|
|
103
|
+
nodeMap.set(node.path, {
|
|
104
|
+
...node,
|
|
105
|
+
children: [],
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
documentStructure.forEach((node) => {
|
|
110
|
+
if (node.parentId) {
|
|
111
|
+
const parent = nodeMap.get(node.parentId);
|
|
112
|
+
if (parent) {
|
|
113
|
+
parent.children.push(nodeMap.get(node.path));
|
|
114
|
+
} else {
|
|
115
|
+
rootNodes.push(nodeMap.get(node.path));
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
rootNodes.push(nodeMap.get(node.path));
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return { rootNodes, nodeMap };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Recursively generate sidebar text from document tree nodes
|
|
127
|
+
* @param {Array} nodes - Array of tree nodes
|
|
128
|
+
* @param {string} indent - Current indentation level
|
|
129
|
+
* @returns {string} - Formatted sidebar text
|
|
130
|
+
*/
|
|
131
|
+
function walk(nodes, indent = "") {
|
|
132
|
+
let out = "";
|
|
133
|
+
for (const node of nodes) {
|
|
134
|
+
const realIndent = node.parentId === null ? "" : indent;
|
|
135
|
+
|
|
136
|
+
// Convert path to .md file path
|
|
137
|
+
// Keep directory structure instead of flattening
|
|
138
|
+
let linkPath;
|
|
139
|
+
if (node.path.endsWith(".md")) {
|
|
140
|
+
linkPath = node.path.startsWith("/") ? node.path : `/${node.path}`;
|
|
141
|
+
} else {
|
|
142
|
+
// Add .md suffix while preserving directory structure
|
|
143
|
+
const relPath = node.path.replace(/^\//, "");
|
|
144
|
+
linkPath = `/${relPath}.md`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
out += `${realIndent}* [${node.title}](${linkPath})\n`;
|
|
148
|
+
|
|
149
|
+
if (node.children && node.children.length > 0) {
|
|
150
|
+
out += walk(node.children, `${indent} `);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Generate sidebar markdown from document structure
|
|
158
|
+
* @param {Array} documentStructure - Flat document structure array
|
|
159
|
+
* @returns {string} - Formatted sidebar markdown
|
|
160
|
+
*/
|
|
161
|
+
export function generateSidebar(documentStructure) {
|
|
162
|
+
const { rootNodes } = buildDocumentTree(documentStructure);
|
|
163
|
+
return walk(rootNodes).replace(/\n+$/, "");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get main language files from docs directory
|
|
168
|
+
* @param {string} docsDir - Documentation directory
|
|
169
|
+
* @returns {Promise<Array>} - Array of markdown files
|
|
170
|
+
*/
|
|
171
|
+
export async function getMainLanguageFiles(docsDir) {
|
|
172
|
+
try {
|
|
173
|
+
await access(docsDir);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (error.code === "ENOENT") {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const files = await readdir(docsDir);
|
|
182
|
+
|
|
183
|
+
const filteredFiles = [];
|
|
184
|
+
const filesSet = new Set(files);
|
|
185
|
+
const processedBaseNames = new Set();
|
|
186
|
+
|
|
187
|
+
for (const file of files) {
|
|
188
|
+
if (!file.endsWith(".md") || file === "_sidebar.md") {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const localeMatch = file.match(/^(.+)\.\w{2}(-\w+)?\.md$/);
|
|
193
|
+
|
|
194
|
+
if (localeMatch) {
|
|
195
|
+
const baseName = localeMatch[1];
|
|
196
|
+
const baseFileName = `${baseName}.md`;
|
|
197
|
+
|
|
198
|
+
if (!filesSet.has(baseFileName) && !processedBaseNames.has(baseName)) {
|
|
199
|
+
filteredFiles.push(file);
|
|
200
|
+
processedBaseNames.add(baseName);
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
const baseName = file.replace(/\.md$/, "");
|
|
204
|
+
if (!processedBaseNames.has(baseName)) {
|
|
205
|
+
filteredFiles.push(file);
|
|
206
|
+
processedBaseNames.add(baseName);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return filteredFiles;
|
|
212
|
+
}
|