gtx-cli 2.5.7 → 2.5.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.
- package/CHANGELOG.md +15 -0
- package/dist/api/downloadFileBatch.js +5 -5
- package/dist/api/saveLocalEdits.js +4 -3
- package/dist/api/uploadFiles.d.ts +1 -1
- package/dist/api/uploadFiles.js +5 -4
- package/dist/cli/base.d.ts +2 -1
- package/dist/cli/base.js +36 -30
- package/dist/cli/commands/setupProject.d.ts +7 -0
- package/dist/cli/commands/setupProject.js +50 -0
- package/dist/cli/commands/stage.d.ts +0 -2
- package/dist/cli/commands/stage.js +13 -61
- package/dist/cli/commands/translate.js +3 -5
- package/dist/cli/flags.js +2 -3
- package/dist/cli/next.js +1 -0
- package/dist/cli/react.d.ts +1 -0
- package/dist/cli/react.js +26 -15
- package/dist/config/generateSettings.js +2 -2
- package/dist/config/validateSettings.d.ts +1 -1
- package/dist/config/validateSettings.js +4 -4
- package/dist/console/logger.d.ts +35 -0
- package/dist/console/logger.js +270 -0
- package/dist/console/logging.d.ts +6 -11
- package/dist/console/logging.js +40 -55
- package/dist/formats/files/collectFiles.d.ts +6 -0
- package/dist/formats/files/collectFiles.js +49 -0
- package/dist/formats/files/fileMapping.js +1 -1
- package/dist/formats/files/save.js +2 -2
- package/dist/formats/files/translate.js +8 -8
- package/dist/formats/files/upload.js +7 -6
- package/dist/formats/gt/save.js +4 -3
- package/dist/formats/json/flattenJson.js +3 -3
- package/dist/formats/json/mergeJson.js +19 -18
- package/dist/formats/json/parseJson.js +14 -13
- package/dist/formats/json/utils.js +16 -15
- package/dist/formats/yaml/mergeYaml.js +6 -5
- package/dist/formats/yaml/parseYaml.js +4 -3
- package/dist/formats/yaml/utils.js +4 -3
- package/dist/fs/clearLocaleDirs.js +6 -6
- package/dist/fs/config/downloadedVersions.js +3 -3
- package/dist/fs/config/parseFilesConfig.js +2 -2
- package/dist/fs/config/setupConfig.js +2 -2
- package/dist/fs/config/updateConfig.js +2 -2
- package/dist/fs/config/updateVersions.js +3 -3
- package/dist/fs/copyFile.js +2 -2
- package/dist/fs/createLoadTranslationsFile.d.ts +1 -1
- package/dist/fs/createLoadTranslationsFile.js +11 -4
- package/dist/fs/determineFramework.js +3 -3
- package/dist/fs/findFilepath.js +5 -4
- package/dist/hooks/postProcess.js +9 -9
- package/dist/next/parse/handleInitGT.js +2 -2
- package/dist/react/parse/addVitePlugin/index.js +4 -3
- package/dist/react/parse/addVitePlugin/installCompiler.js +2 -2
- package/dist/react/parse/addVitePlugin/updateViteConfig.js +9 -12
- package/dist/react/parse/createDictionaryUpdates.js +4 -3
- package/dist/react/parse/createInlineUpdates.js +2 -2
- package/dist/setup/wizard.js +17 -16
- package/dist/translation/parse.js +4 -3
- package/dist/translation/stage.js +4 -4
- package/dist/translation/validate.js +6 -6
- package/dist/types/index.d.ts +1 -0
- package/dist/utils/addExplicitAnchorIds.js +2 -2
- package/dist/utils/constants.d.ts +2 -0
- package/dist/utils/constants.js +3 -0
- package/dist/utils/credentials.js +4 -3
- package/dist/utils/installPackage.js +11 -11
- package/dist/utils/packageJson.js +4 -3
- package/dist/workflow/BranchStep.js +5 -4
- package/dist/workflow/DownloadStep.js +5 -5
- package/dist/workflow/EnqueueStep.js +2 -2
- package/dist/workflow/PollJobsStep.js +4 -4
- package/dist/workflow/SetupStep.d.ts +1 -1
- package/dist/workflow/SetupStep.js +4 -3
- package/dist/workflow/UploadStep.js +3 -3
- package/dist/workflow/UserEditDiffsStep.js +2 -2
- package/dist/workflow/download.js +4 -3
- package/dist/workflow/setupProject.d.ts +13 -0
- package/dist/workflow/setupProject.js +48 -0
- package/dist/workflow/stage.js +4 -21
- package/package.json +3 -3
- package/dist/utils/SpinnerManager.d.ts +0 -30
- package/dist/utils/SpinnerManager.js +0 -73
package/dist/console/logging.js
CHANGED
|
@@ -1,45 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { text, select, confirm, isCancel, cancel, multiselect, } from '@clack/prompts';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { getCLIVersion } from '../utils/packageJson.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
log.info(message);
|
|
7
|
-
}
|
|
8
|
-
export function logWarning(message) {
|
|
9
|
-
log.warn(message);
|
|
10
|
-
}
|
|
11
|
-
export function logError(message) {
|
|
12
|
-
log.error(message);
|
|
13
|
-
}
|
|
14
|
-
export function logSuccess(message) {
|
|
15
|
-
log.success(message);
|
|
16
|
-
}
|
|
17
|
-
export function logStep(message) {
|
|
18
|
-
log.step(message);
|
|
19
|
-
}
|
|
20
|
-
export function logMessage(message) {
|
|
21
|
-
log.message(message, { symbol: chalk.cyan('~') });
|
|
22
|
-
}
|
|
4
|
+
import { logger } from './logger.js';
|
|
5
|
+
import { TEMPLATE_FILE_NAME } from '../utils/constants.js';
|
|
23
6
|
export function logErrorAndExit(message) {
|
|
24
|
-
|
|
25
|
-
|
|
7
|
+
logger.error(message);
|
|
8
|
+
return exitSync(1);
|
|
26
9
|
}
|
|
27
|
-
export function
|
|
10
|
+
export function exitSync(code) {
|
|
11
|
+
// Flush logs before exit
|
|
12
|
+
logger.flush();
|
|
28
13
|
process.exit(code);
|
|
29
14
|
}
|
|
30
|
-
// Clack prompts
|
|
31
|
-
export function startCommand(message) {
|
|
32
|
-
intro(chalk.cyan(message));
|
|
33
|
-
}
|
|
34
|
-
export function endCommand(message) {
|
|
35
|
-
outro(chalk.cyan(message));
|
|
36
|
-
}
|
|
37
15
|
// GT specific logging
|
|
38
16
|
export function displayHeader(introString) {
|
|
39
17
|
displayAsciiTitle();
|
|
40
18
|
displayInitializingText();
|
|
41
19
|
if (introString) {
|
|
42
|
-
startCommand(introString);
|
|
20
|
+
logger.startCommand(introString);
|
|
43
21
|
}
|
|
44
22
|
}
|
|
45
23
|
function displayAsciiTitle() {
|
|
@@ -59,30 +37,22 @@ ${chalk.dim('https://generaltranslation.com/docs')}
|
|
|
59
37
|
${chalk.dim(`CLI Version: ${version}\n`)}`);
|
|
60
38
|
}
|
|
61
39
|
export function displayProjectId(projectId) {
|
|
62
|
-
|
|
40
|
+
logger.message(chalk.dim(`Project ID: ${chalk.bold(projectId)}`), chalk.cyan('~'));
|
|
63
41
|
}
|
|
64
42
|
export function displayResolvedPaths(resolvedPaths) {
|
|
65
43
|
const paths = resolvedPaths.map(([key, resolvedPath]) => {
|
|
66
44
|
return chalk.dim(`'${chalk.white(key)}' → '${chalk.green(resolvedPath)}'`);
|
|
67
45
|
});
|
|
68
|
-
|
|
46
|
+
logger.step(`Resolved path aliases:\n${paths.join('\n')}`);
|
|
69
47
|
}
|
|
70
48
|
export function displayCreatedConfigFile(configFilepath) {
|
|
71
|
-
|
|
49
|
+
logger.step(`Created config file ${chalk.cyan(configFilepath)}`);
|
|
72
50
|
}
|
|
73
51
|
export function displayUpdatedConfigFile(configFilepath) {
|
|
74
|
-
|
|
52
|
+
logger.success(`Updated config file ${chalk.cyan(configFilepath)}`);
|
|
75
53
|
}
|
|
76
54
|
export function displayUpdatedVersionsFile(versionFilepath) {
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
// Spinner functionality
|
|
80
|
-
export function createSpinner(indicator = 'timer') {
|
|
81
|
-
return spinner({ indicator });
|
|
82
|
-
}
|
|
83
|
-
// Spinner functionality
|
|
84
|
-
export function createProgressBar(total) {
|
|
85
|
-
return progress({ max: total });
|
|
55
|
+
logger.success(`Updated versions file ${chalk.cyan(versionFilepath)}`);
|
|
86
56
|
}
|
|
87
57
|
// Input prompts
|
|
88
58
|
export async function promptText({ message, defaultValue, validate, }) {
|
|
@@ -98,7 +68,7 @@ export async function promptText({ message, defaultValue, validate, }) {
|
|
|
98
68
|
});
|
|
99
69
|
if (isCancel(result)) {
|
|
100
70
|
cancel('Operation cancelled');
|
|
101
|
-
|
|
71
|
+
return exitSync(0);
|
|
102
72
|
}
|
|
103
73
|
return result;
|
|
104
74
|
}
|
|
@@ -116,7 +86,7 @@ export async function promptSelect({ message, options, defaultValue, }) {
|
|
|
116
86
|
});
|
|
117
87
|
if (isCancel(result)) {
|
|
118
88
|
cancel('Operation cancelled');
|
|
119
|
-
|
|
89
|
+
return exitSync(0);
|
|
120
90
|
}
|
|
121
91
|
return result;
|
|
122
92
|
}
|
|
@@ -134,7 +104,7 @@ export async function promptMultiSelect({ message, options, required = true, })
|
|
|
134
104
|
});
|
|
135
105
|
if (isCancel(result)) {
|
|
136
106
|
cancel('Operation cancelled');
|
|
137
|
-
|
|
107
|
+
return exitSync(0);
|
|
138
108
|
}
|
|
139
109
|
return result;
|
|
140
110
|
}
|
|
@@ -145,25 +115,25 @@ export async function promptConfirm({ message, defaultValue = true, cancelMessag
|
|
|
145
115
|
});
|
|
146
116
|
if (isCancel(result)) {
|
|
147
117
|
cancel(cancelMessage);
|
|
148
|
-
|
|
118
|
+
return exitSync(0);
|
|
149
119
|
}
|
|
150
120
|
return result;
|
|
151
121
|
}
|
|
152
122
|
// Warning display functions
|
|
153
123
|
export function warnApiKeyInConfig(optionsFilepath) {
|
|
154
|
-
|
|
124
|
+
logger.warn(`Found ${chalk.cyan('apiKey')} in "${chalk.green(optionsFilepath)}". ` +
|
|
155
125
|
chalk.white('Your API key is exposed! Please remove it from the file and include it as an environment variable.'));
|
|
156
126
|
}
|
|
157
127
|
export function warnVariableProp(file, attrName, value) {
|
|
158
|
-
|
|
128
|
+
logger.warn(`Found ${chalk.green('<T>')} component in ${chalk.cyan(file)} with variable ${attrName}: "${chalk.white(value)}". ` +
|
|
159
129
|
`Change "${attrName}" to ensure this content is translated.`);
|
|
160
130
|
}
|
|
161
131
|
export function warnNoId(file) {
|
|
162
|
-
|
|
132
|
+
logger.warn(`Found ${chalk.green('<T>')} component in ${chalk.cyan(file)} with no id. ` +
|
|
163
133
|
chalk.white('Add an id to ensure the content is translated.'));
|
|
164
134
|
}
|
|
165
135
|
export function warnHasUnwrappedExpression(file, id, unwrappedExpressions) {
|
|
166
|
-
|
|
136
|
+
logger.warn(`${chalk.green('<T>')} with id "${id}" in ${chalk.cyan(file)} has children: ${unwrappedExpressions.join(', ')} that could change at runtime. ` +
|
|
167
137
|
chalk.white('Use a variable component like ') +
|
|
168
138
|
chalk.green('<Var>') +
|
|
169
139
|
chalk.white(' (') +
|
|
@@ -171,14 +141,29 @@ export function warnHasUnwrappedExpression(file, id, unwrappedExpressions) {
|
|
|
171
141
|
chalk.white(') to translate this properly.'));
|
|
172
142
|
}
|
|
173
143
|
export function warnNonStaticExpression(file, attrName, value) {
|
|
174
|
-
|
|
144
|
+
logger.warn(`Found non-static expression in ${chalk.cyan(file)} for attribute ${attrName}: "${chalk.white(value)}". ` +
|
|
175
145
|
`Change "${attrName}" to ensure this content is translated.`);
|
|
176
146
|
}
|
|
177
147
|
export function warnTemplateLiteral(file, value) {
|
|
178
|
-
|
|
148
|
+
logger.warn(`Found template literal with quasis (${value}) in ${chalk.cyan(file)}. ` +
|
|
179
149
|
chalk.white('Change the template literal to a string to ensure this content is translated.'));
|
|
180
150
|
}
|
|
181
151
|
export function warnTernary(file) {
|
|
182
|
-
|
|
152
|
+
logger.warn(`Found ternary expression in ${chalk.cyan(file)}. ` +
|
|
183
153
|
chalk.white('A Branch component may be more appropriate here.'));
|
|
184
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Helper: Log all collected files
|
|
157
|
+
*/
|
|
158
|
+
export function logCollectedFiles(files, reactComponents) {
|
|
159
|
+
logger.message(chalk.cyan('Files found in project:') +
|
|
160
|
+
'\n' +
|
|
161
|
+
files
|
|
162
|
+
.map((file) => {
|
|
163
|
+
if (file.fileName === TEMPLATE_FILE_NAME) {
|
|
164
|
+
return `- <React Elements>${reactComponents ? ` (${reactComponents})` : ''}`;
|
|
165
|
+
}
|
|
166
|
+
return `- ${file.fileName}`;
|
|
167
|
+
})
|
|
168
|
+
.join('\n'));
|
|
169
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Settings, SupportedLibraries, TranslateFlags } from '../../types/index.js';
|
|
2
|
+
import type { FileToUpload } from 'generaltranslation/types';
|
|
3
|
+
export declare function collectFiles(options: TranslateFlags, settings: Settings, library: SupportedLibraries): Promise<{
|
|
4
|
+
files: FileToUpload[];
|
|
5
|
+
reactComponents: number;
|
|
6
|
+
}>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { logErrorAndExit } from '../../console/logging.js';
|
|
2
|
+
import { invalidConfigurationError } from '../../console/index.js';
|
|
3
|
+
import { aggregateFiles } from '../../formats/files/translate.js';
|
|
4
|
+
import { aggregateReactTranslations } from '../../translation/stage.js';
|
|
5
|
+
import { hashStringSync } from '../../utils/hash.js';
|
|
6
|
+
import { TEMPLATE_FILE_NAME, TEMPLATE_FILE_ID } from '../../utils/constants.js';
|
|
7
|
+
export async function collectFiles(options, settings, library) {
|
|
8
|
+
// Aggregate files
|
|
9
|
+
const allFiles = await aggregateFiles(settings);
|
|
10
|
+
// Parse for React components
|
|
11
|
+
let reactComponents = 0;
|
|
12
|
+
if (library === 'gt-react' || library === 'gt-next') {
|
|
13
|
+
const updates = await aggregateReactTranslations(options, settings, library);
|
|
14
|
+
if (updates.length > 0) {
|
|
15
|
+
if (!options.dryRun &&
|
|
16
|
+
!settings.publish &&
|
|
17
|
+
!settings.files?.placeholderPaths.gt) {
|
|
18
|
+
logErrorAndExit(invalidConfigurationError);
|
|
19
|
+
}
|
|
20
|
+
// Convert updates to a file object
|
|
21
|
+
const fileData = {};
|
|
22
|
+
const fileMetadata = {};
|
|
23
|
+
// Convert updates to the proper data format
|
|
24
|
+
for (const update of updates) {
|
|
25
|
+
const { source, metadata, dataFormat } = update;
|
|
26
|
+
metadata.dataFormat = dataFormat; // add the data format to the metadata
|
|
27
|
+
const { hash, id } = metadata;
|
|
28
|
+
if (id) {
|
|
29
|
+
fileData[id] = source;
|
|
30
|
+
fileMetadata[id] = metadata;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
fileData[hash] = source;
|
|
34
|
+
fileMetadata[hash] = metadata;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
reactComponents = updates.length;
|
|
38
|
+
allFiles.push({
|
|
39
|
+
fileName: TEMPLATE_FILE_NAME,
|
|
40
|
+
content: JSON.stringify(fileData),
|
|
41
|
+
fileFormat: 'GTJSON',
|
|
42
|
+
formatMetadata: fileMetadata,
|
|
43
|
+
fileId: TEMPLATE_FILE_ID,
|
|
44
|
+
versionId: hashStringSync(JSON.stringify(fileData)),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { files: allFiles, reactComponents };
|
|
49
|
+
}
|
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { getRelative } from '../../fs/findFilepath.js';
|
|
5
5
|
import { getLocaleProperties } from 'generaltranslation';
|
|
6
6
|
import { replaceLocalePlaceholders } from '../utils.js';
|
|
7
|
-
import { TEMPLATE_FILE_NAME } from '../../
|
|
7
|
+
import { TEMPLATE_FILE_NAME } from '../../utils/constants.js';
|
|
8
8
|
/**
|
|
9
9
|
* Creates a mapping between source files and their translated counterparts for each locale
|
|
10
10
|
* @param filePaths - Resolved file paths for different file types
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { logger } from '../../console/logger.js';
|
|
4
4
|
/**
|
|
5
5
|
* Saves translated MDX/MD file content to the appropriate location
|
|
6
6
|
*/
|
|
@@ -12,6 +12,6 @@ export async function saveTranslatedFile(translatedContent, outputDir, fileName,
|
|
|
12
12
|
// Save the translated file with the appropriate extension
|
|
13
13
|
const outputPath = path.join(localeDir, fileName);
|
|
14
14
|
await fs.writeFile(outputPath, translatedContent);
|
|
15
|
-
|
|
15
|
+
logger.success(`Saved translated ${dataFormat} file to: ${outputPath}`);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { logger } from '../../console/logger.js';
|
|
2
2
|
import { getRelative, readFile } from '../../fs/findFilepath.js';
|
|
3
3
|
import { SUPPORTED_FILE_EXTENSIONS } from './supportedFiles.js';
|
|
4
4
|
import sanitizeFileContent from '../../utils/sanitizeFileContent.js';
|
|
@@ -46,7 +46,7 @@ export async function aggregateFiles(settings) {
|
|
|
46
46
|
JSON.parse(content);
|
|
47
47
|
}
|
|
48
48
|
catch (e) {
|
|
49
|
-
|
|
49
|
+
logger.warn(`Skipping ${relativePath}: JSON file is not parsable`);
|
|
50
50
|
return null;
|
|
51
51
|
}
|
|
52
52
|
const parsedJson = parseJson(content, filePath, settings.options || {}, settings.defaultLocale);
|
|
@@ -63,7 +63,7 @@ export async function aggregateFiles(settings) {
|
|
|
63
63
|
if (!file)
|
|
64
64
|
return false;
|
|
65
65
|
if (typeof file.content !== 'string' || !file.content.trim()) {
|
|
66
|
-
|
|
66
|
+
logger.warn(`Skipping ${file.fileName}: JSON file is empty`);
|
|
67
67
|
return false;
|
|
68
68
|
}
|
|
69
69
|
return true;
|
|
@@ -81,7 +81,7 @@ export async function aggregateFiles(settings) {
|
|
|
81
81
|
YAML.parse(content);
|
|
82
82
|
}
|
|
83
83
|
catch (e) {
|
|
84
|
-
|
|
84
|
+
logger.warn(`Skipping ${relativePath}: YAML file is not parsable`);
|
|
85
85
|
return null;
|
|
86
86
|
}
|
|
87
87
|
const { content: parsedYaml, fileFormat } = parseYaml(content, filePath, settings.options || {});
|
|
@@ -95,7 +95,7 @@ export async function aggregateFiles(settings) {
|
|
|
95
95
|
})
|
|
96
96
|
.filter((file) => {
|
|
97
97
|
if (!file || typeof file.content !== 'string' || !file.content.trim()) {
|
|
98
|
-
|
|
98
|
+
logger.warn(`Skipping ${file?.fileName ?? 'unknown'}: YAML file is empty`);
|
|
99
99
|
return false;
|
|
100
100
|
}
|
|
101
101
|
return true;
|
|
@@ -113,7 +113,7 @@ export async function aggregateFiles(settings) {
|
|
|
113
113
|
if (fileType === 'mdx') {
|
|
114
114
|
const validation = isValidMdx(content, filePath);
|
|
115
115
|
if (!validation.isValid) {
|
|
116
|
-
|
|
116
|
+
logger.warn(`Skipping ${relativePath}: MDX file is not AST parsable${validation.error ? `: ${validation.error}` : ''}`);
|
|
117
117
|
return null;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -130,7 +130,7 @@ export async function aggregateFiles(settings) {
|
|
|
130
130
|
if (!file ||
|
|
131
131
|
typeof file.content !== 'string' ||
|
|
132
132
|
!file.content.trim()) {
|
|
133
|
-
|
|
133
|
+
logger.warn(`Skipping ${file?.fileName ?? 'unknown'}: File is empty after sanitization`);
|
|
134
134
|
return false;
|
|
135
135
|
}
|
|
136
136
|
return true;
|
|
@@ -139,7 +139,7 @@ export async function aggregateFiles(settings) {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
if (allFiles.length === 0 && !settings.publish) {
|
|
142
|
-
|
|
142
|
+
logger.error('No files to translate were found. Please check your configuration and try again.');
|
|
143
143
|
}
|
|
144
144
|
return allFiles;
|
|
145
145
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { noSupportedFormatError, noDefaultLocaleError, noApiKeyError, noProjectIdError, devApiKeyError, } from '../../console/index.js';
|
|
2
|
-
import { logErrorAndExit
|
|
2
|
+
import { logErrorAndExit } from '../../console/logging.js';
|
|
3
|
+
import { logger } from '../../console/logger.js';
|
|
3
4
|
import { getRelative, readFile } from '../../fs/findFilepath.js';
|
|
4
5
|
import { SUPPORTED_FILE_EXTENSIONS } from './supportedFiles.js';
|
|
5
6
|
import sanitizeFileContent from '../../utils/sanitizeFileContent.js';
|
|
@@ -80,20 +81,20 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
|
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
if (allFiles.length === 0) {
|
|
83
|
-
|
|
84
|
+
logger.error('No files to upload were found. Please check your configuration and try again.');
|
|
84
85
|
return;
|
|
85
86
|
}
|
|
86
87
|
if (!options.defaultLocale) {
|
|
87
|
-
logErrorAndExit(noDefaultLocaleError);
|
|
88
|
+
return logErrorAndExit(noDefaultLocaleError);
|
|
88
89
|
}
|
|
89
90
|
if (!options.apiKey) {
|
|
90
|
-
logErrorAndExit(noApiKeyError);
|
|
91
|
+
return logErrorAndExit(noApiKeyError);
|
|
91
92
|
}
|
|
92
93
|
if (options.apiKey.startsWith('gtx-dev-')) {
|
|
93
|
-
logErrorAndExit(devApiKeyError);
|
|
94
|
+
return logErrorAndExit(devApiKeyError);
|
|
94
95
|
}
|
|
95
96
|
if (!options.projectId) {
|
|
96
|
-
logErrorAndExit(noProjectIdError);
|
|
97
|
+
return logErrorAndExit(noProjectIdError);
|
|
97
98
|
}
|
|
98
99
|
const locales = options.locales || [];
|
|
99
100
|
// Create file mapping for all file types
|
package/dist/formats/gt/save.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { logger } from '../../console/logger.js';
|
|
4
|
+
import { exitSync } from '../../console/logging.js';
|
|
4
5
|
import { noFilesError } from '../../console/index.js';
|
|
5
6
|
import { resolveLocaleFiles } from '../../fs/config/parseFilesConfig.js';
|
|
6
7
|
/**
|
|
@@ -14,8 +15,8 @@ export async function saveTranslations(translations, placeholderPaths) {
|
|
|
14
15
|
const locale = translation.locale;
|
|
15
16
|
const translationFiles = resolveLocaleFiles(placeholderPaths, locale);
|
|
16
17
|
if (!translationFiles.gt) {
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
logger.error(noFilesError);
|
|
19
|
+
exitSync(1);
|
|
19
20
|
}
|
|
20
21
|
const filepath = translationFiles.gt;
|
|
21
22
|
const translationData = translation.translation;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSONPath } from 'jsonpath-plus';
|
|
2
|
-
import {
|
|
2
|
+
import { logger } from '../../console/logger.js';
|
|
3
3
|
/**
|
|
4
4
|
* Flattens a JSON object according to a list of JSON paths.
|
|
5
5
|
* @param json - The JSON object to flatten
|
|
@@ -25,7 +25,7 @@ export function flattenJson(json, jsonPaths) {
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
catch (error) {
|
|
28
|
-
|
|
28
|
+
logger.error(`Error with JSONPath pattern: ${jsonPath}`);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
return extractedJson;
|
|
@@ -57,7 +57,7 @@ export function flattenJsonWithStringFilter(json, jsonPaths) {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
catch {
|
|
60
|
-
|
|
60
|
+
logger.error(`Error with JSONPath pattern: ${jsonPath}`);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
return extractedJson;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import JSONPointer from 'jsonpointer';
|
|
2
|
-
import {
|
|
2
|
+
import { exitSync } from '../../console/logging.js';
|
|
3
|
+
import { logger } from '../../console/logger.js';
|
|
3
4
|
import { findMatchingItemArray, findMatchingItemObject, generateSourceObjectPointers, getIdentifyingLocaleProperty, getSourceObjectOptionsArray, validateJsonSchema, } from './utils.js';
|
|
4
5
|
import { JSONPath } from 'jsonpath-plus';
|
|
5
6
|
import { getLocaleProperties } from 'generaltranslation';
|
|
@@ -14,8 +15,8 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
|
|
|
14
15
|
originalJson = JSON.parse(originalContent);
|
|
15
16
|
}
|
|
16
17
|
catch {
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
logger.error(`Invalid JSON file: ${inputPath}`);
|
|
19
|
+
return exitSync(1);
|
|
19
20
|
}
|
|
20
21
|
// Handle include
|
|
21
22
|
if (jsonSchema.include) {
|
|
@@ -40,8 +41,8 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
|
|
|
40
41
|
return output;
|
|
41
42
|
}
|
|
42
43
|
if (!jsonSchema.composite) {
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
logger.error('No composite property found in JSON schema');
|
|
45
|
+
return exitSync(1);
|
|
45
46
|
}
|
|
46
47
|
// Handle composite
|
|
47
48
|
// Create a deep copy of the original JSON to avoid mutations
|
|
@@ -54,13 +55,13 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
|
|
|
54
55
|
if (sourceObjectOptions.type === 'array') {
|
|
55
56
|
// Validate type
|
|
56
57
|
if (!Array.isArray(sourceObjectValue)) {
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
logger.error(`Source object value is not an array at path: ${sourceObjectPointer}`);
|
|
59
|
+
return exitSync(1);
|
|
59
60
|
}
|
|
60
61
|
// Get source item for default locale
|
|
61
62
|
const matchingDefaultLocaleItems = findMatchingItemArray(defaultLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
|
|
62
63
|
if (!Object.keys(matchingDefaultLocaleItems).length) {
|
|
63
|
-
|
|
64
|
+
logger.warn(`Matching sourceItems not found at path: ${sourceObjectPointer}. Please check your JSON file includes the key field. Skipping this target`);
|
|
64
65
|
continue;
|
|
65
66
|
}
|
|
66
67
|
const matchingDefaultLocaleItemKeys = new Set(Object.keys(matchingDefaultLocaleItems));
|
|
@@ -96,14 +97,14 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
|
|
|
96
97
|
};
|
|
97
98
|
// 4. Validate that the mergedItems is not empty
|
|
98
99
|
if (Object.keys(mergedItems).length === 0) {
|
|
99
|
-
|
|
100
|
+
logger.warn(`Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`);
|
|
100
101
|
continue;
|
|
101
102
|
}
|
|
102
103
|
for (const [sourceItemPointer, targetItem] of Object.entries(mergedItems)) {
|
|
103
104
|
// 5. Validate that all the array indecies are still present in the source json
|
|
104
105
|
if (!matchingDefaultLocaleItemKeys.has(sourceItemPointer)) {
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
logger.error(`Array index ${sourceItemPointer} is not present in the source json. It is possible that the source json has been modified since the translation was generated.`);
|
|
107
|
+
return exitSync(1);
|
|
107
108
|
}
|
|
108
109
|
// 6. Override the source item with the translated values
|
|
109
110
|
const defaultLocaleSourceItem = matchingDefaultLocaleItems[sourceItemPointer].sourceItem;
|
|
@@ -129,8 +130,8 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
|
|
|
129
130
|
}
|
|
130
131
|
// 8. Check that items to add is >= items to remove (if this happens, something is very wrong)
|
|
131
132
|
if (itemsToAdd.length < indiciesToRemove.size) {
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
logger.error(`Items to add is less than items to remove at path: ${sourceObjectPointer}. Please check your JSON schema key field.`);
|
|
134
|
+
return exitSync(1);
|
|
134
135
|
}
|
|
135
136
|
// 9. Remove all items for the target locale (they can be identified by the key)
|
|
136
137
|
const filteredSourceObjectValue = sourceObjectValue.filter((_, index) => !indiciesToRemove.has(index));
|
|
@@ -141,15 +142,15 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
|
|
|
141
142
|
else {
|
|
142
143
|
// Validate type
|
|
143
144
|
if (typeof sourceObjectValue !== 'object' || sourceObjectValue === null) {
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
logger.error(`Source object value is not an object at path: ${sourceObjectPointer}`);
|
|
146
|
+
return exitSync(1);
|
|
146
147
|
}
|
|
147
148
|
// Validate localeProperty
|
|
148
149
|
const matchingDefaultLocaleItem = findMatchingItemObject(defaultLocale, sourceObjectPointer, sourceObjectOptions, sourceObjectValue);
|
|
149
150
|
// Validate source item exists
|
|
150
151
|
if (!matchingDefaultLocaleItem.sourceItem) {
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
logger.error(`Source item not found at path: ${sourceObjectPointer}. You must specify a source item where its key matches the default locale`);
|
|
153
|
+
return exitSync(1);
|
|
153
154
|
}
|
|
154
155
|
const { sourceItem: defaultLocaleSourceItem } = matchingDefaultLocaleItem;
|
|
155
156
|
// For each target:
|
|
@@ -180,7 +181,7 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
|
|
|
180
181
|
};
|
|
181
182
|
// 4. Validate that the mergedItems is not empty
|
|
182
183
|
if (Object.keys(mergedItems).length === 0) {
|
|
183
|
-
|
|
184
|
+
logger.warn(`Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`);
|
|
184
185
|
continue;
|
|
185
186
|
}
|
|
186
187
|
// 5. Override the source item with the translated values
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { flattenJsonWithStringFilter } from './flattenJson.js';
|
|
2
2
|
import { JSONPath } from 'jsonpath-plus';
|
|
3
|
-
import {
|
|
3
|
+
import { exitSync } from '../../console/logging.js';
|
|
4
|
+
import { logger } from '../../console/logger.js';
|
|
4
5
|
import { findMatchingItemArray, findMatchingItemObject, generateSourceObjectPointers, validateJsonSchema, } from './utils.js';
|
|
5
6
|
// Parse a JSON file according to a JSON schema
|
|
6
7
|
export function parseJson(content, filePath, options, defaultLocale) {
|
|
@@ -13,8 +14,8 @@ export function parseJson(content, filePath, options, defaultLocale) {
|
|
|
13
14
|
json = JSON.parse(content);
|
|
14
15
|
}
|
|
15
16
|
catch {
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
logger.error(`Invalid JSON file: ${filePath}`);
|
|
18
|
+
return exitSync(1);
|
|
18
19
|
}
|
|
19
20
|
// Handle include
|
|
20
21
|
if (jsonSchema.include) {
|
|
@@ -22,8 +23,8 @@ export function parseJson(content, filePath, options, defaultLocale) {
|
|
|
22
23
|
return JSON.stringify(flattenedJson);
|
|
23
24
|
}
|
|
24
25
|
if (!jsonSchema.composite) {
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
logger.error('No composite property found in JSON schema');
|
|
27
|
+
return exitSync(1);
|
|
27
28
|
}
|
|
28
29
|
// Construct lvl 1
|
|
29
30
|
// Create mapping of sourceObjectPointer to SourceObjectOptions
|
|
@@ -40,14 +41,14 @@ export function parseJson(content, filePath, options, defaultLocale) {
|
|
|
40
41
|
if (sourceObjectOptions.type === 'array') {
|
|
41
42
|
// Validate type
|
|
42
43
|
if (!Array.isArray(sourceObjectValue)) {
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
logger.error(`Source object value is not an array at path: ${sourceObjectPointer}`);
|
|
45
|
+
return exitSync(1);
|
|
45
46
|
}
|
|
46
47
|
// Find matching source items
|
|
47
48
|
const matchingItems = findMatchingItemArray(defaultLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
|
|
48
49
|
if (!Object.keys(matchingItems).length) {
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
logger.error(`Matching sourceItem not found at path: ${sourceObjectPointer} for locale: ${defaultLocale}. Please check your JSON schema`);
|
|
51
|
+
return exitSync(1);
|
|
51
52
|
}
|
|
52
53
|
// Construct lvl 3
|
|
53
54
|
const sourceItemsToTranslate = {};
|
|
@@ -87,15 +88,15 @@ export function parseJson(content, filePath, options, defaultLocale) {
|
|
|
87
88
|
// Object: use the key in this object with the matching locale property
|
|
88
89
|
// Validate type
|
|
89
90
|
if (typeof sourceObjectValue !== 'object' || sourceObjectValue === null) {
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
logger.error(`Source object value is not an object at path: ${sourceObjectPointer}`);
|
|
92
|
+
return exitSync(1);
|
|
92
93
|
}
|
|
93
94
|
// Validate localeProperty
|
|
94
95
|
const matchingItem = findMatchingItemObject(defaultLocale, sourceObjectPointer, sourceObjectOptions, sourceObjectValue);
|
|
95
96
|
// Validate source item exists
|
|
96
97
|
if (!matchingItem.sourceItem) {
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
logger.error(`Source item not found at path: ${sourceObjectPointer}. You must specify a source item where its key matches the default locale`);
|
|
99
|
+
return exitSync(1);
|
|
99
100
|
}
|
|
100
101
|
const { sourceItem } = matchingItem;
|
|
101
102
|
// Get the fields to translate from the includes
|