gtx-cli 2.4.15 → 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/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/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 +12 -7
- 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/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
|
@@ -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)) {
|
|
@@ -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[]>;
|
|
@@ -7,6 +7,7 @@ import parseYaml from '../yaml/parseYaml.js';
|
|
|
7
7
|
import YAML from 'yaml';
|
|
8
8
|
import { determineLibrary } from '../../fs/determineFramework.js';
|
|
9
9
|
import { isValidMdx } from '../../utils/validateMdx.js';
|
|
10
|
+
import { hashStringSync } from '../../utils/hash.js';
|
|
10
11
|
export const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
|
|
11
12
|
export async function aggregateFiles(settings) {
|
|
12
13
|
// Aggregate all files to translate
|
|
@@ -50,6 +51,8 @@ export async function aggregateFiles(settings) {
|
|
|
50
51
|
}
|
|
51
52
|
const parsedJson = parseJson(content, filePath, settings.options || {}, settings.defaultLocale);
|
|
52
53
|
return {
|
|
54
|
+
fileId: hashStringSync(relativePath),
|
|
55
|
+
versionId: hashStringSync(parsedJson),
|
|
53
56
|
content: parsedJson,
|
|
54
57
|
fileName: relativePath,
|
|
55
58
|
fileFormat: 'JSON',
|
|
@@ -65,7 +68,7 @@ export async function aggregateFiles(settings) {
|
|
|
65
68
|
}
|
|
66
69
|
return true;
|
|
67
70
|
});
|
|
68
|
-
allFiles.push(...jsonFiles);
|
|
71
|
+
allFiles.push(...jsonFiles.filter((file) => file !== null));
|
|
69
72
|
}
|
|
70
73
|
// Process YAML files
|
|
71
74
|
if (filePaths.yaml) {
|
|
@@ -86,18 +89,18 @@ export async function aggregateFiles(settings) {
|
|
|
86
89
|
content: parsedYaml,
|
|
87
90
|
fileName: relativePath,
|
|
88
91
|
fileFormat,
|
|
92
|
+
fileId: hashStringSync(relativePath),
|
|
93
|
+
versionId: hashStringSync(parsedYaml),
|
|
89
94
|
};
|
|
90
95
|
})
|
|
91
96
|
.filter((file) => {
|
|
92
|
-
if (!file)
|
|
93
|
-
|
|
94
|
-
if (typeof file.content !== 'string' || !file.content.trim()) {
|
|
95
|
-
logWarning(`Skipping ${file.fileName}: YAML file is empty`);
|
|
97
|
+
if (!file || typeof file.content !== 'string' || !file.content.trim()) {
|
|
98
|
+
logWarning(`Skipping ${file?.fileName ?? 'unknown'}: YAML file is empty`);
|
|
96
99
|
return false;
|
|
97
100
|
}
|
|
98
101
|
return true;
|
|
99
102
|
});
|
|
100
|
-
allFiles.push(...yamlFiles);
|
|
103
|
+
allFiles.push(...yamlFiles.filter((file) => file !== null));
|
|
101
104
|
}
|
|
102
105
|
for (const fileType of SUPPORTED_FILE_EXTENSIONS) {
|
|
103
106
|
if (fileType === 'json' || fileType === 'yaml')
|
|
@@ -119,6 +122,8 @@ export async function aggregateFiles(settings) {
|
|
|
119
122
|
content: sanitizedContent,
|
|
120
123
|
fileName: relativePath,
|
|
121
124
|
fileFormat: fileType.toUpperCase(),
|
|
125
|
+
fileId: hashStringSync(relativePath),
|
|
126
|
+
versionId: hashStringSync(content),
|
|
122
127
|
};
|
|
123
128
|
})
|
|
124
129
|
.filter((file) => {
|
|
@@ -130,7 +135,7 @@ export async function aggregateFiles(settings) {
|
|
|
130
135
|
}
|
|
131
136
|
return true;
|
|
132
137
|
});
|
|
133
|
-
allFiles.push(...files);
|
|
138
|
+
allFiles.push(...files.filter((file) => file !== null));
|
|
134
139
|
}
|
|
135
140
|
}
|
|
136
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/data.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type JSONDictionary = {
|
|
|
17
17
|
export type FlattenedJSONDictionary = {
|
|
18
18
|
[key: string]: string;
|
|
19
19
|
};
|
|
20
|
-
export type { FileFormat, DataFormat,
|
|
20
|
+
export type { FileFormat, DataFormat, FileToUpload, } from 'generaltranslation/types';
|
|
21
21
|
export type JsxChildren = string | string[] | any;
|
|
22
22
|
export type Translations = {
|
|
23
23
|
[key: string]: JsxChildren;
|
package/dist/types/files.d.ts
CHANGED
package/dist/types/index.d.ts
CHANGED
|
@@ -133,6 +133,13 @@ export type Settings = {
|
|
|
133
133
|
options?: AdditionalOptions;
|
|
134
134
|
modelProvider?: string;
|
|
135
135
|
parsingOptions: ParsingConfigOptions;
|
|
136
|
+
branchOptions: BranchOptions;
|
|
137
|
+
};
|
|
138
|
+
export type BranchOptions = {
|
|
139
|
+
currentBranch?: string;
|
|
140
|
+
autoDetectBranches?: boolean;
|
|
141
|
+
remoteName: string;
|
|
142
|
+
enabled: boolean;
|
|
136
143
|
};
|
|
137
144
|
export type AdditionalOptions = {
|
|
138
145
|
jsonSchema?: {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized spinner management for tracking multiple async operations
|
|
3
|
+
*/
|
|
4
|
+
export declare class SpinnerManager {
|
|
5
|
+
private spinners;
|
|
6
|
+
/**
|
|
7
|
+
* Run an async operation with a spinner
|
|
8
|
+
*/
|
|
9
|
+
run<T>(id: string, message: string, fn: () => Promise<T>): Promise<T>;
|
|
10
|
+
/**
|
|
11
|
+
* Mark a spinner as successful
|
|
12
|
+
*/
|
|
13
|
+
succeed(id: string, message: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Mark a spinner as warning
|
|
16
|
+
*/
|
|
17
|
+
warn(id: string, message: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Start a new spinner
|
|
20
|
+
*/
|
|
21
|
+
start(id: string, message: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Stop a specific spinner
|
|
24
|
+
*/
|
|
25
|
+
stop(id: string, message?: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* Stop all running spinners
|
|
28
|
+
*/
|
|
29
|
+
stopAll(): void;
|
|
30
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { createSpinner } from '../console/logging.js';
|
|
3
|
+
/**
|
|
4
|
+
* Centralized spinner management for tracking multiple async operations
|
|
5
|
+
*/
|
|
6
|
+
export class SpinnerManager {
|
|
7
|
+
spinners = new Map();
|
|
8
|
+
/**
|
|
9
|
+
* Run an async operation with a spinner
|
|
10
|
+
*/
|
|
11
|
+
async run(id, message, fn) {
|
|
12
|
+
const spinner = createSpinner('dots');
|
|
13
|
+
this.spinners.set(id, spinner);
|
|
14
|
+
spinner.start(message);
|
|
15
|
+
try {
|
|
16
|
+
const result = await fn();
|
|
17
|
+
spinner.stop(chalk.green('✓'));
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
spinner.stop(chalk.red('✗'));
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
this.spinners.delete(id);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Mark a spinner as successful
|
|
30
|
+
*/
|
|
31
|
+
succeed(id, message) {
|
|
32
|
+
const spinner = this.spinners.get(id);
|
|
33
|
+
if (spinner) {
|
|
34
|
+
spinner.stop(chalk.green(message));
|
|
35
|
+
this.spinners.delete(id);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Mark a spinner as warning
|
|
40
|
+
*/
|
|
41
|
+
warn(id, message) {
|
|
42
|
+
const spinner = this.spinners.get(id);
|
|
43
|
+
if (spinner) {
|
|
44
|
+
spinner.stop(chalk.yellow(message));
|
|
45
|
+
this.spinners.delete(id);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Start a new spinner
|
|
50
|
+
*/
|
|
51
|
+
start(id, message) {
|
|
52
|
+
const spinner = createSpinner('dots');
|
|
53
|
+
this.spinners.set(id, spinner);
|
|
54
|
+
spinner.start(message);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Stop a specific spinner
|
|
58
|
+
*/
|
|
59
|
+
stop(id, message) {
|
|
60
|
+
const spinner = this.spinners.get(id);
|
|
61
|
+
if (spinner) {
|
|
62
|
+
spinner.stop(message);
|
|
63
|
+
this.spinners.delete(id);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Stop all running spinners
|
|
68
|
+
*/
|
|
69
|
+
stopAll() {
|
|
70
|
+
this.spinners.forEach((s) => s.stop());
|
|
71
|
+
this.spinners.clear();
|
|
72
|
+
}
|
|
73
|
+
}
|
package/dist/utils/gitDiff.js
CHANGED
|
@@ -9,24 +9,26 @@ const execFileAsync = promisify(execFile);
|
|
|
9
9
|
* Throws if git is unavailable or another error occurs.
|
|
10
10
|
*/
|
|
11
11
|
export async function getGitUnifiedDiff(oldPath, newPath) {
|
|
12
|
-
|
|
13
|
-
'
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
try {
|
|
13
|
+
const res = await execFileAsync('git', [
|
|
14
|
+
'diff',
|
|
15
|
+
'--no-index',
|
|
16
|
+
'--text',
|
|
17
|
+
'--unified=3',
|
|
18
|
+
'--no-color',
|
|
19
|
+
'--',
|
|
20
|
+
oldPath,
|
|
21
|
+
newPath,
|
|
22
|
+
], {
|
|
23
|
+
windowsHide: true,
|
|
24
|
+
});
|
|
25
|
+
return res.stdout || '';
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
24
28
|
// Exit code 1 means differences found; stdout contains the diff
|
|
25
29
|
if (error && error.code === 1 && typeof error.stdout === 'string') {
|
|
26
|
-
return
|
|
30
|
+
return error.stdout;
|
|
27
31
|
}
|
|
28
32
|
throw error;
|
|
29
|
-
}
|
|
30
|
-
// When there are no changes, stdout is empty string and exit code 0
|
|
31
|
-
return res.stdout || '';
|
|
33
|
+
}
|
|
32
34
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { WorkflowStep } from './Workflow.js';
|
|
2
|
+
import { GT } from 'generaltranslation';
|
|
3
|
+
import { Settings } from '../types/index.js';
|
|
4
|
+
import { BranchData } from '../types/branch.js';
|
|
5
|
+
export declare class BranchStep extends WorkflowStep<null, BranchData | null> {
|
|
6
|
+
private spinner;
|
|
7
|
+
private branchData;
|
|
8
|
+
private settings;
|
|
9
|
+
private gt;
|
|
10
|
+
constructor(gt: GT, settings: Settings);
|
|
11
|
+
run(): Promise<BranchData | null>;
|
|
12
|
+
wait(): Promise<void>;
|
|
13
|
+
}
|