gtx-cli 2.5.0-alpha.0 → 2.5.0-alpha.1
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 +13 -0
- package/dist/api/collectUserEditDiffs.d.ts +2 -7
- package/dist/api/collectUserEditDiffs.js +33 -78
- package/dist/api/downloadFileBatch.d.ts +11 -10
- package/dist/api/downloadFileBatch.js +120 -127
- package/dist/api/saveLocalEdits.js +18 -15
- package/dist/cli/base.js +1 -1
- package/dist/cli/commands/stage.d.ts +8 -2
- package/dist/cli/commands/stage.js +25 -7
- package/dist/cli/commands/translate.d.ts +4 -2
- package/dist/cli/commands/translate.js +5 -6
- package/dist/cli/flags.js +4 -1
- package/dist/config/generateSettings.js +10 -0
- package/dist/console/colors.d.ts +0 -1
- package/dist/console/colors.js +0 -3
- package/dist/console/index.d.ts +0 -6
- package/dist/console/index.js +2 -13
- package/dist/console/logging.d.ts +1 -1
- package/dist/console/logging.js +3 -4
- package/dist/formats/files/translate.d.ts +2 -2
- package/dist/formats/files/translate.js +31 -5
- package/dist/fs/config/downloadedVersions.d.ts +10 -3
- package/dist/fs/config/downloadedVersions.js +8 -0
- package/dist/fs/config/updateVersions.d.ts +2 -1
- package/dist/git/branches.d.ts +7 -0
- package/dist/git/branches.js +88 -0
- package/dist/react/{jsx/utils/jsxParsing → data-_gt}/addGTIdentifierToSyntaxTree.d.ts +1 -2
- package/dist/react/{jsx/utils/jsxParsing → data-_gt}/addGTIdentifierToSyntaxTree.js +6 -30
- package/dist/react/jsx/evaluateJsx.d.ts +6 -5
- package/dist/react/jsx/evaluateJsx.js +4 -32
- package/dist/react/jsx/trimJsxStringChildren.d.ts +7 -0
- package/dist/react/jsx/trimJsxStringChildren.js +122 -0
- package/dist/react/jsx/utils/constants.d.ts +0 -2
- package/dist/react/jsx/utils/constants.js +2 -11
- package/dist/react/jsx/utils/parseJsx.d.ts +21 -0
- package/dist/react/jsx/utils/parseJsx.js +259 -0
- package/dist/react/jsx/utils/parseStringFunction.js +141 -4
- package/dist/react/parse/createInlineUpdates.js +70 -19
- package/dist/types/branch.d.ts +14 -0
- package/dist/types/branch.js +1 -0
- package/dist/types/data.d.ts +1 -1
- package/dist/types/files.d.ts +7 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/utils/SpinnerManager.d.ts +30 -0
- package/dist/utils/SpinnerManager.js +73 -0
- package/dist/utils/gitDiff.js +18 -16
- package/dist/workflow/BranchStep.d.ts +13 -0
- package/dist/workflow/BranchStep.js +131 -0
- package/dist/workflow/DownloadStep.d.ts +19 -0
- package/dist/workflow/DownloadStep.js +127 -0
- package/dist/workflow/EnqueueStep.d.ts +15 -0
- package/dist/workflow/EnqueueStep.js +33 -0
- package/dist/workflow/PollJobsStep.d.ts +31 -0
- package/dist/workflow/PollJobsStep.js +284 -0
- package/dist/workflow/SetupStep.d.ts +16 -0
- package/dist/workflow/SetupStep.js +71 -0
- package/dist/workflow/UploadStep.d.ts +21 -0
- package/dist/workflow/UploadStep.js +72 -0
- package/dist/workflow/UserEditDiffsStep.d.ts +11 -0
- package/dist/workflow/UserEditDiffsStep.js +30 -0
- package/dist/workflow/Workflow.d.ts +4 -0
- package/dist/workflow/Workflow.js +2 -0
- package/dist/workflow/download.d.ts +22 -0
- package/dist/workflow/download.js +104 -0
- package/dist/workflow/stage.d.ts +14 -0
- package/dist/workflow/stage.js +76 -0
- package/package.json +4 -5
- package/dist/api/checkFileTranslations.d.ts +0 -23
- package/dist/api/checkFileTranslations.js +0 -281
- package/dist/api/sendFiles.d.ts +0 -17
- package/dist/api/sendFiles.js +0 -127
- package/dist/api/sendUserEdits.d.ts +0 -19
- package/dist/api/sendUserEdits.js +0 -15
- package/dist/cli/commands/edits.d.ts +0 -8
- package/dist/cli/commands/edits.js +0 -32
- package/dist/react/jsx/utils/buildImportMap.d.ts +0 -9
- package/dist/react/jsx/utils/buildImportMap.js +0 -30
- package/dist/react/jsx/utils/getPathsAndAliases.d.ts +0 -17
- package/dist/react/jsx/utils/getPathsAndAliases.js +0 -89
- package/dist/react/jsx/utils/jsxParsing/handleChildrenWhitespace.d.ts +0 -6
- package/dist/react/jsx/utils/jsxParsing/handleChildrenWhitespace.js +0 -199
- package/dist/react/jsx/utils/jsxParsing/multiplication/findMultiplicationNode.d.ts +0 -13
- package/dist/react/jsx/utils/jsxParsing/multiplication/findMultiplicationNode.js +0 -42
- package/dist/react/jsx/utils/jsxParsing/multiplication/multiplyJsxTree.d.ts +0 -5
- package/dist/react/jsx/utils/jsxParsing/multiplication/multiplyJsxTree.js +0 -69
- package/dist/react/jsx/utils/jsxParsing/parseJsx.d.ts +0 -60
- package/dist/react/jsx/utils/jsxParsing/parseJsx.js +0 -949
- package/dist/react/jsx/utils/jsxParsing/parseTProps.d.ts +0 -8
- package/dist/react/jsx/utils/jsxParsing/parseTProps.js +0 -47
- package/dist/react/jsx/utils/jsxParsing/types.d.ts +0 -48
- package/dist/react/jsx/utils/jsxParsing/types.js +0 -34
- package/dist/react/jsx/utils/resolveImportPath.d.ts +0 -11
- package/dist/react/jsx/utils/resolveImportPath.js +0 -111
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
+
## 2.4.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#792](https://github.com/generaltranslation/gt/pull/792) [`b6d6869`](https://github.com/generaltranslation/gt/commit/b6d686917316f6ed44130a54509459a7f9ee35fa) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Skipping over JSON and YAML files with failing parse to avoid crashing
|
|
8
|
+
|
|
9
|
+
## 2.4.14
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`3da05a1`](https://github.com/generaltranslation/gt/commit/3da05a12a37a62ace3c7e321aa2fed5a4af52ad9)]:
|
|
14
|
+
- generaltranslation@7.9.1
|
|
15
|
+
|
|
3
16
|
## 2.4.13
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { Settings } from '../types/index.js';
|
|
2
|
-
|
|
3
|
-
fileId: string;
|
|
4
|
-
versionId: string;
|
|
5
|
-
fileName: string;
|
|
6
|
-
};
|
|
2
|
+
import { FileReference } from 'generaltranslation/types';
|
|
7
3
|
/**
|
|
8
4
|
* Collects local user edits by diffing the latest downloaded server translation version
|
|
9
5
|
* against the current local translation file, and submits the diffs upstream.
|
|
10
6
|
*
|
|
11
7
|
* Must run before enqueueing new translations so rules are available to the generator.
|
|
12
8
|
*/
|
|
13
|
-
export declare function collectAndSendUserEditDiffs(
|
|
14
|
-
export {};
|
|
9
|
+
export declare function collectAndSendUserEditDiffs(files: FileReference[], settings: Settings): Promise<void>;
|
|
@@ -3,48 +3,41 @@ import * as path from 'node:path';
|
|
|
3
3
|
import { getDownloadedVersions } from '../fs/config/downloadedVersions.js';
|
|
4
4
|
import { createFileMapping } from '../formats/files/fileMapping.js';
|
|
5
5
|
import { getGitUnifiedDiff } from '../utils/gitDiff.js';
|
|
6
|
-
import { sendUserEditDiffs } from './sendUserEdits.js';
|
|
7
6
|
import { gt } from '../utils/gt.js';
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
10
9
|
/**
|
|
11
10
|
* Collects local user edits by diffing the latest downloaded server translation version
|
|
12
11
|
* against the current local translation file, and submits the diffs upstream.
|
|
13
12
|
*
|
|
14
13
|
* Must run before enqueueing new translations so rules are available to the generator.
|
|
15
14
|
*/
|
|
16
|
-
export async function collectAndSendUserEditDiffs(
|
|
15
|
+
export async function collectAndSendUserEditDiffs(files, settings) {
|
|
17
16
|
if (!settings.files)
|
|
18
17
|
return;
|
|
19
18
|
const { resolvedPaths, placeholderPaths, transformPaths } = settings.files;
|
|
20
19
|
const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, settings.locales, settings.defaultLocale);
|
|
21
20
|
const downloadedVersions = getDownloadedVersions(settings.configDirectory);
|
|
22
|
-
const tempDir = path.join(
|
|
21
|
+
const tempDir = path.join(os.tmpdir(), randomUUID());
|
|
23
22
|
if (!fs.existsSync(tempDir))
|
|
24
23
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
25
24
|
const candidates = [];
|
|
26
|
-
for (const uploadedFile of
|
|
25
|
+
for (const uploadedFile of files) {
|
|
27
26
|
for (const locale of settings.locales) {
|
|
28
|
-
const resolvedLocale = gt.resolveAliasLocale(locale);
|
|
29
27
|
const outputPath = fileMapping[locale]?.[uploadedFile.fileName] ?? null;
|
|
30
28
|
if (!outputPath)
|
|
31
29
|
continue;
|
|
32
30
|
if (!fs.existsSync(outputPath))
|
|
33
31
|
continue;
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
: null;
|
|
37
|
-
const lockKeyByName = `${uploadedFile.fileName}:${resolvedLocale}`;
|
|
38
|
-
const lockEntry = (lockKeyById && downloadedVersions.entries[lockKeyById]) ||
|
|
39
|
-
downloadedVersions.entries[lockKeyByName];
|
|
40
|
-
const versionId = lockEntry?.versionId;
|
|
41
|
-
if (!versionId)
|
|
32
|
+
const downloadedVersion = downloadedVersions.entries?.[uploadedFile.branchId]?.[uploadedFile.fileId]?.[uploadedFile.versionId]?.[locale];
|
|
33
|
+
if (!downloadedVersion)
|
|
42
34
|
continue;
|
|
43
35
|
candidates.push({
|
|
36
|
+
branchId: uploadedFile.branchId,
|
|
44
37
|
fileName: uploadedFile.fileName,
|
|
45
38
|
fileId: uploadedFile.fileId,
|
|
46
|
-
versionId,
|
|
47
|
-
locale:
|
|
39
|
+
versionId: uploadedFile.versionId,
|
|
40
|
+
locale: locale,
|
|
48
41
|
outputPath,
|
|
49
42
|
});
|
|
50
43
|
}
|
|
@@ -53,59 +46,39 @@ export async function collectAndSendUserEditDiffs(uploadedFiles, settings) {
|
|
|
53
46
|
if (candidates.length > 0) {
|
|
54
47
|
const fileQueryData = candidates.map((c) => ({
|
|
55
48
|
versionId: c.versionId,
|
|
56
|
-
fileName: c.fileName,
|
|
57
49
|
locale: c.locale,
|
|
50
|
+
fileId: c.fileId,
|
|
51
|
+
branchId: c.branchId,
|
|
58
52
|
}));
|
|
59
53
|
// Single batched check to obtain translation IDs
|
|
60
|
-
const checkResponse = await gt.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
for (const t of translations) {
|
|
65
|
-
const resolved = gt.resolveAliasLocale(t.locale);
|
|
66
|
-
idByKey.set(`${t.fileName}:${resolved}`, t.id);
|
|
67
|
-
}
|
|
68
|
-
// Collect translation IDs in batches and download contents
|
|
69
|
-
const ids = [];
|
|
70
|
-
for (const c of candidates) {
|
|
71
|
-
const id = idByKey.get(`${c.fileName}:${c.locale}`);
|
|
72
|
-
if (id)
|
|
73
|
-
ids.push(id);
|
|
74
|
-
}
|
|
75
|
-
// Helper to chunk array
|
|
76
|
-
function chunk(arr, size) {
|
|
77
|
-
const res = [];
|
|
78
|
-
for (let i = 0; i < arr.length; i += size)
|
|
79
|
-
res.push(arr.slice(i, i + size));
|
|
80
|
-
return res;
|
|
81
|
-
}
|
|
54
|
+
const checkResponse = await gt.queryFileData({
|
|
55
|
+
translatedFiles: fileQueryData,
|
|
56
|
+
});
|
|
57
|
+
const translatedFiles = checkResponse.translatedFiles?.filter((t) => t.completedAt) ?? [];
|
|
82
58
|
const serverContentByKey = new Map();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
// Ignore chunk failures; proceed with what we have
|
|
59
|
+
try {
|
|
60
|
+
const resp = await gt.downloadFileBatch(translatedFiles.map((file) => ({
|
|
61
|
+
branchId: file.branchId,
|
|
62
|
+
fileId: file.fileId,
|
|
63
|
+
locale: file.locale,
|
|
64
|
+
versionId: file.versionId,
|
|
65
|
+
})));
|
|
66
|
+
const files = resp?.files || [];
|
|
67
|
+
for (const f of files) {
|
|
68
|
+
serverContentByKey.set(`${f.branchId}:${f.fileId}:${f.versionId}:${f.locale}`, f.data);
|
|
99
69
|
}
|
|
100
70
|
}
|
|
71
|
+
catch {
|
|
72
|
+
// Ignore chunk failures; proceed with what we have
|
|
73
|
+
}
|
|
101
74
|
// Compute diffs using fetched server contents
|
|
102
75
|
for (const c of candidates) {
|
|
103
|
-
const key = `${c.
|
|
76
|
+
const key = `${c.branchId}:${c.fileId}:${c.versionId}:${c.locale}`;
|
|
104
77
|
const serverContent = serverContentByKey.get(key);
|
|
105
78
|
if (!serverContent)
|
|
106
79
|
continue;
|
|
107
80
|
try {
|
|
108
|
-
const safeName = Buffer.from(`${c.
|
|
81
|
+
const safeName = Buffer.from(`${c.branchId}:${c.fileId}:${c.versionId}:${c.locale}`)
|
|
109
82
|
.toString('base64')
|
|
110
83
|
.replace(/=+$/g, '');
|
|
111
84
|
const tempServerFile = path.join(tempDir, `${safeName}.server`);
|
|
@@ -121,6 +94,7 @@ export async function collectAndSendUserEditDiffs(uploadedFiles, settings) {
|
|
|
121
94
|
fileName: c.fileName,
|
|
122
95
|
locale: c.locale,
|
|
123
96
|
diff,
|
|
97
|
+
branchId: c.branchId,
|
|
124
98
|
versionId: c.versionId,
|
|
125
99
|
fileId: c.fileId,
|
|
126
100
|
localContent,
|
|
@@ -133,25 +107,6 @@ export async function collectAndSendUserEditDiffs(uploadedFiles, settings) {
|
|
|
133
107
|
}
|
|
134
108
|
}
|
|
135
109
|
if (collectedDiffs.length > 0) {
|
|
136
|
-
|
|
137
|
-
const maxBatchBytes = MAX_DIFF_BATCH_BYTES;
|
|
138
|
-
const batches = [];
|
|
139
|
-
let currentBatch = [];
|
|
140
|
-
for (const d of collectedDiffs) {
|
|
141
|
-
const tentative = [...currentBatch, d];
|
|
142
|
-
const bytes = Buffer.byteLength(JSON.stringify({ projectId: settings.projectId, diffs: tentative }), 'utf8');
|
|
143
|
-
if (bytes > maxBatchBytes && currentBatch.length > 0) {
|
|
144
|
-
batches.push(currentBatch);
|
|
145
|
-
currentBatch = [d];
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
currentBatch = tentative;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
if (currentBatch.length > 0)
|
|
152
|
-
batches.push(currentBatch);
|
|
153
|
-
for (const batch of batches) {
|
|
154
|
-
await sendUserEditDiffs(batch, settings);
|
|
155
|
-
}
|
|
110
|
+
await gt.submitUserEditDiffs({ diffs: collectedDiffs });
|
|
156
111
|
}
|
|
157
112
|
}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { Settings } from '../types/index.js';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import type { FileStatusTracker } from '../workflow/PollJobsStep.js';
|
|
3
|
+
export type BatchedFiles = {
|
|
4
|
+
branchId: string;
|
|
5
|
+
fileId: string;
|
|
6
|
+
versionId: string;
|
|
7
|
+
locale: string;
|
|
4
8
|
outputPath: string;
|
|
5
9
|
inputPath: string;
|
|
6
|
-
|
|
7
|
-
fileLocale: string;
|
|
8
|
-
fileId?: string;
|
|
9
|
-
versionId?: string;
|
|
10
|
-
}>;
|
|
10
|
+
}[];
|
|
11
11
|
export type DownloadFileBatchResult = {
|
|
12
|
-
successful:
|
|
13
|
-
failed:
|
|
12
|
+
successful: BatchedFiles;
|
|
13
|
+
failed: BatchedFiles;
|
|
14
|
+
skipped: BatchedFiles;
|
|
14
15
|
};
|
|
15
16
|
/**
|
|
16
17
|
* Downloads multiple translation files in a single batch request
|
|
@@ -19,4 +20,4 @@ export type DownloadFileBatchResult = {
|
|
|
19
20
|
* @param retryDelay - Delay between retries in milliseconds
|
|
20
21
|
* @returns Object containing successful and failed file IDs
|
|
21
22
|
*/
|
|
22
|
-
export declare function downloadFileBatch(files: BatchedFiles, options: Settings,
|
|
23
|
+
export declare function downloadFileBatch(fileTracker: FileStatusTracker, files: BatchedFiles, options: Settings, forceDownload?: boolean): Promise<DownloadFileBatchResult>;
|
|
@@ -6,7 +6,7 @@ import { validateJsonSchema } from '../formats/json/utils.js';
|
|
|
6
6
|
import { validateYamlSchema } from '../formats/yaml/utils.js';
|
|
7
7
|
import { mergeJson } from '../formats/json/mergeJson.js';
|
|
8
8
|
import mergeYaml from '../formats/yaml/mergeYaml.js';
|
|
9
|
-
import { getDownloadedVersions, saveDownloadedVersions, } from '../fs/config/downloadedVersions.js';
|
|
9
|
+
import { getDownloadedVersions, saveDownloadedVersions, ensureNestedObject, } from '../fs/config/downloadedVersions.js';
|
|
10
10
|
import { recordDownloaded } from '../state/recentDownloads.js';
|
|
11
11
|
import stringify from 'fast-json-stable-stringify';
|
|
12
12
|
/**
|
|
@@ -16,152 +16,145 @@ import stringify from 'fast-json-stable-stringify';
|
|
|
16
16
|
* @param retryDelay - Delay between retries in milliseconds
|
|
17
17
|
* @returns Object containing successful and failed file IDs
|
|
18
18
|
*/
|
|
19
|
-
export async function downloadFileBatch(files, options,
|
|
19
|
+
export async function downloadFileBatch(fileTracker, files, options, forceDownload = false) {
|
|
20
20
|
// Local record of what version was last downloaded for each fileName:locale
|
|
21
21
|
const downloadedVersions = getDownloadedVersions(options.configDirectory);
|
|
22
22
|
let didUpdateDownloadedLock = false;
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
23
|
+
// Create a map of requested file keys to the file object
|
|
24
|
+
const requestedFileMap = new Map(files.map((file) => [
|
|
25
|
+
`${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,
|
|
26
|
+
file,
|
|
27
|
+
]));
|
|
28
|
+
const result = {
|
|
29
|
+
successful: [],
|
|
30
|
+
failed: [],
|
|
31
|
+
skipped: [],
|
|
32
|
+
};
|
|
26
33
|
// Create a map of translationId to outputPath for easier lookup
|
|
27
|
-
const outputPathMap = new Map(files.map((file) => [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const localeMap = new Map(files.map((file) => [
|
|
31
|
-
file.translationId,
|
|
32
|
-
gt.resolveAliasLocale(file.locale),
|
|
34
|
+
const outputPathMap = new Map(files.map((file) => [
|
|
35
|
+
`${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,
|
|
36
|
+
file.outputPath,
|
|
33
37
|
]));
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
38
|
+
try {
|
|
39
|
+
// Download the files
|
|
40
|
+
const responseData = await gt.downloadFileBatch(files.map((file) => ({
|
|
41
|
+
fileId: file.fileId,
|
|
42
|
+
branchId: file.branchId,
|
|
43
|
+
versionId: file.versionId,
|
|
44
|
+
locale: file.locale,
|
|
45
|
+
})));
|
|
46
|
+
const downloadedFiles = responseData.files || [];
|
|
47
|
+
// Process each file in the response
|
|
48
|
+
for (const file of downloadedFiles) {
|
|
49
|
+
const fileKey = `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`;
|
|
50
|
+
const requestedFile = requestedFileMap.get(fileKey);
|
|
51
|
+
if (!requestedFile) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const outputPath = outputPathMap.get(fileKey);
|
|
56
|
+
const fileProperties = fileTracker.completed.get(fileKey);
|
|
57
|
+
if (!outputPath || !fileProperties) {
|
|
58
|
+
logWarning(`No input/output path found for file: ${fileKey}`);
|
|
59
|
+
result.failed.push(requestedFile);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const { fileId, versionId, locale, branchId, fileName: inputPath, } = fileProperties;
|
|
63
|
+
// Ensure the directory exists
|
|
64
|
+
const dir = path.dirname(outputPath);
|
|
65
|
+
if (!fs.existsSync(dir)) {
|
|
66
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
// If a local translation already exists for the same source version, skip overwrite
|
|
69
|
+
const downloadedVersion = downloadedVersions.entries[branchId]?.[fileId]?.[versionId]?.[locale];
|
|
70
|
+
const fileExists = fs.existsSync(outputPath);
|
|
71
|
+
if (!forceDownload && fileExists && downloadedVersion) {
|
|
72
|
+
result.skipped.push(requestedFile);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
let data = file.data;
|
|
76
|
+
if (options.options?.jsonSchema && locale) {
|
|
77
|
+
const jsonSchema = validateJsonSchema(options.options, inputPath);
|
|
78
|
+
if (jsonSchema) {
|
|
79
|
+
const originalContent = fs.readFileSync(inputPath, 'utf8');
|
|
80
|
+
if (originalContent) {
|
|
81
|
+
data = mergeJson(originalContent, inputPath, options.options, [
|
|
82
|
+
{
|
|
83
|
+
translatedContent: file.data,
|
|
84
|
+
targetLocale: locale,
|
|
85
|
+
},
|
|
86
|
+
], options.defaultLocale)[0];
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
}
|
|
90
|
+
if (options.options?.yamlSchema && locale) {
|
|
91
|
+
const yamlSchema = validateYamlSchema(options.options, inputPath);
|
|
92
|
+
if (yamlSchema) {
|
|
93
|
+
const originalContent = fs.readFileSync(inputPath, 'utf8');
|
|
94
|
+
if (originalContent) {
|
|
95
|
+
data = mergeYaml(originalContent, inputPath, options.options, [
|
|
96
|
+
{
|
|
97
|
+
translatedContent: file.data,
|
|
98
|
+
targetLocale: locale,
|
|
99
|
+
},
|
|
100
|
+
], options.defaultLocale)[0];
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
catch (error) {
|
|
109
|
-
logWarning(`Failed to sort GTJson file: ${file.id}: ` + error);
|
|
110
|
-
}
|
|
103
|
+
}
|
|
104
|
+
// If the file is a GTJSON file, stable sort the order and format the data
|
|
105
|
+
if (file.fileFormat === 'GTJSON') {
|
|
106
|
+
try {
|
|
107
|
+
const jsonData = JSON.parse(data);
|
|
108
|
+
const sortedData = stringify(jsonData); // stably sort with fast-json-stable-stringify
|
|
109
|
+
const sortedJsonData = JSON.parse(sortedData);
|
|
110
|
+
data = JSON.stringify(sortedJsonData, null, 2); // format the data
|
|
111
111
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// Track as downloaded
|
|
115
|
-
recordDownloaded(outputPath);
|
|
116
|
-
result.successful.push(translationId);
|
|
117
|
-
if (versionId) {
|
|
118
|
-
downloadedVersions.entries[downloadedKey] = {
|
|
119
|
-
versionId,
|
|
120
|
-
fileId: fileId || undefined,
|
|
121
|
-
fileName: inputPath,
|
|
122
|
-
updatedAt: new Date().toISOString(),
|
|
123
|
-
};
|
|
124
|
-
didUpdateDownloadedLock = true;
|
|
112
|
+
catch (error) {
|
|
113
|
+
logWarning(`Failed to sort GTJson file: ${file.id}: ` + error);
|
|
125
114
|
}
|
|
126
115
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
116
|
+
// Write the file to disk
|
|
117
|
+
await fs.promises.writeFile(outputPath, data);
|
|
118
|
+
// Track as downloaded
|
|
119
|
+
recordDownloaded(outputPath);
|
|
120
|
+
result.successful.push(requestedFile);
|
|
121
|
+
if (branchId && fileId && versionId && locale) {
|
|
122
|
+
ensureNestedObject(downloadedVersions.entries, [
|
|
123
|
+
branchId,
|
|
124
|
+
fileId,
|
|
125
|
+
versionId,
|
|
126
|
+
locale,
|
|
127
|
+
]);
|
|
128
|
+
downloadedVersions.entries[branchId][fileId][versionId][locale] = {
|
|
129
|
+
updatedAt: new Date().toISOString(),
|
|
130
|
+
};
|
|
131
|
+
didUpdateDownloadedLock = true;
|
|
137
132
|
}
|
|
138
133
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
didUpdateDownloadedLock = false;
|
|
134
|
+
catch (error) {
|
|
135
|
+
logError(`Error saving file ${fileKey}: ` + error);
|
|
136
|
+
result.failed.push(requestedFile);
|
|
143
137
|
}
|
|
144
|
-
return result;
|
|
145
138
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// Mark all files as failed
|
|
152
|
-
result.failed = [...fileIds];
|
|
153
|
-
if (didUpdateDownloadedLock) {
|
|
154
|
-
saveDownloadedVersions(options.configDirectory, downloadedVersions);
|
|
155
|
-
}
|
|
156
|
-
return result;
|
|
139
|
+
// Add any files that weren't in the response to the failed list
|
|
140
|
+
const downloadedFileKeys = new Set(downloadedFiles.map((file) => `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`));
|
|
141
|
+
for (const [fileKey, requestedFile] of requestedFileMap.entries()) {
|
|
142
|
+
if (!downloadedFileKeys.has(fileKey)) {
|
|
143
|
+
result.failed.push(requestedFile);
|
|
157
144
|
}
|
|
158
|
-
// Increment retry counter and wait before next attempt
|
|
159
|
-
retries++;
|
|
160
|
-
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
161
145
|
}
|
|
146
|
+
// Persist any updates to the downloaded map at the end of a successful cycle
|
|
147
|
+
if (didUpdateDownloadedLock) {
|
|
148
|
+
saveDownloadedVersions(options.configDirectory, downloadedVersions);
|
|
149
|
+
didUpdateDownloadedLock = false;
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
logError(`An unexpected error occurred while downloading files: ` + error);
|
|
162
155
|
}
|
|
163
156
|
// Mark all files as failed if we get here
|
|
164
|
-
result.failed = [...
|
|
157
|
+
result.failed = [...requestedFileMap.values()];
|
|
165
158
|
if (didUpdateDownloadedLock) {
|
|
166
159
|
saveDownloadedVersions(options.configDirectory, downloadedVersions);
|
|
167
160
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { gt } from '../utils/gt.js';
|
|
2
1
|
import { aggregateFiles } from '../formats/files/translate.js';
|
|
3
2
|
import { collectAndSendUserEditDiffs } from './collectUserEditDiffs.js';
|
|
3
|
+
import { gt } from '../utils/gt.js';
|
|
4
|
+
import { BranchStep } from '../workflow/BranchStep.js';
|
|
5
|
+
import { logErrorAndExit } from '../console/logging.js';
|
|
4
6
|
/**
|
|
5
7
|
* Uploads current source files to obtain file references, then collects and sends
|
|
6
8
|
* diffs for all locales based on last downloaded versions. Does not enqueue translations.
|
|
@@ -12,19 +14,20 @@ export async function saveLocalEdits(settings) {
|
|
|
12
14
|
const files = await aggregateFiles(settings);
|
|
13
15
|
if (!files.length)
|
|
14
16
|
return;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
// run branch query to get branch id
|
|
18
|
+
// Run the branch step
|
|
19
|
+
const branchStep = new BranchStep(gt, settings);
|
|
20
|
+
const branchResult = await branchStep.run();
|
|
21
|
+
await branchStep.wait();
|
|
22
|
+
if (!branchResult) {
|
|
23
|
+
logErrorAndExit('Failed to resolve git branch information.');
|
|
24
|
+
}
|
|
25
|
+
const uploads = files.map((file) => ({
|
|
26
|
+
fileName: file.fileName,
|
|
27
|
+
fileFormat: file.fileFormat,
|
|
28
|
+
branchId: branchResult.currentBranch.id,
|
|
29
|
+
fileId: file.fileId,
|
|
30
|
+
versionId: file.versionId,
|
|
23
31
|
}));
|
|
24
|
-
|
|
25
|
-
const upload = await gt.uploadSourceFiles(uploads, {
|
|
26
|
-
sourceLocale: settings.defaultLocale,
|
|
27
|
-
modelProvider: settings.modelProvider,
|
|
28
|
-
});
|
|
29
|
-
await collectAndSendUserEditDiffs(upload.uploadedFiles, settings);
|
|
32
|
+
await collectAndSendUserEditDiffs(uploads, settings);
|
|
30
33
|
}
|
package/dist/cli/base.js
CHANGED
|
@@ -94,7 +94,7 @@ export class BaseCLI {
|
|
|
94
94
|
if (!settings.stageTranslations) {
|
|
95
95
|
const results = await handleStage(initOptions, settings, this.library, false);
|
|
96
96
|
if (results) {
|
|
97
|
-
await handleTranslate(initOptions, settings, results);
|
|
97
|
+
await handleTranslate(initOptions, settings, results.fileVersionData, results.jobData, results.branchData);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
else {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Settings, SupportedLibraries, TranslateFlags } from '../../types/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { EnqueueFilesResult } from 'generaltranslation/types';
|
|
3
|
+
import { FileTranslationData } from '../../workflow/download.js';
|
|
4
|
+
import { BranchData } from '../../types/branch.js';
|
|
3
5
|
export declare const TEMPLATE_FILE_NAME = "__INTERNAL_GT_TEMPLATE_NAME__";
|
|
4
6
|
export declare const TEMPLATE_FILE_ID: string;
|
|
5
|
-
export declare function handleStage(options: TranslateFlags, settings: Settings, library: SupportedLibraries, stage: boolean): Promise<
|
|
7
|
+
export declare function handleStage(options: TranslateFlags, settings: Settings, library: SupportedLibraries, stage: boolean): Promise<{
|
|
8
|
+
fileVersionData: FileTranslationData | undefined;
|
|
9
|
+
jobData: EnqueueFilesResult | undefined;
|
|
10
|
+
branchData: BranchData | undefined;
|
|
11
|
+
} | null>;
|