gtx-cli 2.5.0-alpha.0 → 2.5.0-alpha.2
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 +286 -0
- package/dist/workflow/SetupStep.d.ts +16 -0
- package/dist/workflow/SetupStep.js +72 -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 +3 -4
- 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
|
@@ -2,7 +2,7 @@ import { logErrorAndExit, logSuccess } from '../../console/logging.js';
|
|
|
2
2
|
import { noLocalesError, noDefaultLocaleError, noApiKeyError, noProjectIdError, devApiKeyError, invalidConfigurationError, } from '../../console/index.js';
|
|
3
3
|
import { aggregateFiles } from '../../formats/files/translate.js';
|
|
4
4
|
import { aggregateReactTranslations } from '../../translation/stage.js';
|
|
5
|
-
import {
|
|
5
|
+
import { stageFiles } from '../../workflow/stage.js';
|
|
6
6
|
import { updateVersions } from '../../fs/config/updateVersions.js';
|
|
7
7
|
import updateConfig from '../../fs/config/updateConfig.js';
|
|
8
8
|
import { hashStringSync } from '../../utils/hash.js';
|
|
@@ -62,6 +62,8 @@ export async function handleStage(options, settings, library, stage) {
|
|
|
62
62
|
content: JSON.stringify(fileData),
|
|
63
63
|
fileFormat: 'GTJSON',
|
|
64
64
|
formatMetadata: fileMetadata,
|
|
65
|
+
fileId: TEMPLATE_FILE_ID,
|
|
66
|
+
versionId: hashStringSync(JSON.stringify(fileData)),
|
|
65
67
|
});
|
|
66
68
|
}
|
|
67
69
|
}
|
|
@@ -76,19 +78,31 @@ export async function handleStage(options, settings, library, stage) {
|
|
|
76
78
|
})
|
|
77
79
|
.join('\n');
|
|
78
80
|
logSuccess(`Dry run: No files were sent to General Translation. Found files:\n${fileNames}`);
|
|
79
|
-
return
|
|
81
|
+
return null;
|
|
80
82
|
}
|
|
81
83
|
// Send translations to General Translation
|
|
82
|
-
let
|
|
84
|
+
let fileVersionData;
|
|
85
|
+
let jobData;
|
|
86
|
+
let branchData;
|
|
83
87
|
if (allFiles.length > 0) {
|
|
84
|
-
|
|
88
|
+
const { branchData: branchDataResult, enqueueResult } = await stageFiles(allFiles, options, settings);
|
|
89
|
+
jobData = enqueueResult;
|
|
90
|
+
branchData = branchDataResult;
|
|
91
|
+
fileVersionData = Object.fromEntries(allFiles.map((file) => [
|
|
92
|
+
file.fileId,
|
|
93
|
+
{
|
|
94
|
+
fileName: file.fileName,
|
|
95
|
+
versionId: file.versionId,
|
|
96
|
+
},
|
|
97
|
+
]));
|
|
98
|
+
// This logic is a little scuffed because stage is async from the API
|
|
85
99
|
if (stage) {
|
|
86
100
|
await updateVersions({
|
|
87
101
|
configDirectory: settings.configDirectory,
|
|
88
|
-
versionData:
|
|
102
|
+
versionData: fileVersionData,
|
|
89
103
|
});
|
|
90
104
|
}
|
|
91
|
-
const templateData =
|
|
105
|
+
const templateData = allFiles.find((file) => file.fileId === TEMPLATE_FILE_ID);
|
|
92
106
|
if (templateData?.versionId) {
|
|
93
107
|
await updateConfig({
|
|
94
108
|
configFilepath: settings.config,
|
|
@@ -96,5 +110,9 @@ export async function handleStage(options, settings, library, stage) {
|
|
|
96
110
|
});
|
|
97
111
|
}
|
|
98
112
|
}
|
|
99
|
-
return
|
|
113
|
+
return {
|
|
114
|
+
fileVersionData,
|
|
115
|
+
jobData,
|
|
116
|
+
branchData,
|
|
117
|
+
};
|
|
100
118
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EnqueueFilesResult } from 'generaltranslation/types';
|
|
2
2
|
import { TranslateFlags } from '../../types/index.js';
|
|
3
3
|
import { Settings } from '../../types/index.js';
|
|
4
|
-
|
|
4
|
+
import { FileTranslationData } from '../../workflow/download.js';
|
|
5
|
+
import { BranchData } from '../../types/branch.js';
|
|
6
|
+
export declare function handleTranslate(options: TranslateFlags, settings: Settings, fileVersionData: FileTranslationData | undefined, jobData: EnqueueFilesResult | undefined, branchData: BranchData | undefined): Promise<void>;
|
|
5
7
|
export declare function handleDownload(options: TranslateFlags, settings: Settings): Promise<void>;
|
|
6
8
|
export declare function postProcessTranslations(settings: Settings, includeFiles?: Set<string>): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { downloadTranslations, } from '../../workflow/download.js';
|
|
2
2
|
import { createFileMapping } from '../../formats/files/fileMapping.js';
|
|
3
3
|
import { logError } from '../../console/logging.js';
|
|
4
4
|
import { getStagedVersions } from '../../fs/config/updateVersions.js';
|
|
@@ -9,13 +9,12 @@ import processAnchorIds from '../../utils/processAnchorIds.js';
|
|
|
9
9
|
import { noFilesError, noVersionIdError } from '../../console/index.js';
|
|
10
10
|
import localizeStaticImports from '../../utils/localizeStaticImports.js';
|
|
11
11
|
// Downloads translations that were completed
|
|
12
|
-
export async function handleTranslate(options, settings,
|
|
13
|
-
if (
|
|
12
|
+
export async function handleTranslate(options, settings, fileVersionData, jobData, branchData) {
|
|
13
|
+
if (fileVersionData) {
|
|
14
14
|
const { resolvedPaths, placeholderPaths, transformPaths } = settings.files;
|
|
15
15
|
const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, settings.locales, settings.defaultLocale);
|
|
16
|
-
const { data } = filesTranslationResponse;
|
|
17
16
|
// Check for remaining translations
|
|
18
|
-
await
|
|
17
|
+
await downloadTranslations(fileVersionData, jobData, branchData, settings.locales, options.timeout, (sourcePath, locale) => fileMapping[locale]?.[sourcePath] ?? null, settings, options.force, options.forceDownload);
|
|
19
18
|
}
|
|
20
19
|
}
|
|
21
20
|
// Downloads translations that were originally staged
|
|
@@ -33,7 +32,7 @@ export async function handleDownload(options, settings) {
|
|
|
33
32
|
const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, settings.locales, settings.defaultLocale);
|
|
34
33
|
const stagedVersionData = await getStagedVersions(settings.configDirectory);
|
|
35
34
|
// Check for remaining translations
|
|
36
|
-
await
|
|
35
|
+
await downloadTranslations(stagedVersionData, undefined, undefined, settings.locales, options.timeout, (sourcePath, locale) => fileMapping[locale][sourcePath] ?? null, settings, false, // force is not applicable for downloading staged translations
|
|
37
36
|
options.forceDownload);
|
|
38
37
|
}
|
|
39
38
|
export async function postProcessTranslations(settings, includeFiles) {
|
package/dist/cli/flags.js
CHANGED
|
@@ -28,7 +28,10 @@ export function attachTranslateFlags(command) {
|
|
|
28
28
|
.option('--experimental-localize-static-imports', 'Triggering this will run a script after the cli tool that localizes all static imports in content files. Currently only supported for md and mdx files.', false)
|
|
29
29
|
.option('--force', 'Force a retranslation, invalidating all existing cached translations if they exist.', false)
|
|
30
30
|
.option('--force-download', 'Force download and overwrite local files, bypassing gt-lock.json checks.', false)
|
|
31
|
-
.option('--experimental-clear-locale-dirs', 'Clear locale directories before downloading new translations', false)
|
|
31
|
+
.option('--experimental-clear-locale-dirs', 'Clear locale directories before downloading new translations', false)
|
|
32
|
+
.option('--branch <branch>', 'Specify a custom branch to use for translations')
|
|
33
|
+
.option('--disable-branch-detection', 'Disable additional branch detection and optimizations and use the manually specified branch', false)
|
|
34
|
+
.option('--enable-branching', 'Enable branching for the project', false); // disabled by default for now
|
|
32
35
|
return command;
|
|
33
36
|
}
|
|
34
37
|
export function attachAdditionalReactTranslateFlags(command) {
|
|
@@ -159,6 +159,16 @@ export async function generateSettings(flags, cwd = process.cwd()) {
|
|
|
159
159
|
mergedOptions.parsingOptions = mergedOptions.parsingOptions || {};
|
|
160
160
|
mergedOptions.parsingOptions.conditionNames = mergedOptions.parsingOptions
|
|
161
161
|
.conditionNames || ['browser', 'module', 'import', 'require', 'default'];
|
|
162
|
+
// Add branch options if not provided
|
|
163
|
+
const branchOptions = mergedOptions.branchOptions || {};
|
|
164
|
+
branchOptions.enabled = flags.enableBranching ?? false;
|
|
165
|
+
branchOptions.currentBranch =
|
|
166
|
+
flags.branch ?? gtConfig.branchOptions?.currentBranch ?? undefined;
|
|
167
|
+
branchOptions.autoDetectBranches = flags.disableBranchDetection
|
|
168
|
+
? false
|
|
169
|
+
: true;
|
|
170
|
+
branchOptions.remoteName = gtConfig.branchOptions?.remoteName ?? 'origin';
|
|
171
|
+
mergedOptions.branchOptions = branchOptions;
|
|
162
172
|
// if there's no existing config file, creates one
|
|
163
173
|
// does not include the API key to avoid exposing it
|
|
164
174
|
if (!fs.existsSync(mergedOptions.config)) {
|
package/dist/console/colors.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export declare function colorizeFilepath(filepath: string): string;
|
|
2
2
|
export declare function colorizeComponent(component: string): string;
|
|
3
|
-
export declare function colorizeFunctionName(functionName: string): string;
|
|
4
3
|
export declare function colorizeIdString(id: string): string;
|
|
5
4
|
export declare function colorizeContent(content: string): string;
|
|
6
5
|
export declare function colorizeLine(line: string): string;
|
package/dist/console/colors.js
CHANGED
|
@@ -5,9 +5,6 @@ export function colorizeFilepath(filepath) {
|
|
|
5
5
|
export function colorizeComponent(component) {
|
|
6
6
|
return chalk.yellow(component);
|
|
7
7
|
}
|
|
8
|
-
export function colorizeFunctionName(functionName) {
|
|
9
|
-
return chalk.yellow(functionName);
|
|
10
|
-
}
|
|
11
8
|
export function colorizeIdString(id) {
|
|
12
9
|
return chalk.yellow(id);
|
|
13
10
|
}
|
package/dist/console/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export declare const warnApiKeyInConfigSync: (optionsFilepath: string) => string;
|
|
2
2
|
export declare const warnVariablePropSync: (file: string, attrName: string, value: string, location?: string) => string;
|
|
3
|
-
export declare const warnInvalidReturnSync: (file: string, functionName: string, expression: string, location?: string) => string;
|
|
4
|
-
export declare const warnMissingReturnSync: (file: string, functionName: string, location?: string) => string;
|
|
5
3
|
export declare const warnHasUnwrappedExpressionSync: (file: string, unwrappedExpressions: string[], id?: string, location?: string) => string;
|
|
6
4
|
export declare const warnNestedTComponent: (file: string, location?: string) => string;
|
|
7
5
|
export declare const warnNonStaticExpressionSync: (file: string, attrName: string, value: string, location?: string) => string;
|
|
@@ -12,10 +10,6 @@ export declare const warnAsyncUseGT: (file: string, location?: string) => string
|
|
|
12
10
|
export declare const warnSyncGetGT: (file: string, location?: string) => string;
|
|
13
11
|
export declare const warnTernarySync: (file: string, location?: string) => string;
|
|
14
12
|
export declare const withLocation: (file: string, message: string, location?: string) => string;
|
|
15
|
-
export declare const warnInvalidStaticChildSync: (file: string, location?: string) => string;
|
|
16
|
-
export declare const warnFunctionNotFoundSync: (file: string, functionName: string, location?: string) => string;
|
|
17
|
-
export declare const warnDuplicateFunctionDefinitionSync: (file: string, functionName: string, location?: string) => string;
|
|
18
|
-
export declare const warnInvalidStaticInitSync: (file: string, functionName: string, location?: string) => string;
|
|
19
13
|
export declare const noLocalesError = "No locales found! Please provide a list of locales to translate to, or specify them in your gt.config.json file.";
|
|
20
14
|
export declare const noDefaultLocaleError = "No default locale found! Please provide a default locale, or specify it in your gt.config.json file.";
|
|
21
15
|
export declare const noFilesError = "Incorrect or missing files configuration! Please make sure your files are configured correctly in your gt.config.json file.";
|
package/dist/console/index.js
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import { colorizeFilepath, colorizeComponent, colorizeIdString, colorizeContent, colorizeLine,
|
|
2
|
-
const withWillErrorInNextVersion = (message) => `${message} (This will become an error in the next major version of the CLI.)`;
|
|
3
|
-
// Static function related errors
|
|
4
|
-
const withStaticError = (message) => `<Static> rules violation: ${message}`;
|
|
1
|
+
import { colorizeFilepath, colorizeComponent, colorizeIdString, colorizeContent, colorizeLine, } from './colors.js';
|
|
5
2
|
// Synchronous wrappers for backward compatibility
|
|
6
3
|
export const warnApiKeyInConfigSync = (optionsFilepath) => `${colorizeFilepath(optionsFilepath)}: Your API key is exposed! Please remove it from the file and include it as an environment variable.`;
|
|
7
4
|
export const warnVariablePropSync = (file, attrName, value, location) => withLocation(file, `${colorizeComponent('<T>')} component has dynamic attribute ${colorizeIdString(attrName)} with value: ${colorizeContent(value)}. Change ${colorizeIdString(attrName)} to ensure this content is translated.`, location);
|
|
8
|
-
export const warnInvalidReturnSync = (file, functionName, expression, location) => withLocation(file, withStaticError(`Function ${colorizeFunctionName(functionName)} does not return a static expression. ${colorizeFunctionName(functionName)} must return either (1) a static string literal, (2) another static function invocation, (3) static JSX content, or (4) a ternary expression. Instead got:\n${colorizeContent(expression)}`), location);
|
|
9
|
-
// TODO: this is temporary until we handle implicit returns
|
|
10
|
-
export const warnMissingReturnSync = (file, functionName, location) => withLocation(file, `Function ${colorizeFunctionName(functionName)} is wrapped in ${colorizeComponent('<Static>')} tags but does have an explicit return statement. Static functions must have an explicit return statment.`, location);
|
|
11
5
|
export const warnHasUnwrappedExpressionSync = (file, unwrappedExpressions, id, location) => withLocation(file, `${colorizeComponent('<T>')} component${id ? ` with id ${colorizeIdString(id)}` : ''} has children that could change at runtime. Use a variable component like ${colorizeComponent('<Var>')} to ensure this content is translated.\n${colorizeContent(unwrappedExpressions.join('\n'))}`, location);
|
|
12
6
|
export const warnNestedTComponent = (file, location) => withLocation(file, `Found nested <T> component. <T> components cannot be directly nested.`, location);
|
|
13
7
|
export const warnNonStaticExpressionSync = (file, attrName, value, location) => withLocation(file, `Found non-static expression for attribute ${colorizeIdString(attrName)}: ${colorizeContent(value)}. Change "${colorizeIdString(attrName)}" to ensure this content is translated.`, location);
|
|
@@ -18,12 +12,7 @@ export const warnAsyncUseGT = (file, location) => withLocation(file, `Found useG
|
|
|
18
12
|
export const warnSyncGetGT = (file, location) => withLocation(file, `Found getGT() in a synchronous function. Use useGT() instead, or make the function async.`, location);
|
|
19
13
|
export const warnTernarySync = (file, location) => withLocation(file, 'Found ternary expression. A Branch component may be more appropriate here.', location);
|
|
20
14
|
export const withLocation = (file, message, location) => `${colorizeFilepath(file)}${location ? ` (${colorizeLine(location)})` : ''}: ${message}`;
|
|
21
|
-
|
|
22
|
-
export const warnFunctionNotFoundSync = (file, functionName, location) => withLocation(file, `Function ${colorizeFunctionName(functionName)} definition could not be resolved. This might affect translation resolution for this ${colorizeComponent('<T>')} component.`, location);
|
|
23
|
-
export const warnDuplicateFunctionDefinitionSync = (file, functionName, location) => withLocation(file, `Function ${colorizeFunctionName(functionName)} is defined multiple times. Only the first definition will be used.`, location);
|
|
24
|
-
export const warnInvalidStaticInitSync = (file, functionName, location) => withLocation(file, withStaticError(`The definition for ${colorizeFunctionName(functionName)} could not be resolved. When using arrow syntax to define a static function, the right hand side or the assignment MUST only contain the arrow function itself and no other expressions.
|
|
25
|
-
Example: ${colorizeContent(`const ${colorizeFunctionName(functionName)} = () => { ... }`)}
|
|
26
|
-
Invalid: ${colorizeContent(`const ${colorizeFunctionName(functionName)} = [() => { ... }][0]`)}`), location);
|
|
15
|
+
const withWillErrorInNextVersion = (message) => `${message} (This will become an error in the next major version of the CLI.)`;
|
|
27
16
|
// Re-export error messages
|
|
28
17
|
export const noLocalesError = `No locales found! Please provide a list of locales to translate to, or specify them in your gt.config.json file.`;
|
|
29
18
|
export const noDefaultLocaleError = `No default locale found! Please provide a default locale, or specify it in your gt.config.json file.`;
|
|
@@ -15,7 +15,7 @@ export declare function displayCreatedConfigFile(configFilepath: string): void;
|
|
|
15
15
|
export declare function displayUpdatedConfigFile(configFilepath: string): void;
|
|
16
16
|
export declare function displayUpdatedVersionsFile(versionFilepath: string): void;
|
|
17
17
|
export declare function createSpinner(indicator?: 'dots' | 'timer'): import("@clack/prompts").SpinnerResult;
|
|
18
|
-
export declare function
|
|
18
|
+
export declare function createProgressBar(total: number): import("@clack/prompts").ProgressResult;
|
|
19
19
|
export declare function promptText({ message, defaultValue, validate, }: {
|
|
20
20
|
message: string;
|
|
21
21
|
defaultValue?: string;
|
package/dist/console/logging.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { log, spinner, intro, outro, text, select, confirm, isCancel, cancel, multiselect, } from '@clack/prompts';
|
|
1
|
+
import { log, spinner, intro, outro, text, select, confirm, isCancel, cancel, multiselect, progress, } from '@clack/prompts';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { getCLIVersion } from '../utils/packageJson.js';
|
|
4
4
|
// Basic logging functions
|
|
@@ -81,9 +81,8 @@ export function createSpinner(indicator = 'timer') {
|
|
|
81
81
|
return spinner({ indicator });
|
|
82
82
|
}
|
|
83
83
|
// Spinner functionality
|
|
84
|
-
export
|
|
85
|
-
|
|
86
|
-
return ora.default({ spinner: indicator });
|
|
84
|
+
export function createProgressBar(total) {
|
|
85
|
+
return progress({ max: total });
|
|
87
86
|
}
|
|
88
87
|
// Input prompts
|
|
89
88
|
export async function promptText({ message, defaultValue, validate, }) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Settings } from '../../types/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { FileToUpload } from '../../types/data.js';
|
|
3
3
|
export declare const SUPPORTED_DATA_FORMATS: string[];
|
|
4
|
-
export declare function aggregateFiles(settings: Settings): Promise<
|
|
4
|
+
export declare function aggregateFiles(settings: Settings): Promise<FileToUpload[]>;
|
|
@@ -4,8 +4,10 @@ import { SUPPORTED_FILE_EXTENSIONS } from './supportedFiles.js';
|
|
|
4
4
|
import sanitizeFileContent from '../../utils/sanitizeFileContent.js';
|
|
5
5
|
import { parseJson } from '../json/parseJson.js';
|
|
6
6
|
import parseYaml from '../yaml/parseYaml.js';
|
|
7
|
+
import YAML from 'yaml';
|
|
7
8
|
import { determineLibrary } from '../../fs/determineFramework.js';
|
|
8
9
|
import { isValidMdx } from '../../utils/validateMdx.js';
|
|
10
|
+
import { hashStringSync } from '../../utils/hash.js';
|
|
9
11
|
export const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
|
|
10
12
|
export async function aggregateFiles(settings) {
|
|
11
13
|
// Aggregate all files to translate
|
|
@@ -39,8 +41,18 @@ export async function aggregateFiles(settings) {
|
|
|
39
41
|
.map((filePath) => {
|
|
40
42
|
const content = readFile(filePath);
|
|
41
43
|
const relativePath = getRelative(filePath);
|
|
44
|
+
// Pre-validate JSON parseability
|
|
45
|
+
try {
|
|
46
|
+
JSON.parse(content);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
logWarning(`Skipping ${relativePath}: JSON file is not parsable`);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
42
52
|
const parsedJson = parseJson(content, filePath, settings.options || {}, settings.defaultLocale);
|
|
43
53
|
return {
|
|
54
|
+
fileId: hashStringSync(relativePath),
|
|
55
|
+
versionId: hashStringSync(parsedJson),
|
|
44
56
|
content: parsedJson,
|
|
45
57
|
fileName: relativePath,
|
|
46
58
|
fileFormat: 'JSON',
|
|
@@ -48,13 +60,15 @@ export async function aggregateFiles(settings) {
|
|
|
48
60
|
};
|
|
49
61
|
})
|
|
50
62
|
.filter((file) => {
|
|
51
|
-
if (!file
|
|
52
|
-
|
|
63
|
+
if (!file)
|
|
64
|
+
return false;
|
|
65
|
+
if (typeof file.content !== 'string' || !file.content.trim()) {
|
|
66
|
+
logWarning(`Skipping ${file.fileName}: JSON file is empty`);
|
|
53
67
|
return false;
|
|
54
68
|
}
|
|
55
69
|
return true;
|
|
56
70
|
});
|
|
57
|
-
allFiles.push(...jsonFiles);
|
|
71
|
+
allFiles.push(...jsonFiles.filter((file) => file !== null));
|
|
58
72
|
}
|
|
59
73
|
// Process YAML files
|
|
60
74
|
if (filePaths.yaml) {
|
|
@@ -62,11 +76,21 @@ export async function aggregateFiles(settings) {
|
|
|
62
76
|
.map((filePath) => {
|
|
63
77
|
const content = readFile(filePath);
|
|
64
78
|
const relativePath = getRelative(filePath);
|
|
79
|
+
// Pre-validate YAML parseability
|
|
80
|
+
try {
|
|
81
|
+
YAML.parse(content);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
logWarning(`Skipping ${relativePath}: YAML file is not parsable`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
65
87
|
const { content: parsedYaml, fileFormat } = parseYaml(content, filePath, settings.options || {});
|
|
66
88
|
return {
|
|
67
89
|
content: parsedYaml,
|
|
68
90
|
fileName: relativePath,
|
|
69
91
|
fileFormat,
|
|
92
|
+
fileId: hashStringSync(relativePath),
|
|
93
|
+
versionId: hashStringSync(parsedYaml),
|
|
70
94
|
};
|
|
71
95
|
})
|
|
72
96
|
.filter((file) => {
|
|
@@ -76,7 +100,7 @@ export async function aggregateFiles(settings) {
|
|
|
76
100
|
}
|
|
77
101
|
return true;
|
|
78
102
|
});
|
|
79
|
-
allFiles.push(...yamlFiles);
|
|
103
|
+
allFiles.push(...yamlFiles.filter((file) => file !== null));
|
|
80
104
|
}
|
|
81
105
|
for (const fileType of SUPPORTED_FILE_EXTENSIONS) {
|
|
82
106
|
if (fileType === 'json' || fileType === 'yaml')
|
|
@@ -98,6 +122,8 @@ export async function aggregateFiles(settings) {
|
|
|
98
122
|
content: sanitizedContent,
|
|
99
123
|
fileName: relativePath,
|
|
100
124
|
fileFormat: fileType.toUpperCase(),
|
|
125
|
+
fileId: hashStringSync(relativePath),
|
|
126
|
+
versionId: hashStringSync(content),
|
|
101
127
|
};
|
|
102
128
|
})
|
|
103
129
|
.filter((file) => {
|
|
@@ -109,7 +135,7 @@ export async function aggregateFiles(settings) {
|
|
|
109
135
|
}
|
|
110
136
|
return true;
|
|
111
137
|
});
|
|
112
|
-
allFiles.push(...files);
|
|
138
|
+
allFiles.push(...files.filter((file) => file !== null));
|
|
113
139
|
}
|
|
114
140
|
}
|
|
115
141
|
if (allFiles.length === 0 && !settings.publish) {
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
export type DownloadedVersionEntry = {
|
|
2
|
-
versionId: string;
|
|
3
|
-
fileId?: string;
|
|
4
2
|
fileName?: string;
|
|
5
3
|
updatedAt?: string;
|
|
6
4
|
};
|
|
7
5
|
export type DownloadedVersions = {
|
|
8
6
|
version: number;
|
|
9
|
-
entries:
|
|
7
|
+
entries: {
|
|
8
|
+
[branchId: string]: {
|
|
9
|
+
[fileId: string]: {
|
|
10
|
+
[versionId: string]: {
|
|
11
|
+
[locale: string]: DownloadedVersionEntry;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
};
|
|
10
16
|
};
|
|
11
17
|
export declare function getDownloadedVersions(configDirectory: string): DownloadedVersions;
|
|
12
18
|
export declare function saveDownloadedVersions(configDirectory: string, lock: DownloadedVersions): void;
|
|
19
|
+
export declare function ensureNestedObject(obj: any, path: string[]): any;
|
|
@@ -40,3 +40,11 @@ export function saveDownloadedVersions(configDirectory, lock) {
|
|
|
40
40
|
logError(`An error occurred while updating ${GT_LOCK_FILE}: ${error}`);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
export function ensureNestedObject(obj, path) {
|
|
44
|
+
return path.reduce((current, key, index) => {
|
|
45
|
+
if (index === path.length - 1)
|
|
46
|
+
return current;
|
|
47
|
+
current[key] = current[key] || {};
|
|
48
|
+
return current[key];
|
|
49
|
+
}, obj);
|
|
50
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function getCurrentBranch(remoteName: string): Promise<{
|
|
2
|
+
currentBranchName: string;
|
|
3
|
+
defaultBranch: boolean;
|
|
4
|
+
defaultBranchName: string;
|
|
5
|
+
} | null>;
|
|
6
|
+
export declare function getIncomingBranches(remoteName: string): Promise<string[]>;
|
|
7
|
+
export declare function getCheckedOutBranches(remoteName: string): Promise<string[]>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
const execAsync = promisify(execFile);
|
|
4
|
+
const MAX_BRANCHES = 5;
|
|
5
|
+
export async function getCurrentBranch(remoteName) {
|
|
6
|
+
try {
|
|
7
|
+
const { stdout } = await execAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
8
|
+
encoding: 'utf8',
|
|
9
|
+
windowsHide: true,
|
|
10
|
+
});
|
|
11
|
+
const currentBranchName = stdout.trim();
|
|
12
|
+
// Get the default branch (usually main or master)
|
|
13
|
+
const { stdout: defaultBranchRef } = await execAsync('git', ['symbolic-ref', `refs/remotes/${remoteName}/HEAD`], { encoding: 'utf8', windowsHide: true });
|
|
14
|
+
const defaultBranchName = defaultBranchRef
|
|
15
|
+
.trim()
|
|
16
|
+
.replace(`refs/remotes/${remoteName}/`, '');
|
|
17
|
+
const defaultBranch = currentBranchName === defaultBranchName;
|
|
18
|
+
return { currentBranchName, defaultBranch, defaultBranchName };
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function getIncomingBranches(remoteName) {
|
|
25
|
+
try {
|
|
26
|
+
// Get merge commits into the current branch
|
|
27
|
+
const { stdout } = await execAsync('git', [
|
|
28
|
+
'log',
|
|
29
|
+
'--merges',
|
|
30
|
+
'--first-parent',
|
|
31
|
+
'--pretty=format:%s',
|
|
32
|
+
`-${MAX_BRANCHES}`,
|
|
33
|
+
], {
|
|
34
|
+
encoding: 'utf8',
|
|
35
|
+
windowsHide: true,
|
|
36
|
+
});
|
|
37
|
+
if (!stdout.trim()) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
const branches = [];
|
|
41
|
+
const lines = stdout.trim().split('\n');
|
|
42
|
+
for (const line of lines) {
|
|
43
|
+
// Parse merge commit messages:
|
|
44
|
+
// - "Merge branch 'feature-name'" or "Merge branch 'feature-name' into main"
|
|
45
|
+
// - "Merge pull request #123 from user/branch-name"
|
|
46
|
+
const branchMatch = line.match(/Merge branch '([^']+)'/);
|
|
47
|
+
const prMatch = line.match(/Merge pull request #\d+ from [^/]+\/(.+)/);
|
|
48
|
+
if (branchMatch && branchMatch[1]) {
|
|
49
|
+
branches.push(branchMatch[1]);
|
|
50
|
+
}
|
|
51
|
+
else if (prMatch && prMatch[1]) {
|
|
52
|
+
branches.push(prMatch[1]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return branches.slice(0, MAX_BRANCHES);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// If log fails or no merges found, return empty array
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export async function getCheckedOutBranches(remoteName) {
|
|
63
|
+
try {
|
|
64
|
+
// Get current branch
|
|
65
|
+
const currentBranchResult = await getCurrentBranch(remoteName);
|
|
66
|
+
if (!currentBranchResult) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
// If we're already on the default branch, return empty
|
|
70
|
+
if (currentBranchResult.defaultBranch) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
// Check if there's a merge-base (common ancestor) between default branch and current
|
|
74
|
+
// This means the branch was at some point checked out from the default branch
|
|
75
|
+
try {
|
|
76
|
+
await execAsync('git', ['merge-base', currentBranchResult.defaultBranchName, 'HEAD'], { encoding: 'utf8', windowsHide: true });
|
|
77
|
+
// If merge-base exists, the branch shares history with default branch
|
|
78
|
+
return [currentBranchResult.defaultBranchName];
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// No common ancestor found
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { JsxChildren } from 'generaltranslation/types';
|
|
2
|
-
import { MultipliedTreeNode } from './types.js';
|
|
3
2
|
/**
|
|
4
3
|
* Add GT Identifier and minify the tree (recreates addGTIdentifier and writeChildrenAsObjects)
|
|
5
4
|
* @param tree - The tree to add GT identifiers to
|
|
6
5
|
* @param startingIndex - The starting index for GT IDs
|
|
7
6
|
* @returns The tree with GT identifiers added
|
|
8
7
|
*/
|
|
9
|
-
export default function addGTIdentifierToSyntaxTree(tree:
|
|
8
|
+
export default function addGTIdentifierToSyntaxTree(tree: any, startingIndex?: number): JsxChildren;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HTML_CONTENT_PROPS, } from 'generaltranslation/types';
|
|
2
|
-
import { defaultVariableNames, getVariableName, minifyVariableType, } from '
|
|
2
|
+
import { defaultVariableNames, getVariableName, minifyVariableType, } from '../utils/getVariableName.js';
|
|
3
3
|
import { isAcceptedPluralForm } from 'generaltranslation/internal';
|
|
4
4
|
/**
|
|
5
5
|
* Construct the data-_gt prop
|
|
@@ -55,10 +55,6 @@ function constructGTProp(type, props, id) {
|
|
|
55
55
|
* @returns The tree with GT identifiers added
|
|
56
56
|
*/
|
|
57
57
|
export default function addGTIdentifierToSyntaxTree(tree, startingIndex = 0) {
|
|
58
|
-
// Edge case: boolean or null, just return the tree
|
|
59
|
-
if (typeof tree === 'boolean' || tree === null) {
|
|
60
|
-
return tree;
|
|
61
|
-
}
|
|
62
58
|
// Object to keep track of the current index for GT IDs
|
|
63
59
|
const indexObject = { index: startingIndex };
|
|
64
60
|
/**
|
|
@@ -87,23 +83,12 @@ export default function addGTIdentifierToSyntaxTree(tree, startingIndex = 0) {
|
|
|
87
83
|
};
|
|
88
84
|
}
|
|
89
85
|
// Construct the data-_gt prop
|
|
90
|
-
const generaltranslation = constructGTProp(type,
|
|
91
|
-
// Save current index and recurse
|
|
92
|
-
const currentIndex = indexObject.index;
|
|
93
|
-
const children = handleChildren(props?.children === undefined ? null : props?.children);
|
|
94
|
-
// Enforce boolean rendering behavior
|
|
95
|
-
// <T>{true}</T> -> true <- this is a boolean value, not a string
|
|
96
|
-
// <T>{false}</T> -> nothing
|
|
97
|
-
let includeChildren = true;
|
|
98
|
-
if (typeof children === 'boolean') {
|
|
99
|
-
// So, technically JsxChildren do include boolean values, but the type is incorrect
|
|
100
|
-
includeChildren = children !== false;
|
|
101
|
-
}
|
|
86
|
+
const generaltranslation = constructGTProp(type, props, indexObject.index);
|
|
102
87
|
// Return the result
|
|
103
88
|
return {
|
|
104
|
-
t: type || `C${
|
|
105
|
-
i:
|
|
106
|
-
|
|
89
|
+
t: type || `C${indexObject.index}`,
|
|
90
|
+
i: indexObject.index,
|
|
91
|
+
c: handleChildren(props.children),
|
|
107
92
|
...(generaltranslation && { d: generaltranslation }),
|
|
108
93
|
};
|
|
109
94
|
}
|
|
@@ -119,16 +104,7 @@ export default function addGTIdentifierToSyntaxTree(tree, startingIndex = 0) {
|
|
|
119
104
|
*/
|
|
120
105
|
const handleChildren = (children) => {
|
|
121
106
|
return Array.isArray(children)
|
|
122
|
-
? children.map(handleSingleChild)
|
|
123
|
-
// Enforce boolean rendering behavior
|
|
124
|
-
// <T>{true}{false}{null}</T> -> []
|
|
125
|
-
if (typeof child === 'boolean' ||
|
|
126
|
-
child === null ||
|
|
127
|
-
child === undefined) {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
return true;
|
|
131
|
-
})
|
|
107
|
+
? children.map(handleSingleChild)
|
|
132
108
|
: handleSingleChild(children);
|
|
133
109
|
};
|
|
134
110
|
return handleChildren(tree);
|
|
@@ -5,14 +5,15 @@ import * as t from '@babel/types';
|
|
|
5
5
|
* @returns Whether the node is meaningful
|
|
6
6
|
*/
|
|
7
7
|
export declare function isMeaningful(node: t.Node): boolean;
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Checks if an expression is static (does not contain any variables which could change at runtime).
|
|
10
|
+
* @param expr - The expression to check
|
|
11
|
+
* @returns An object containing the result of the static check
|
|
12
|
+
*/
|
|
13
|
+
export declare function isStaticExpression(expr: t.Expression | t.JSXEmptyExpression): {
|
|
9
14
|
isStatic: boolean;
|
|
10
15
|
value?: string;
|
|
11
16
|
};
|
|
12
|
-
export declare function isStaticExpression(expr: t.Expression | t.JSXEmptyExpression, jsxStatic?: true): {
|
|
13
|
-
isStatic: boolean;
|
|
14
|
-
value?: string | boolean | null;
|
|
15
|
-
};
|
|
16
17
|
export declare function isStaticValue(expr: t.Expression | t.JSXEmptyExpression): boolean;
|
|
17
18
|
export declare function isValidIcu(string: string): {
|
|
18
19
|
isValid: boolean;
|