gtx-cli 2.5.17 → 2.5.18

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.5.18
4
+
5
+ ### Patch Changes
6
+
7
+ - [#851](https://github.com/generaltranslation/gt/pull/851) [`cf5f0e3`](https://github.com/generaltranslation/gt/commit/cf5f0e3f1537c304b7ea5703714ffb4956a7f6f4) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Skip anchor ID fallback in codeblocks to avoid adding anchors to comments
8
+
3
9
  ## 2.5.17
4
10
 
5
11
  ### Patch Changes
@@ -30,6 +30,53 @@ function extractHeadingText(heading) {
30
30
  });
31
31
  return text;
32
32
  }
33
+ /**
34
+ * Simple line-by-line heading extractor that skips fenced code blocks.
35
+ * Used as a fallback when MDX parsing fails.
36
+ */
37
+ function extractHeadingsWithFallback(mdxContent) {
38
+ const headings = [];
39
+ const lines = mdxContent.split('\n');
40
+ let position = 0;
41
+ let inFence = false;
42
+ let fenceChar = null;
43
+ for (const line of lines) {
44
+ const fenceMatch = line.match(/^(\s*)(`{3,}|~{3,})/);
45
+ if (fenceMatch) {
46
+ const fenceString = fenceMatch[2];
47
+ if (!inFence) {
48
+ inFence = true;
49
+ fenceChar = fenceString;
50
+ }
51
+ else if (fenceChar &&
52
+ fenceString[0] === fenceChar[0] &&
53
+ fenceString.length >= fenceChar.length) {
54
+ inFence = false;
55
+ fenceChar = null;
56
+ }
57
+ continue;
58
+ }
59
+ if (inFence) {
60
+ continue;
61
+ }
62
+ const headingMatch = line.match(/^(#{1,6})\s+(.*)$/);
63
+ if (!headingMatch) {
64
+ continue;
65
+ }
66
+ const hashes = headingMatch[1];
67
+ const rawText = headingMatch[2];
68
+ const { cleanedText, explicitId } = parseHeadingContent(rawText);
69
+ if (cleanedText || explicitId) {
70
+ headings.push({
71
+ text: cleanedText,
72
+ level: hashes.length,
73
+ slug: explicitId ?? generateSlug(cleanedText),
74
+ position: position++,
75
+ });
76
+ }
77
+ }
78
+ return headings;
79
+ }
33
80
  function parseHeadingContent(text) {
34
81
  // Support both {#id} and escaped \{#id\} forms
35
82
  const anchorMatch = text.match(/(\\\{#([^}]+)\\\}|\{#([^}]+)\})\s*$/);
@@ -67,25 +114,8 @@ export function extractHeadingInfo(mdxContent) {
67
114
  }
68
115
  catch (error) {
69
116
  console.warn(`Failed to parse MDX content: ${error instanceof Error ? error.message : String(error)}`);
70
- // Fallback: simple regex-based extraction to keep IDs usable
71
- const fallbackHeadings = [];
72
- const headingRegex = /^(#{1,6})\s+(.*)$/gm;
73
- let position = 0;
74
- let match;
75
- while ((match = headingRegex.exec(mdxContent)) !== null) {
76
- const hashes = match[1];
77
- const rawText = match[2];
78
- const { cleanedText, explicitId } = parseHeadingContent(rawText);
79
- if (cleanedText || explicitId) {
80
- fallbackHeadings.push({
81
- text: cleanedText,
82
- level: hashes.length,
83
- slug: explicitId ?? generateSlug(cleanedText),
84
- position: position++,
85
- });
86
- }
87
- }
88
- return fallbackHeadings;
117
+ // Fallback: line-by-line extraction skipping fenced code blocks
118
+ return extractHeadingsWithFallback(mdxContent);
89
119
  }
90
120
  let position = 0;
91
121
  visit(processedAst, 'heading', (heading) => {
@@ -270,25 +300,52 @@ function applyInlineIds(translatedContent, idMappings, escapeAnchors) {
270
300
  */
271
301
  function applyInlineIdsStringFallback(translatedContent, idMappings, escapeAnchors) {
272
302
  let headingIndex = 0;
273
- return translatedContent.replace(/^(#{1,6}\s+)(.*)$/gm, (match, prefix, text) => {
303
+ let inFence = false;
304
+ let fenceChar = null;
305
+ const processedLines = translatedContent.split('\n').map((line) => {
306
+ const fenceMatch = line.match(/^(\s*)(`{3,}|~{3,})/);
307
+ if (fenceMatch) {
308
+ const fenceString = fenceMatch[2];
309
+ if (!inFence) {
310
+ inFence = true;
311
+ fenceChar = fenceString;
312
+ }
313
+ else if (fenceChar &&
314
+ fenceString[0] === fenceChar[0] &&
315
+ fenceString.length >= fenceChar.length) {
316
+ inFence = false;
317
+ fenceChar = null;
318
+ }
319
+ return line;
320
+ }
321
+ if (inFence) {
322
+ return line;
323
+ }
324
+ const headingMatch = line.match(/^(#{1,6}\s+)(.*)$/);
325
+ if (!headingMatch) {
326
+ return line;
327
+ }
328
+ const prefix = headingMatch[1];
329
+ const text = headingMatch[2];
274
330
  const id = idMappings.get(headingIndex++);
275
331
  if (!id) {
276
- return match;
332
+ return line;
277
333
  }
278
334
  const hasEscaped = /\\\{#[^}]+\\\}\s*$/.test(text);
279
335
  const hasUnescaped = /\{#[^}]+\}\s*$/.test(text);
280
336
  if (hasEscaped) {
281
- return match;
337
+ return line;
282
338
  }
283
339
  if (hasUnescaped) {
284
340
  if (!escapeAnchors) {
285
- return match;
341
+ return line;
286
342
  }
287
343
  return `${prefix}${text.replace(/\{#([^}]+)\}\s*$/, '\\\\{#$1\\\\}')}`;
288
344
  }
289
345
  const suffix = escapeAnchors ? ` \\{#${id}\\}` : ` {#${id}}`;
290
346
  return `${prefix}${text}${suffix}`;
291
347
  });
348
+ return processedLines.join('\n');
292
349
  }
293
350
  /**
294
351
  * Wraps headings in divs with IDs (Mintlify approach)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "2.5.17",
3
+ "version": "2.5.18",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "files": [