gtx-cli 2.1.17 → 2.1.19
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/dist/formats/files/translate.js +30 -7
- package/dist/utils/addExplicitAnchorIds.js +7 -4
- package/dist/utils/encodePlaceholders.d.ts +11 -0
- package/dist/utils/encodePlaceholders.js +30 -0
- package/dist/utils/localizeStaticImports.js +9 -13
- package/dist/utils/localizeStaticUrls.js +17 -4
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
+
## 2.1.19
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#641](https://github.com/generaltranslation/gt/pull/641) [`4c67f77`](https://github.com/generaltranslation/gt/commit/4c67f775ee892b47eebcc3178c00ad6547a84d84) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Encoding placeholders that break MDX parse
|
|
8
|
+
|
|
9
|
+
## 2.1.18
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#637](https://github.com/generaltranslation/gt/pull/637) [`9c40a3c`](https://github.com/generaltranslation/gt/commit/9c40a3c729bf690381959679078c11c9c29bcdf2) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Skipping over empty files when sending for translation
|
|
14
|
+
|
|
3
15
|
## 2.1.17
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -35,30 +35,46 @@ export async function aggregateFiles(settings) {
|
|
|
35
35
|
else {
|
|
36
36
|
dataFormat = 'JSX';
|
|
37
37
|
}
|
|
38
|
-
const jsonFiles = filePaths.json
|
|
38
|
+
const jsonFiles = filePaths.json
|
|
39
|
+
.map((filePath) => {
|
|
39
40
|
const content = readFile(filePath);
|
|
40
|
-
const parsedJson = parseJson(content, filePath, settings.options || {}, settings.defaultLocale);
|
|
41
41
|
const relativePath = getRelative(filePath);
|
|
42
|
+
const parsedJson = parseJson(content, filePath, settings.options || {}, settings.defaultLocale);
|
|
42
43
|
return {
|
|
43
44
|
content: parsedJson,
|
|
44
45
|
fileName: relativePath,
|
|
45
46
|
fileFormat: 'JSON',
|
|
46
47
|
dataFormat,
|
|
47
48
|
};
|
|
49
|
+
})
|
|
50
|
+
.filter((file) => {
|
|
51
|
+
if (!file || typeof file.content !== 'string' || !file.content.trim()) {
|
|
52
|
+
logWarning(`Skipping ${file?.fileName ?? 'unknown'}: JSON file is empty`);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
48
56
|
});
|
|
49
57
|
allFiles.push(...jsonFiles);
|
|
50
58
|
}
|
|
51
59
|
// Process YAML files
|
|
52
60
|
if (filePaths.yaml) {
|
|
53
|
-
const yamlFiles = filePaths.yaml
|
|
61
|
+
const yamlFiles = filePaths.yaml
|
|
62
|
+
.map((filePath) => {
|
|
54
63
|
const content = readFile(filePath);
|
|
55
|
-
const { content: parsedYaml, fileFormat } = parseYaml(content, filePath, settings.options || {});
|
|
56
64
|
const relativePath = getRelative(filePath);
|
|
65
|
+
const { content: parsedYaml, fileFormat } = parseYaml(content, filePath, settings.options || {});
|
|
57
66
|
return {
|
|
58
67
|
content: parsedYaml,
|
|
59
68
|
fileName: relativePath,
|
|
60
69
|
fileFormat,
|
|
61
70
|
};
|
|
71
|
+
})
|
|
72
|
+
.filter((file) => {
|
|
73
|
+
if (!file || typeof file.content !== 'string' || !file.content.trim()) {
|
|
74
|
+
logWarning(`Skipping ${file?.fileName ?? 'unknown'}: YAML file is empty`);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
62
78
|
});
|
|
63
79
|
allFiles.push(...yamlFiles);
|
|
64
80
|
}
|
|
@@ -73,8 +89,7 @@ export async function aggregateFiles(settings) {
|
|
|
73
89
|
if (fileType === 'mdx') {
|
|
74
90
|
const validation = isValidMdx(content, filePath);
|
|
75
91
|
if (!validation.isValid) {
|
|
76
|
-
|
|
77
|
-
logWarning(`Skipping ${relativePath}: MDX file is not AST parsable${errorMsg}`);
|
|
92
|
+
logWarning(`Skipping ${relativePath}: MDX file is not AST parsable${validation.error ? `: ${validation.error}` : ''}`);
|
|
78
93
|
return null;
|
|
79
94
|
}
|
|
80
95
|
}
|
|
@@ -85,7 +100,15 @@ export async function aggregateFiles(settings) {
|
|
|
85
100
|
fileFormat: fileType.toUpperCase(),
|
|
86
101
|
};
|
|
87
102
|
})
|
|
88
|
-
.filter((file) =>
|
|
103
|
+
.filter((file) => {
|
|
104
|
+
if (!file ||
|
|
105
|
+
typeof file.content !== 'string' ||
|
|
106
|
+
!file.content.trim()) {
|
|
107
|
+
logWarning(`Skipping ${file?.fileName ?? 'unknown'}: File is empty after sanitization`);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
});
|
|
89
112
|
allFiles.push(...files);
|
|
90
113
|
}
|
|
91
114
|
}
|
|
@@ -5,6 +5,7 @@ import remarkFrontmatter from 'remark-frontmatter';
|
|
|
5
5
|
import remarkStringify from 'remark-stringify';
|
|
6
6
|
import { visit } from 'unist-util-visit';
|
|
7
7
|
import { logWarning } from '../console/logging.js';
|
|
8
|
+
import { encodeAnglePlaceholders } from './encodePlaceholders.js';
|
|
8
9
|
/**
|
|
9
10
|
* Generates a slug from heading text
|
|
10
11
|
*/
|
|
@@ -173,6 +174,9 @@ function applyInlineIds(translatedContent, idMappings) {
|
|
|
173
174
|
// Convert the modified AST back to MDX string
|
|
174
175
|
try {
|
|
175
176
|
const stringifyProcessor = unified()
|
|
177
|
+
.use(remarkFrontmatter, ['yaml', 'toml'])
|
|
178
|
+
.use(remarkMdx)
|
|
179
|
+
.use(encodeAnglePlaceholders)
|
|
176
180
|
.use(remarkStringify, {
|
|
177
181
|
bullet: '-',
|
|
178
182
|
emphasis: '_',
|
|
@@ -186,10 +190,9 @@ function applyInlineIds(translatedContent, idMappings) {
|
|
|
186
190
|
return node.value;
|
|
187
191
|
},
|
|
188
192
|
},
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
let content = stringifyProcessor.stringify(processedAst);
|
|
193
|
+
});
|
|
194
|
+
const outTree = stringifyProcessor.runSync(processedAst);
|
|
195
|
+
let content = stringifyProcessor.stringify(outTree);
|
|
193
196
|
// Handle newline formatting to match original input
|
|
194
197
|
if (content.endsWith('\n') && !translatedContent.endsWith('\n')) {
|
|
195
198
|
content = content.slice(0, -1);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Plugin } from 'unified';
|
|
2
|
+
import type { Root } from 'mdast';
|
|
3
|
+
/**
|
|
4
|
+
* Re-encode angle-bracket placeholders like <accountName> -> <accountName>
|
|
5
|
+
* in plain text, to prevent MDX JSX parsing on re-parse.
|
|
6
|
+
*
|
|
7
|
+
* - Only touches Text nodes.
|
|
8
|
+
* - Idempotent: already-encoded entities are left unchanged.
|
|
9
|
+
* - Does NOT affect actual MDX/JSX elements because those are not text nodes.
|
|
10
|
+
*/
|
|
11
|
+
export declare const encodeAnglePlaceholders: Plugin<[], Root>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { findAndReplace } from 'mdast-util-find-and-replace';
|
|
2
|
+
const IGNORE_PARENTS = [
|
|
3
|
+
'code',
|
|
4
|
+
'inlineCode',
|
|
5
|
+
'mdxFlowExpression',
|
|
6
|
+
'mdxTextExpression',
|
|
7
|
+
'mdxjsEsm',
|
|
8
|
+
'yaml',
|
|
9
|
+
'toml',
|
|
10
|
+
'math',
|
|
11
|
+
'inlineMath',
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Re-encode angle-bracket placeholders like <accountName> -> <accountName>
|
|
15
|
+
* in plain text, to prevent MDX JSX parsing on re-parse.
|
|
16
|
+
*
|
|
17
|
+
* - Only touches Text nodes.
|
|
18
|
+
* - Idempotent: already-encoded entities are left unchanged.
|
|
19
|
+
* - Does NOT affect actual MDX/JSX elements because those are not text nodes.
|
|
20
|
+
*/
|
|
21
|
+
export const encodeAnglePlaceholders = function () {
|
|
22
|
+
return (tree) => {
|
|
23
|
+
findAndReplace(tree, [
|
|
24
|
+
[
|
|
25
|
+
/<([A-Za-z][\w.-]*)>/g, // <content>, <con-tent>, <con.tent>, <con_tent>
|
|
26
|
+
(_match, name) => `<${name}>`,
|
|
27
|
+
],
|
|
28
|
+
], { ignore: IGNORE_PARENTS });
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -8,17 +8,6 @@ import remarkMdx from 'remark-mdx';
|
|
|
8
8
|
import remarkFrontmatter from 'remark-frontmatter';
|
|
9
9
|
import { visit } from 'unist-util-visit';
|
|
10
10
|
const { isMatch } = micromatch;
|
|
11
|
-
/**
|
|
12
|
-
* Checks if a file exists at the given path
|
|
13
|
-
*/
|
|
14
|
-
function fileExists(filePath) {
|
|
15
|
-
try {
|
|
16
|
-
return fs.existsSync(filePath);
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
11
|
/**
|
|
23
12
|
* Localizes static imports in content files.
|
|
24
13
|
* Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)
|
|
@@ -55,6 +44,10 @@ export default async function localizeStaticImports(settings) {
|
|
|
55
44
|
}
|
|
56
45
|
if (defaultLocaleFiles.length > 0) {
|
|
57
46
|
const defaultPromise = Promise.all(defaultLocaleFiles.map(async (filePath) => {
|
|
47
|
+
// Check if file exists before processing
|
|
48
|
+
if (!fs.existsSync(filePath)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
58
51
|
// Get file content
|
|
59
52
|
const fileContent = await fs.promises.readFile(filePath, 'utf8');
|
|
60
53
|
// Localize the file using default locale
|
|
@@ -72,6 +65,10 @@ export default async function localizeStaticImports(settings) {
|
|
|
72
65
|
const targetFiles = Object.values(filesMap).filter((path) => path.endsWith('.md') || path.endsWith('.mdx'));
|
|
73
66
|
// Replace the placeholder path with the target path
|
|
74
67
|
await Promise.all(targetFiles.map(async (filePath) => {
|
|
68
|
+
// Check if file exists before processing
|
|
69
|
+
if (!fs.existsSync(filePath)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
75
72
|
// Get file content
|
|
76
73
|
const fileContent = await fs.promises.readFile(filePath, 'utf8');
|
|
77
74
|
// Localize the file
|
|
@@ -211,8 +208,7 @@ function transformImportPath(fullPath, patternHead, targetLocale, defaultLocale,
|
|
|
211
208
|
const currentDir = path.dirname(currentFilePath);
|
|
212
209
|
resolvedPath = path.resolve(currentDir, newPath);
|
|
213
210
|
}
|
|
214
|
-
|
|
215
|
-
if (!pathExists) {
|
|
211
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
216
212
|
return null;
|
|
217
213
|
}
|
|
218
214
|
}
|
|
@@ -7,6 +7,7 @@ import remarkMdx from 'remark-mdx';
|
|
|
7
7
|
import remarkFrontmatter from 'remark-frontmatter';
|
|
8
8
|
import remarkStringify from 'remark-stringify';
|
|
9
9
|
import { visit } from 'unist-util-visit';
|
|
10
|
+
import { encodeAnglePlaceholders } from './encodePlaceholders.js';
|
|
10
11
|
const { isMatch } = micromatch;
|
|
11
12
|
/**
|
|
12
13
|
* Localizes static urls in content files.
|
|
@@ -49,6 +50,10 @@ export default async function localizeStaticUrls(settings, targetLocales) {
|
|
|
49
50
|
}
|
|
50
51
|
if (defaultLocaleFiles.length > 0) {
|
|
51
52
|
const defaultPromise = Promise.all(defaultLocaleFiles.map(async (filePath) => {
|
|
53
|
+
// Check if file exists before processing
|
|
54
|
+
if (!fs.existsSync(filePath)) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
52
57
|
// Get file content
|
|
53
58
|
const fileContent = await fs.promises.readFile(filePath, 'utf8');
|
|
54
59
|
// Localize the file using default locale
|
|
@@ -366,6 +371,9 @@ function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLo
|
|
|
366
371
|
let content;
|
|
367
372
|
try {
|
|
368
373
|
const stringifyProcessor = unified()
|
|
374
|
+
.use(remarkFrontmatter, ['yaml', 'toml'])
|
|
375
|
+
.use(remarkMdx)
|
|
376
|
+
.use(encodeAnglePlaceholders)
|
|
369
377
|
.use(remarkStringify, {
|
|
370
378
|
bullet: '-',
|
|
371
379
|
emphasis: '_',
|
|
@@ -373,10 +381,15 @@ function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLo
|
|
|
373
381
|
rule: '-',
|
|
374
382
|
ruleRepetition: 3,
|
|
375
383
|
ruleSpaces: false,
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
384
|
+
handlers: {
|
|
385
|
+
// Handler to prevent escaping (avoids '<' -> '\<')
|
|
386
|
+
text(node) {
|
|
387
|
+
return node.value;
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
const outTree = stringifyProcessor.runSync(processedAst);
|
|
392
|
+
content = stringifyProcessor.stringify(outTree);
|
|
380
393
|
}
|
|
381
394
|
catch (error) {
|
|
382
395
|
console.warn(`Failed to stringify MDX content: ${error instanceof Error ? error.message : String(error)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gtx-cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.19",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": "dist/main.js",
|
|
6
6
|
"files": [
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
"json-pointer": "^0.6.2",
|
|
92
92
|
"jsonpath-plus": "^10.3.0",
|
|
93
93
|
"jsonpointer": "^5.0.1",
|
|
94
|
+
"mdast-util-find-and-replace": "^3.0.2",
|
|
94
95
|
"micromatch": "^4.0.8",
|
|
95
96
|
"open": "^10.1.1",
|
|
96
97
|
"ora": "^8.2.0",
|