@aigne/doc-smith 0.2.6 → 0.2.9

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.
@@ -0,0 +1,205 @@
1
+ import { access, readFile } from "node:fs/promises";
2
+ import { execSync } from "node:child_process";
3
+ import path from "node:path";
4
+ import { glob } from "glob";
5
+
6
+ /**
7
+ * Check if a directory is inside a git repository using git command
8
+ * @param {string} dir - Directory path to check
9
+ * @returns {boolean} True if inside a git repository
10
+ */
11
+ function isInGitRepository(dir) {
12
+ try {
13
+ execSync("git rev-parse --is-inside-work-tree", {
14
+ cwd: dir,
15
+ stdio: "pipe",
16
+ encoding: "utf8",
17
+ });
18
+ return true;
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Find git repository root directory using git command
26
+ * @param {string} startDir - Starting directory path
27
+ * @returns {string|null} Git repository root path or null if not found
28
+ */
29
+ function findGitRoot(startDir) {
30
+ try {
31
+ const gitRoot = execSync("git rev-parse --show-toplevel", {
32
+ cwd: startDir,
33
+ stdio: "pipe",
34
+ encoding: "utf8",
35
+ }).trim();
36
+ return gitRoot;
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Convert gitignore patterns to glob-compatible patterns
44
+ * @param {string} pattern - A single gitignore pattern
45
+ * @returns {string[]} Array of glob patterns that match gitignore behavior
46
+ */
47
+ function gitignoreToGlobPatterns(pattern) {
48
+ const patterns = [];
49
+
50
+ // Remove leading slash (already handled by gitignore parsing)
51
+ const cleanPattern = pattern.replace(/^\//, "");
52
+
53
+ // If pattern doesn't contain wildcards and doesn't end with /
54
+ // it could match both files and directories
55
+ if (!cleanPattern.includes("*") && !cleanPattern.includes("?") && !cleanPattern.endsWith("/")) {
56
+ // Add patterns to match both file and directory
57
+ patterns.push(cleanPattern); // Exact match
58
+ patterns.push(`${cleanPattern}/**`); // Directory contents
59
+ patterns.push(`**/${cleanPattern}`); // Nested exact match
60
+ patterns.push(`**/${cleanPattern}/**`); // Nested directory contents
61
+ } else if (cleanPattern.endsWith("/")) {
62
+ // Directory-only pattern
63
+ const dirPattern = cleanPattern.slice(0, -1);
64
+ patterns.push(`${dirPattern}/**`);
65
+ patterns.push(`**/${dirPattern}/**`);
66
+ } else {
67
+ // Pattern with wildcards or specific file
68
+ patterns.push(cleanPattern);
69
+ if (!cleanPattern.startsWith("**/")) {
70
+ patterns.push(`**/${cleanPattern}`);
71
+ }
72
+ }
73
+
74
+ return patterns;
75
+ }
76
+
77
+ /**
78
+ * Parse .gitignore content into patterns
79
+ * @param {string} content - .gitignore file content
80
+ * @returns {string[]} Array of ignore patterns converted to glob format
81
+ */
82
+ function parseGitignoreContent(content) {
83
+ const lines = content
84
+ .split("\n")
85
+ .map((line) => line.trim())
86
+ .filter((line) => line && !line.startsWith("#"))
87
+ .map((line) => line.replace(/^\//, "")); // Remove leading slash
88
+
89
+ // Convert each gitignore pattern to glob patterns
90
+ const allPatterns = [];
91
+ for (const line of lines) {
92
+ allPatterns.push(...gitignoreToGlobPatterns(line));
93
+ }
94
+
95
+ return [...new Set(allPatterns)]; // Remove duplicates
96
+ }
97
+
98
+ /**
99
+ * Load .gitignore patterns from multiple directories (current + all parent directories up to git root)
100
+ * @param {string} dir - Directory path (will search up to find all .gitignore files)
101
+ * @returns {string[]|null} Array of merged ignore patterns or null if no .gitignore found
102
+ */
103
+ export async function loadGitignore(dir) {
104
+ // First, check if we're in a git repository
105
+ const inGitRepo = isInGitRepository(dir);
106
+ if (!inGitRepo) {
107
+ // Not in a git repository, just check the current directory
108
+ const gitignorePath = path.join(dir, ".gitignore");
109
+ try {
110
+ await access(gitignorePath);
111
+ const gitignoreContent = await readFile(gitignorePath, "utf8");
112
+ const ignorePatterns = parseGitignoreContent(gitignoreContent);
113
+ return ignorePatterns.length > 0 ? ignorePatterns : null;
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+
119
+ // We're in a git repository, collect all .gitignore files from current dir to git root
120
+ const gitRoot = findGitRoot(dir);
121
+ if (!gitRoot) {
122
+ return null;
123
+ }
124
+
125
+ const allPatterns = [];
126
+ let currentDir = path.resolve(dir);
127
+
128
+ // Collect .gitignore patterns from current directory up to git root
129
+ while (currentDir.startsWith(gitRoot)) {
130
+ const gitignorePath = path.join(currentDir, ".gitignore");
131
+ try {
132
+ await access(gitignorePath);
133
+ const gitignoreContent = await readFile(gitignorePath, "utf8");
134
+ const patterns = parseGitignoreContent(gitignoreContent);
135
+
136
+ // Add patterns with context of which directory they came from
137
+ // Patterns from deeper directories take precedence
138
+ allPatterns.unshift(...patterns);
139
+ } catch {
140
+ // .gitignore doesn't exist in this directory, continue
141
+ }
142
+
143
+ // Move up one directory
144
+ if (currentDir === gitRoot) {
145
+ break;
146
+ }
147
+ currentDir = path.dirname(currentDir);
148
+ }
149
+
150
+ return allPatterns.length > 0 ? [...new Set(allPatterns)] : null;
151
+ }
152
+
153
+ /**
154
+ * Get files using glob patterns
155
+ * @param {string} dir - Directory to search
156
+ * @param {string[]} includePatterns - Include patterns
157
+ * @param {string[]} excludePatterns - Exclude patterns
158
+ * @param {string[]} gitignorePatterns - .gitignore patterns
159
+ * @returns {Promise<string[]>} Array of file paths
160
+ */
161
+ export async function getFilesWithGlob(dir, includePatterns, excludePatterns, gitignorePatterns) {
162
+ // Prepare all ignore patterns
163
+ const allIgnorePatterns = [];
164
+
165
+ if (excludePatterns) {
166
+ allIgnorePatterns.push(...excludePatterns);
167
+ }
168
+
169
+ if (gitignorePatterns) {
170
+ allIgnorePatterns.push(...gitignorePatterns);
171
+ }
172
+
173
+ // Add default exclusions if not already present
174
+ const defaultExclusions = ["node_modules/**", "test/**", "temp/**"];
175
+ for (const exclusion of defaultExclusions) {
176
+ if (!allIgnorePatterns.includes(exclusion)) {
177
+ allIgnorePatterns.push(exclusion);
178
+ }
179
+ }
180
+
181
+ // Convert patterns to be relative to the directory
182
+ const patterns = includePatterns.map((pattern) => {
183
+ // If pattern doesn't start with / or **, make it relative to dir
184
+ if (!pattern.startsWith("/") && !pattern.startsWith("**")) {
185
+ return `**/${pattern}`; // Use ** to search recursively
186
+ }
187
+ return pattern;
188
+ });
189
+
190
+ try {
191
+ const files = await glob(patterns, {
192
+ cwd: dir,
193
+ ignore: allIgnorePatterns.length > 0 ? allIgnorePatterns : undefined,
194
+ absolute: true,
195
+ nodir: true, // Only return files, not directories
196
+ dot: false, // Don't include dot files by default
197
+ gitignore: true, // Enable .gitignore support
198
+ });
199
+
200
+ return files;
201
+ } catch (error) {
202
+ console.warn(`Warning: Error during glob search in ${dir}: ${error.message}`);
203
+ return [];
204
+ }
205
+ }
@@ -1,9 +1,9 @@
1
- import { unified } from "unified";
2
- import remarkParse from "remark-parse";
3
1
  import remarkGfm from "remark-gfm";
4
2
  import remarkLint from "remark-lint";
5
- import { VFile } from "vfile";
3
+ import remarkParse from "remark-parse";
4
+ import { unified } from "unified";
6
5
  import { visit } from "unist-util-visit";
6
+ import { VFile } from "vfile";
7
7
  import { validateMermaidSyntax } from "./mermaid-validator.mjs";
8
8
 
9
9
  /**
@@ -16,10 +16,7 @@ function countTableColumns(line) {
16
16
  const trimmed = line.trim();
17
17
 
18
18
  // Remove leading and trailing pipes if present
19
- const content =
20
- trimmed.startsWith("|") && trimmed.endsWith("|")
21
- ? trimmed.slice(1, -1)
22
- : trimmed;
19
+ const content = trimmed.startsWith("|") && trimmed.endsWith("|") ? trimmed.slice(1, -1) : trimmed;
23
20
 
24
21
  if (!content.trim()) {
25
22
  return 0;
@@ -65,7 +62,7 @@ function countTableColumns(line) {
65
62
  * @param {Array} errorMessages - Array to push error messages to
66
63
  */
67
64
  function checkDeadLinks(markdown, source, allowedLinks, errorMessages) {
68
- const linkRegex = /(?<!\!)\[([^\]]+)\]\(([^)]+)\)/g;
65
+ const linkRegex = /(?<!!)\[([^\]]+)\]\(([^)]+)\)/g;
69
66
  let match;
70
67
 
71
68
  while ((match = linkRegex.exec(markdown)) !== null) {
@@ -77,7 +74,7 @@ function checkDeadLinks(markdown, source, allowedLinks, errorMessages) {
77
74
  if (/^(https?:\/\/|mailto:)/.test(trimLink)) continue;
78
75
 
79
76
  // Preserve anchors
80
- const [path, hash] = trimLink.split("#");
77
+ const [path, _hash] = trimLink.split("#");
81
78
 
82
79
  // Only process relative paths or paths starting with /
83
80
  if (!path) continue;
@@ -85,7 +82,89 @@ function checkDeadLinks(markdown, source, allowedLinks, errorMessages) {
85
82
  // Check if this link is in the allowed links set
86
83
  if (!allowedLinks.has(trimLink)) {
87
84
  errorMessages.push(
88
- `Found a dead link in ${source}: [${match[1]}](${trimLink}), ensure the link exists in the structure plan path`
85
+ `Found a dead link in ${source}: [${match[1]}](${trimLink}), ensure the link exists in the structure plan path`,
86
+ );
87
+ }
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Check code block content for indentation consistency issues
93
+ * @param {Array} codeBlockContent - Array of {line, lineNumber} objects from the code block
94
+ * @param {string} source - Source description for error reporting
95
+ * @param {Array} errorMessages - Array to push error messages to
96
+ */
97
+ function checkCodeBlockIndentation(codeBlockContent, source, errorMessages) {
98
+ if (codeBlockContent.length === 0) return;
99
+
100
+ // Filter out empty lines for base indentation calculation
101
+ const nonEmptyLines = codeBlockContent.filter((item) => item.line.trim().length > 0);
102
+ if (nonEmptyLines.length === 0) return;
103
+
104
+ // Find the base indentation from the first meaningful line
105
+ let baseCodeIndent = null;
106
+ const problematicLines = [];
107
+
108
+ for (const item of nonEmptyLines) {
109
+ const { line, lineNumber } = item;
110
+ const match = line.match(/^(\s*)/);
111
+ const currentIndent = match ? match[1].length : 0;
112
+ const trimmedLine = line.trim();
113
+
114
+ // Skip lines that are clearly comments (common pattern: # comment)
115
+ if (trimmedLine.startsWith("#") && !trimmedLine.includes("=") && !trimmedLine.includes("{")) {
116
+ continue;
117
+ }
118
+
119
+ // Establish base indentation from the first meaningful line
120
+ if (baseCodeIndent === null) {
121
+ baseCodeIndent = currentIndent;
122
+ continue;
123
+ }
124
+
125
+ // Check if current line has less indentation than the base
126
+ // This indicates inconsistent indentation that may cause rendering issues
127
+ if (currentIndent < baseCodeIndent && baseCodeIndent > 0) {
128
+ problematicLines.push({
129
+ lineNumber,
130
+ line: line.trimEnd(),
131
+ currentIndent,
132
+ baseIndent: baseCodeIndent,
133
+ });
134
+ }
135
+ }
136
+
137
+ // Report indentation issues if found
138
+ if (problematicLines.length > 0) {
139
+ // Group consecutive issues to avoid spam
140
+ const groupedIssues = [];
141
+ let currentGroup = [problematicLines[0]];
142
+
143
+ for (let i = 1; i < problematicLines.length; i++) {
144
+ const current = problematicLines[i];
145
+ const previous = problematicLines[i - 1];
146
+
147
+ if (current.lineNumber - previous.lineNumber <= 2) {
148
+ currentGroup.push(current);
149
+ } else {
150
+ groupedIssues.push(currentGroup);
151
+ currentGroup = [current];
152
+ }
153
+ }
154
+ groupedIssues.push(currentGroup);
155
+
156
+ // Report the most significant groups
157
+ for (const group of groupedIssues.slice(0, 2)) {
158
+ // Limit to avoid spam
159
+ const firstIssue = group[0];
160
+ const lineNumbers =
161
+ group.length > 1
162
+ ? `lines ${group[0].lineNumber}-${group[group.length - 1].lineNumber}`
163
+ : `line ${firstIssue.lineNumber}`;
164
+
165
+ const issue = `inconsistent indentation: ${firstIssue.currentIndent} spaces (base: ${firstIssue.baseIndent} spaces)`;
166
+ errorMessages.push(
167
+ `Found code block with inconsistent indentation in ${source} at ${lineNumbers}: ${issue}. This may cause rendering issues`,
89
168
  );
90
169
  }
91
170
  }
@@ -103,10 +182,9 @@ function checkContentStructure(markdown, source, errorMessages) {
103
182
 
104
183
  // State variables for different checks
105
184
  let inCodeBlock = false;
106
- let codeBlockIndentLevel = 0;
107
- let codeBlockStartLine = 0;
108
185
  let inAnyCodeBlock = false;
109
186
  let anyCodeBlockStartLine = 0;
187
+ let codeBlockContent = [];
110
188
 
111
189
  for (let i = 0; i < lines.length; i++) {
112
190
  const line = lines[i];
@@ -118,17 +196,28 @@ function checkContentStructure(markdown, source, errorMessages) {
118
196
  // Starting a new code block
119
197
  inAnyCodeBlock = true;
120
198
  anyCodeBlockStartLine = lineNumber;
199
+ inCodeBlock = true;
200
+ codeBlockContent = [];
121
201
  } else {
122
202
  // Ending the code block
123
203
  inAnyCodeBlock = false;
204
+
205
+ if (inCodeBlock) {
206
+ // Check code block content for indentation issues
207
+ checkCodeBlockIndentation(codeBlockContent, source, errorMessages);
208
+ inCodeBlock = false;
209
+ }
124
210
  }
211
+ } else if (inCodeBlock) {
212
+ // Collect code block content for indentation analysis
213
+ codeBlockContent.push({ line, lineNumber });
125
214
  }
126
215
  }
127
216
 
128
217
  // Check for incomplete code blocks (started but not closed)
129
218
  if (inAnyCodeBlock) {
130
219
  errorMessages.push(
131
- `Found incomplete code block in ${source} starting at line ${anyCodeBlockStartLine}: code block opened with \`\`\` but never closed. Please return the complete content`
220
+ `Found incomplete code block in ${source} starting at line ${anyCodeBlockStartLine}: code block opened with \`\`\` but never closed. Please return the complete content`,
132
221
  );
133
222
  }
134
223
 
@@ -136,19 +225,15 @@ function checkContentStructure(markdown, source, errorMessages) {
136
225
  const newlineCount = (markdown.match(/\n/g) || []).length;
137
226
  if (newlineCount === 0 && markdown.trim().length > 0) {
138
227
  errorMessages.push(
139
- `Found single line content in ${source}: content appears to be on only one line, check for missing line breaks`
228
+ `Found single line content in ${source}: content appears to be on only one line, check for missing line breaks`,
140
229
  );
141
230
  }
142
231
 
143
232
  // Check if content ends with proper punctuation (indicating completeness)
144
233
  const trimmedText = markdown.trim();
145
- if (
146
- trimmedText.length > 0 &&
147
- !trimmedText.endsWith(".") &&
148
- !trimmedText.endsWith("。")
149
- ) {
234
+ if (trimmedText.length > 0 && !trimmedText.endsWith(".") && !trimmedText.endsWith("。")) {
150
235
  errorMessages.push(
151
- `Found incomplete content in ${source}: content does not end with proper punctuation (. or 。). Please return the complete content`
236
+ `Found incomplete content in ${source}: content does not end with proper punctuation (. or 。). Please return the complete content`,
152
237
  );
153
238
  }
154
239
  }
@@ -161,11 +246,7 @@ function checkContentStructure(markdown, source, errorMessages) {
161
246
  * @param {Array} [options.allowedLinks] - Set of allowed links for link validation
162
247
  * @returns {Promise<Array<string>>} - Array of error messages in check-detail-result format
163
248
  */
164
- export async function checkMarkdown(
165
- markdown,
166
- source = "content",
167
- options = {}
168
- ) {
249
+ export async function checkMarkdown(markdown, source = "content", options = {}) {
169
250
  const file = new VFile({ value: markdown, path: source });
170
251
  const errorMessages = [];
171
252
 
@@ -224,15 +305,14 @@ export async function checkMarkdown(
224
305
  // Check for mermaid syntax errors
225
306
  mermaidChecks.push(
226
307
  validateMermaidSyntax(node.value).catch((error) => {
227
- const errorMessage =
228
- error?.message || String(error) || "Unknown mermaid syntax error";
308
+ const errorMessage = error?.message || String(error) || "Unknown mermaid syntax error";
229
309
 
230
310
  // Format mermaid error in check-detail-result style
231
311
  const line = node.position?.start?.line || "unknown";
232
312
  errorMessages.push(
233
- `Found Mermaid syntax error in ${source} at line ${line}: ${errorMessage}`
313
+ `Found Mermaid syntax error in ${source} at line ${line}: ${errorMessage}`,
234
314
  );
235
- })
315
+ }),
236
316
  );
237
317
 
238
318
  // Check for specific mermaid rendering issues
@@ -240,38 +320,31 @@ export async function checkMarkdown(
240
320
  const line = node.position?.start?.line || "unknown";
241
321
 
242
322
  // Check for backticks in node labels
243
- const nodeLabelRegex =
244
- /[A-Za-z0-9_]+\["([^"]*`[^"]*)"\]|[A-Za-z0-9_]+{"([^}]*`[^}]*)"}/g;
323
+ const nodeLabelRegex = /[A-Za-z0-9_]+\["([^"]*`[^"]*)"\]|[A-Za-z0-9_]+{"([^}]*`[^}]*)"}/g;
245
324
  let match;
246
325
  while ((match = nodeLabelRegex.exec(mermaidContent)) !== null) {
247
326
  const label = match[1] || match[2];
248
327
  errorMessages.push(
249
- `Found backticks in Mermaid node label in ${source} at line ${line}: "${label}" - backticks in node labels cause rendering issues in Mermaid diagrams`
328
+ `Found backticks in Mermaid node label in ${source} at line ${line}: "${label}" - backticks in node labels cause rendering issues in Mermaid diagrams`,
250
329
  );
251
330
  }
252
331
 
253
332
  // Check for numbered list format in edge descriptions
254
333
  const edgeDescriptionRegex = /--\s*"([^"]*)"\s*-->/g;
255
334
  let edgeMatch;
256
- while (
257
- (edgeMatch = edgeDescriptionRegex.exec(mermaidContent)) !== null
258
- ) {
335
+ while ((edgeMatch = edgeDescriptionRegex.exec(mermaidContent)) !== null) {
259
336
  const description = edgeMatch[1];
260
337
  if (/^\d+\.\s/.test(description)) {
261
338
  errorMessages.push(
262
- `Unsupported markdown: list - Found numbered list format in Mermaid edge description in ${source} at line ${line}: "${description}" - numbered lists in edge descriptions are not supported`
339
+ `Unsupported markdown: list - Found numbered list format in Mermaid edge description in ${source} at line ${line}: "${description}" - numbered lists in edge descriptions are not supported`,
263
340
  );
264
341
  }
265
342
  }
266
343
 
267
344
  // Check for special characters in node labels that should be quoted
268
- const nodeWithSpecialCharsRegex =
269
- /([A-Za-z0-9_]+)\[([^\]]*[(){}:;,\-\s\.][^\]]*)\]/g;
345
+ const nodeWithSpecialCharsRegex = /([A-Za-z0-9_]+)\[([^\]]*[(){}:;,\-\s.][^\]]*)\]/g;
270
346
  let specialCharMatch;
271
- while (
272
- (specialCharMatch =
273
- nodeWithSpecialCharsRegex.exec(mermaidContent)) !== null
274
- ) {
347
+ while ((specialCharMatch = nodeWithSpecialCharsRegex.exec(mermaidContent)) !== null) {
275
348
  const nodeId = specialCharMatch[1];
276
349
  const label = specialCharMatch[2];
277
350
 
@@ -279,15 +352,13 @@ export async function checkMarkdown(
279
352
  if (!/^".*"$/.test(label)) {
280
353
  // List of characters that typically need quoting
281
354
  const specialChars = ["(", ")", "{", "}", ":", ";", ",", "-", "."];
282
- const foundSpecialChars = specialChars.filter((char) =>
283
- label.includes(char)
284
- );
355
+ const foundSpecialChars = specialChars.filter((char) => label.includes(char));
285
356
 
286
357
  if (foundSpecialChars.length > 0) {
287
358
  errorMessages.push(
288
359
  `Found unquoted special characters in Mermaid node label in ${source} at line ${line}: "${label}" contains ${foundSpecialChars.join(
289
- ", "
290
- )} - node labels with special characters should be quoted like ${nodeId}["${label}"]`
360
+ ", ",
361
+ )} - node labels with special characters should be quoted like ${nodeId}["${label}"]`,
291
362
  );
292
363
  }
293
364
  }
@@ -317,7 +388,7 @@ export async function checkMarkdown(
317
388
  errorMessages.push(
318
389
  `Found table separator with mismatched column count in ${source} at line ${
319
390
  i + 1
320
- }: separator has ${separatorColumns} columns but header has ${headerColumns} columns - this causes table rendering issues`
391
+ }: separator has ${separatorColumns} columns but header has ${headerColumns} columns - this causes table rendering issues`,
321
392
  );
322
393
  }
323
394
 
@@ -330,7 +401,7 @@ export async function checkMarkdown(
330
401
  errorMessages.push(
331
402
  `Found table data row with mismatched column count in ${source} at line ${
332
403
  i + 2
333
- }: data row has ${dataColumns} columns but separator defines ${separatorColumns} columns - this causes table rendering issues`
404
+ }: data row has ${dataColumns} columns but separator defines ${separatorColumns} columns - this causes table rendering issues`,
334
405
  );
335
406
  }
336
407
  }
@@ -349,7 +420,7 @@ export async function checkMarkdown(
349
420
  // Format messages in check-detail-result style
350
421
  file.messages.forEach((message) => {
351
422
  const line = message.line || "unknown";
352
- const column = message.column || "unknown";
423
+ const _column = message.column || "unknown";
353
424
  const reason = message.reason || "Unknown markdown issue";
354
425
  const ruleId = message.ruleId || message.source || "markdown";
355
426
 
@@ -366,21 +437,17 @@ export async function checkMarkdown(
366
437
  // Format error message similar to check-detail-result style
367
438
  if (line !== "unknown") {
368
439
  errorMessages.push(
369
- `Found ${errorType} issue in ${source} at line ${line}: ${reason} (${ruleId})`
440
+ `Found ${errorType} issue in ${source} at line ${line}: ${reason} (${ruleId})`,
370
441
  );
371
442
  } else {
372
- errorMessages.push(
373
- `Found ${errorType} issue in ${source}: ${reason} (${ruleId})`
374
- );
443
+ errorMessages.push(`Found ${errorType} issue in ${source}: ${reason} (${ruleId})`);
375
444
  }
376
445
  });
377
446
 
378
447
  return errorMessages;
379
448
  } catch (error) {
380
449
  // Handle any unexpected errors during processing
381
- errorMessages.push(
382
- `Found markdown processing error in ${source}: ${error.message}`
383
- );
450
+ errorMessages.push(`Found markdown processing error in ${source}: ${error.message}`);
384
451
  return errorMessages;
385
452
  }
386
453
  }
@@ -3,10 +3,7 @@
3
3
  * Provides concurrent-safe validation with isolated worker environments
4
4
  */
5
5
 
6
- import {
7
- getMermaidWorkerPool,
8
- shutdownMermaidWorkerPool,
9
- } from "./mermaid-worker-pool.mjs";
6
+ import { getMermaidWorkerPool, shutdownMermaidWorkerPool } from "./mermaid-worker-pool.mjs";
10
7
 
11
8
  /**
12
9
  * Worker-based mermaid validation - DEPRECATED but kept for compatibility
@@ -55,17 +52,15 @@ export function validateBasicMermaidSyntax(content) {
55
52
  ];
56
53
 
57
54
  const firstLine = trimmedContent.split("\n")[0].trim();
58
- const hasValidType = validDiagramTypes.some((type) =>
59
- firstLine.includes(type)
60
- );
55
+ const hasValidType = validDiagramTypes.some((type) => firstLine.includes(type));
61
56
 
62
57
  if (!hasValidType) {
63
58
  throw new Error("Invalid or missing diagram type");
64
59
  }
65
60
 
66
61
  // Basic bracket matching
67
- const openBrackets = (content.match(/[\[\{\(]/g) || []).length;
68
- const closeBrackets = (content.match(/[\]\}\)]/g) || []).length;
62
+ const openBrackets = (content.match(/[[{(]/g) || []).length;
63
+ const closeBrackets = (content.match(/[\]})]/g) || []).length;
69
64
 
70
65
  if (openBrackets !== closeBrackets) {
71
66
  throw new Error("Unmatched brackets in diagram");
@@ -125,7 +120,7 @@ export async function validateMermaidSyntax(content) {
125
120
  // Fall back to basic validation for environment issues
126
121
  console.warn(
127
122
  "Worker-based mermaid validation failed, falling back to basic validation:",
128
- errorMsg
123
+ errorMsg,
129
124
  );
130
125
  return validateBasicMermaidSyntax(content);
131
126
  }
@@ -5,9 +5,9 @@
5
5
  * Manages worker threads for concurrent mermaid validation
6
6
  */
7
7
 
8
- import { Worker } from "worker_threads";
9
- import { fileURLToPath } from "url";
10
- import { dirname, join } from "path";
8
+ import { dirname, join } from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ import { Worker } from "node:worker_threads";
11
11
 
12
12
  const __filename = fileURLToPath(import.meta.url);
13
13
  const __dirname = dirname(__filename);
@@ -51,18 +51,14 @@ class SimpleMermaidWorkerPool {
51
51
  // Handle worker errors more gracefully
52
52
  worker.on("error", (error) => {
53
53
  if (worker.currentRequest) {
54
- worker.currentRequest.reject(
55
- new Error(`Worker error: ${error.message}`)
56
- );
54
+ worker.currentRequest.reject(new Error(`Worker error: ${error.message}`));
57
55
  worker.currentRequest = null;
58
56
  }
59
57
  });
60
58
 
61
- worker.on("exit", (code) => {
59
+ worker.on("exit", (_code) => {
62
60
  if (worker.currentRequest) {
63
- worker.currentRequest.reject(
64
- new Error("Worker exited unexpectedly")
65
- );
61
+ worker.currentRequest.reject(new Error("Worker exited unexpectedly"));
66
62
  worker.currentRequest = null;
67
63
  }
68
64
  });
@@ -212,7 +208,7 @@ class SimpleMermaidWorkerPool {
212
208
  const terminationPromises = this.workers.map(async (worker) => {
213
209
  try {
214
210
  await worker.terminate();
215
- } catch (error) {
211
+ } catch (_error) {
216
212
  // Ignore termination errors
217
213
  }
218
214
  });