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.
Files changed (81) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/api/downloadFileBatch.js +5 -5
  3. package/dist/api/saveLocalEdits.js +4 -3
  4. package/dist/api/uploadFiles.d.ts +1 -1
  5. package/dist/api/uploadFiles.js +5 -4
  6. package/dist/cli/base.d.ts +2 -1
  7. package/dist/cli/base.js +36 -30
  8. package/dist/cli/commands/setupProject.d.ts +7 -0
  9. package/dist/cli/commands/setupProject.js +50 -0
  10. package/dist/cli/commands/stage.d.ts +0 -2
  11. package/dist/cli/commands/stage.js +13 -61
  12. package/dist/cli/commands/translate.js +3 -5
  13. package/dist/cli/flags.js +2 -3
  14. package/dist/cli/next.js +1 -0
  15. package/dist/cli/react.d.ts +1 -0
  16. package/dist/cli/react.js +26 -15
  17. package/dist/config/generateSettings.js +2 -2
  18. package/dist/config/validateSettings.d.ts +1 -1
  19. package/dist/config/validateSettings.js +4 -4
  20. package/dist/console/logger.d.ts +35 -0
  21. package/dist/console/logger.js +270 -0
  22. package/dist/console/logging.d.ts +6 -11
  23. package/dist/console/logging.js +40 -55
  24. package/dist/formats/files/collectFiles.d.ts +6 -0
  25. package/dist/formats/files/collectFiles.js +49 -0
  26. package/dist/formats/files/fileMapping.js +1 -1
  27. package/dist/formats/files/save.js +2 -2
  28. package/dist/formats/files/translate.js +8 -8
  29. package/dist/formats/files/upload.js +7 -6
  30. package/dist/formats/gt/save.js +4 -3
  31. package/dist/formats/json/flattenJson.js +3 -3
  32. package/dist/formats/json/mergeJson.js +19 -18
  33. package/dist/formats/json/parseJson.js +14 -13
  34. package/dist/formats/json/utils.js +16 -15
  35. package/dist/formats/yaml/mergeYaml.js +6 -5
  36. package/dist/formats/yaml/parseYaml.js +4 -3
  37. package/dist/formats/yaml/utils.js +4 -3
  38. package/dist/fs/clearLocaleDirs.js +6 -6
  39. package/dist/fs/config/downloadedVersions.js +3 -3
  40. package/dist/fs/config/parseFilesConfig.js +2 -2
  41. package/dist/fs/config/setupConfig.js +2 -2
  42. package/dist/fs/config/updateConfig.js +2 -2
  43. package/dist/fs/config/updateVersions.js +3 -3
  44. package/dist/fs/copyFile.js +2 -2
  45. package/dist/fs/createLoadTranslationsFile.d.ts +1 -1
  46. package/dist/fs/createLoadTranslationsFile.js +11 -4
  47. package/dist/fs/determineFramework.js +3 -3
  48. package/dist/fs/findFilepath.js +5 -4
  49. package/dist/hooks/postProcess.js +9 -9
  50. package/dist/next/parse/handleInitGT.js +2 -2
  51. package/dist/react/parse/addVitePlugin/index.js +4 -3
  52. package/dist/react/parse/addVitePlugin/installCompiler.js +2 -2
  53. package/dist/react/parse/addVitePlugin/updateViteConfig.js +9 -12
  54. package/dist/react/parse/createDictionaryUpdates.js +4 -3
  55. package/dist/react/parse/createInlineUpdates.js +2 -2
  56. package/dist/setup/wizard.js +17 -16
  57. package/dist/translation/parse.js +4 -3
  58. package/dist/translation/stage.js +4 -4
  59. package/dist/translation/validate.js +6 -6
  60. package/dist/types/index.d.ts +1 -0
  61. package/dist/utils/addExplicitAnchorIds.js +2 -2
  62. package/dist/utils/constants.d.ts +2 -0
  63. package/dist/utils/constants.js +3 -0
  64. package/dist/utils/credentials.js +4 -3
  65. package/dist/utils/installPackage.js +11 -11
  66. package/dist/utils/packageJson.js +4 -3
  67. package/dist/workflow/BranchStep.js +5 -4
  68. package/dist/workflow/DownloadStep.js +5 -5
  69. package/dist/workflow/EnqueueStep.js +2 -2
  70. package/dist/workflow/PollJobsStep.js +4 -4
  71. package/dist/workflow/SetupStep.d.ts +1 -1
  72. package/dist/workflow/SetupStep.js +4 -3
  73. package/dist/workflow/UploadStep.js +3 -3
  74. package/dist/workflow/UserEditDiffsStep.js +2 -2
  75. package/dist/workflow/download.js +4 -3
  76. package/dist/workflow/setupProject.d.ts +13 -0
  77. package/dist/workflow/setupProject.js +48 -0
  78. package/dist/workflow/stage.js +4 -21
  79. package/package.json +3 -3
  80. package/dist/utils/SpinnerManager.d.ts +0 -30
  81. package/dist/utils/SpinnerManager.js +0 -73
@@ -1,6 +1,6 @@
1
1
  import { detectFormatter } from '../hooks/postProcess.js';
2
- import { createSpinner, promptSelect } from '../console/logging.js';
3
- import { logInfo, logError, logStep, logWarning } from '../console/logging.js';
2
+ import { promptSelect } from '../console/logging.js';
3
+ import { logger } from '../console/logger.js';
4
4
  import chalk from 'chalk';
5
5
  import { promptConfirm } from '../console/logging.js';
6
6
  import findFilepath from '../fs/findFilepath.js';
@@ -13,6 +13,7 @@ import { installPackage } from '../utils/installPackage.js';
13
13
  import { createOrUpdateConfig } from '../fs/config/setupConfig.js';
14
14
  import { loadConfig } from '../fs/config/loadConfig.js';
15
15
  import { addVitePlugin } from '../react/parse/addVitePlugin/index.js';
16
+ import { exitSync } from '../console/logging.js';
16
17
  export async function handleSetupReactCommand(options) {
17
18
  // Ask user for confirmation using inquirer
18
19
  const answer = await promptConfirm({
@@ -24,8 +25,8 @@ Make sure you have committed or stashed any changes. Do you want to continue?`),
24
25
  cancelMessage: 'Operation cancelled. You can re-run this wizard with: npx gtx-cli setup',
25
26
  });
26
27
  if (!answer) {
27
- logInfo('Operation cancelled. You can re-run this wizard with: npx gtx-cli setup');
28
- process.exit(0);
28
+ logger.info('Operation cancelled. You can re-run this wizard with: npx gtx-cli setup');
29
+ exitSync(0);
29
30
  }
30
31
  const frameworkType = await promptSelect({
31
32
  message: 'What framework are you using?',
@@ -41,9 +42,9 @@ Make sure you have committed or stashed any changes. Do you want to continue?`),
41
42
  defaultValue: 'next-app',
42
43
  });
43
44
  if (frameworkType === 'other') {
44
- logError(`Sorry, other React frameworks are not currently supported.
45
+ logger.error(`Sorry, other React frameworks are not currently supported.
45
46
  Please let us know what you would like to see supported at https://github.com/generaltranslation/gt/issues`);
46
- process.exit(0);
47
+ exitSync(0);
47
48
  }
48
49
  // ----- Create a starter gt.config.json file -----
49
50
  await createOrUpdateConfig(options.config || 'gt.config.json', {
@@ -51,14 +52,14 @@ Please let us know what you would like to see supported at https://github.com/ge
51
52
  });
52
53
  const packageJson = await getPackageJson();
53
54
  if (!packageJson) {
54
- logError(chalk.red('No package.json found in the current directory. Please run this command from the root of your project.'));
55
- process.exit(1);
55
+ logger.error(chalk.red('No package.json found in the current directory. Please run this command from the root of your project.'));
56
+ exitSync(1);
56
57
  }
57
58
  // Check if gt-next or gt-react is installed
58
59
  if (frameworkType === 'next-app' &&
59
60
  !isPackageInstalled('gt-next', packageJson)) {
60
61
  const packageManager = await getPackageManager();
61
- const spinner = createSpinner('timer');
62
+ const spinner = logger.createSpinner('timer');
62
63
  spinner.start(`Installing gt-next with ${packageManager.name}...`);
63
64
  await installPackage('gt-next', packageManager);
64
65
  spinner.stop(chalk.green('Automatically installed gt-next.'));
@@ -66,7 +67,7 @@ Please let us know what you would like to see supported at https://github.com/ge
66
67
  else if (['next-pages', 'react', 'redwood', 'vite', 'gatsby'].includes(frameworkType) &&
67
68
  !isPackageInstalled('gt-react', packageJson)) {
68
69
  const packageManager = await getPackageManager();
69
- const spinner = createSpinner('timer');
70
+ const spinner = logger.createSpinner('timer');
70
71
  spinner.start(`Installing gt-react with ${packageManager.name}...`);
71
72
  await installPackage('gt-react', packageManager);
72
73
  spinner.stop(chalk.green('Automatically installed gt-react.'));
@@ -86,8 +87,8 @@ Please let us know what you would like to see supported at https://github.com/ge
86
87
  './next.config.mts',
87
88
  ]);
88
89
  if (!nextConfigPath) {
89
- logError('No next.config.[js|ts|mjs|mts] file found.');
90
- process.exit(1);
90
+ logger.error('No next.config.[js|ts|mjs|mts] file found.');
91
+ exitSync(1);
91
92
  }
92
93
  const mergeOptions = {
93
94
  ...options,
@@ -96,7 +97,7 @@ Please let us know what you would like to see supported at https://github.com/ge
96
97
  skipTs: true,
97
98
  addGTProvider: true,
98
99
  };
99
- const spinner = createSpinner();
100
+ const spinner = logger.createSpinner();
100
101
  spinner.start('Wrapping JSX content with <T> tags...');
101
102
  // Wrap all JSX elements in the src directory with a <T> tag, with unique ids
102
103
  const { filesUpdated: filesUpdatedNext } = await wrapContentNext(mergeOptions, 'gt-next', errors, warnings);
@@ -104,7 +105,7 @@ Please let us know what you would like to see supported at https://github.com/ge
104
105
  spinner.stop(chalk.green(`Success! Updated ${chalk.bold.cyan(filesUpdated.length)} files:\n`) + filesUpdated.map((file) => `${chalk.green('-')} ${file}`).join('\n'));
105
106
  // Add the withGTConfig() function to the next.config.js file
106
107
  await handleInitGT(nextConfigPath, errors, warnings, filesUpdated, packageJson, tsconfigJson);
107
- logStep(chalk.green(`Added withGTConfig() to your ${nextConfigPath} file.`));
108
+ logger.step(chalk.green(`Added withGTConfig() to your ${nextConfigPath} file.`));
108
109
  }
109
110
  // Add gt compiler plugin
110
111
  if (frameworkType === 'vite') {
@@ -117,10 +118,10 @@ Please let us know what you would like to see supported at https://github.com/ge
117
118
  });
118
119
  }
119
120
  if (errors.length > 0) {
120
- logError(chalk.red('Failed to write files:\n') + errors.join('\n'));
121
+ logger.error(chalk.red('Failed to write files:\n') + errors.join('\n'));
121
122
  }
122
123
  if (warnings.length > 0) {
123
- logWarning(chalk.yellow('Warnings encountered:') +
124
+ logger.warn(chalk.yellow('Warnings encountered:') +
124
125
  '\n' +
125
126
  warnings.map((warning) => `${chalk.yellow('-')} ${warning}`).join('\n'));
126
127
  }
@@ -1,10 +1,11 @@
1
1
  import fs from 'fs';
2
- import { logError } from '../console/logging.js';
2
+ import { logger } from '../console/logger.js';
3
3
  import loadJSON from '../fs/loadJSON.js';
4
4
  import { createDictionaryUpdates } from '../react/parse/createDictionaryUpdates.js';
5
5
  import { createInlineUpdates } from '../react/parse/createInlineUpdates.js';
6
6
  import createESBuildConfig from '../react/config/createESBuildConfig.js';
7
7
  import chalk from 'chalk';
8
+ import { exitSync } from '../console/logging.js';
8
9
  /**
9
10
  * Searches for gt-react or gt-next dictionary files and creates updates for them,
10
11
  * as well as inline updates for <T> tags and useGT()/getGT() calls
@@ -33,8 +34,8 @@ export async function createUpdates(options, src, sourceDictionary, pkg, validat
33
34
  if (options.jsconfig) {
34
35
  const jsconfig = loadJSON(options.jsconfig);
35
36
  if (!jsconfig) {
36
- logError(`Failed to resolve jsconfig.json or tsconfig.json at provided filepath: "${options.jsconfig}"`);
37
- process.exit(1);
37
+ logger.error(`Failed to resolve jsconfig.json or tsconfig.json at provided filepath: "${options.jsconfig}"`);
38
+ exitSync(1);
38
39
  }
39
40
  esbuildConfig = createESBuildConfig(jsconfig);
40
41
  }
@@ -1,7 +1,7 @@
1
1
  import { logErrorAndExit } from '../console/logging.js';
2
2
  import chalk from 'chalk';
3
3
  import findFilepath from '../fs/findFilepath.js';
4
- import { logWarning, logError } from '../console/logging.js';
4
+ import { logger } from '../console/logger.js';
5
5
  import { createUpdates } from './parse.js';
6
6
  export async function aggregateReactTranslations(options, settings, library) {
7
7
  if (!options.dictionary) {
@@ -17,14 +17,14 @@ export async function aggregateReactTranslations(options, settings, library) {
17
17
  // ---- CREATING UPDATES ---- //
18
18
  const { updates, errors, warnings } = await createUpdates(options, settings.src, options.dictionary, library, false, settings.parsingOptions);
19
19
  if (warnings.length > 0) {
20
- logWarning(chalk.yellow(`CLI tool encountered ${warnings.length} warnings while scanning for translatable content.\n` +
20
+ logger.warn(chalk.yellow(`CLI tool encountered ${warnings.length} warnings while scanning for translatable content.\n` +
21
21
  warnings
22
22
  .map((warning) => chalk.yellow('• Warning: ') + chalk.white(warning))
23
23
  .join('\n')));
24
24
  }
25
25
  if (errors.length > 0) {
26
26
  if (options.ignoreErrors) {
27
- logWarning(chalk.yellow(`Warning: CLI tool encountered ${errors.length} syntax errors while scanning for translatable content. These components will not be translated.\n` +
27
+ logger.warn(chalk.yellow(`Warning: CLI tool encountered ${errors.length} syntax errors while scanning for translatable content. These components will not be translated.\n` +
28
28
  errors
29
29
  .map((error) => chalk.yellow('• ') + chalk.white(error) + '\n')
30
30
  .join('')));
@@ -37,7 +37,7 @@ export async function aggregateReactTranslations(options, settings, library) {
37
37
  }
38
38
  }
39
39
  if (updates.length == 0) {
40
- logError(chalk.red(`No in-line content or dictionaries were found for ${chalk.green(library)}. Are you sure you're running this command in the right directory?`));
40
+ logger.error(chalk.red(`No in-line content or dictionaries were found for ${chalk.green(library)}. Are you sure you're running this command in the right directory?`));
41
41
  return updates;
42
42
  }
43
43
  return updates;
@@ -1,7 +1,7 @@
1
- import { logErrorAndExit, logWarning } from '../console/logging.js';
1
+ import { logErrorAndExit } from '../console/logging.js';
2
2
  import chalk from 'chalk';
3
3
  import findFilepath from '../fs/findFilepath.js';
4
- import { logError, logSuccess } from '../console/logging.js';
4
+ import { logger } from '../console/logger.js';
5
5
  import { createUpdates } from './parse.js';
6
6
  import { createInlineUpdates } from '../react/parse/createInlineUpdates.js';
7
7
  export async function validateProject(settings, pkg, files) {
@@ -14,7 +14,7 @@ export async function validateProject(settings, pkg, files) {
14
14
  .map((error) => chalk.red('• ') + chalk.white(error) + '\n')
15
15
  .join('')));
16
16
  }
17
- logSuccess(chalk.green(`Success! Found ${updates.length} translatable entries.`));
17
+ logger.success(chalk.green(`Success! Found ${updates.length} translatable entries.`));
18
18
  return;
19
19
  }
20
20
  if (!settings.dictionary) {
@@ -29,7 +29,7 @@ export async function validateProject(settings, pkg, files) {
29
29
  }
30
30
  const { updates, errors, warnings } = await createUpdates(settings, settings.src, settings.dictionary, pkg, true, settings.parsingOptions);
31
31
  if (warnings.length > 0) {
32
- logWarning(chalk.yellow(`CLI tool encountered ${warnings.length} warnings while scanning for translatable content.`) +
32
+ logger.warn(chalk.yellow(`CLI tool encountered ${warnings.length} warnings while scanning for translatable content.`) +
33
33
  '\n' +
34
34
  warnings
35
35
  .map((warning) => chalk.yellow('• ') + chalk.white(warning))
@@ -42,9 +42,9 @@ export async function validateProject(settings, pkg, files) {
42
42
  .join('')));
43
43
  }
44
44
  if (updates.length === 0) {
45
- logError(chalk.red(`No in-line content or dictionaries were found for ${chalk.green(pkg)}. Are you sure you're running this command in the right directory?`));
45
+ logger.error(chalk.red(`No in-line content or dictionaries were found for ${chalk.green(pkg)}. Are you sure you're running this command in the right directory?`));
46
46
  }
47
47
  else {
48
- logSuccess(chalk.green(`Success! Found ${updates.length} translatable entries for ${chalk.green(pkg)}.`));
48
+ logger.success(chalk.green(`Success! Found ${updates.length} translatable entries for ${chalk.green(pkg)}.`));
49
49
  }
50
50
  }
@@ -41,6 +41,7 @@ export type TranslateFlags = {
41
41
  dryRun: boolean;
42
42
  saveLocal?: boolean;
43
43
  stageTranslations?: boolean;
44
+ setupProject?: boolean;
44
45
  publish?: boolean;
45
46
  force?: boolean;
46
47
  forceDownload?: boolean;
@@ -4,7 +4,7 @@ import remarkMdx from 'remark-mdx';
4
4
  import remarkFrontmatter from 'remark-frontmatter';
5
5
  import remarkStringify from 'remark-stringify';
6
6
  import { visit } from 'unist-util-visit';
7
- import { logWarning } from '../console/logging.js';
7
+ import { logger } from '../console/logger.js';
8
8
  import escapeHtmlInTextNodes from 'gt-remark';
9
9
  /**
10
10
  * Generates a slug from heading text
@@ -90,7 +90,7 @@ export function addExplicitAnchorIds(translatedContent, sourceHeadingMap, settin
90
90
  const translatedFile = translatedPath
91
91
  ? `translated file: ${translatedPath}`
92
92
  : 'translated file';
93
- logWarning(`Header count mismatch detected! ${sourceFile} has ${sourceHeadingMap.length} headers but ${translatedFile} has ${translatedHeadings.length} headers. ` +
93
+ logger.warn(`Header count mismatch detected! ${sourceFile} has ${sourceHeadingMap.length} headers but ${translatedFile} has ${translatedHeadings.length} headers. ` +
94
94
  `This likely means your source file was edited after translation was requested, causing a mismatch between ` +
95
95
  `the number of headers in your source file vs the translated file. Please re-translate this file to resolve the issue.`);
96
96
  }
@@ -1,2 +1,4 @@
1
1
  export declare const GT_DASHBOARD_URL = "https://dash.generaltranslation.com";
2
2
  export declare const GT_CONFIG_SCHEMA_URL = "https://assets.gtx.dev/config-schema.json";
3
+ export declare const TEMPLATE_FILE_NAME = "__INTERNAL_GT_TEMPLATE_NAME__";
4
+ export declare const TEMPLATE_FILE_ID: string;
@@ -1,2 +1,5 @@
1
+ import { hashStringSync } from './hash.js';
1
2
  export const GT_DASHBOARD_URL = 'https://dash.generaltranslation.com';
2
3
  export const GT_CONFIG_SCHEMA_URL = 'https://assets.gtx.dev/config-schema.json';
4
+ export const TEMPLATE_FILE_NAME = '__INTERNAL_GT_TEMPLATE_NAME__';
5
+ export const TEMPLATE_FILE_ID = hashStringSync(TEMPLATE_FILE_NAME);
@@ -1,4 +1,5 @@
1
- import { createSpinner, logErrorAndExit, logMessage, } from '../console/logging.js';
1
+ import { logErrorAndExit } from '../console/logging.js';
2
+ import { logger } from '../console/logger.js';
2
3
  import path from 'node:path';
3
4
  import fs from 'node:fs';
4
5
  import chalk from 'chalk';
@@ -10,8 +11,8 @@ export async function retrieveCredentials(settings, keyType) {
10
11
  await import('open').then((open) => open.default(urlToOpen, {
11
12
  wait: false,
12
13
  }));
13
- logMessage(`${chalk.dim(`If the browser window didn't open automatically, please open the following link:`)}\n\n${chalk.cyan(urlToOpen)}`);
14
- const spinner = createSpinner('dots');
14
+ logger.message(`${chalk.dim(`If the browser window didn't open automatically, please open the following link:`)}\n\n${chalk.cyan(urlToOpen)}`);
15
+ const spinner = logger.createSpinner('dots');
15
16
  spinner.start('Waiting for response from dashboard...');
16
17
  const credentials = await new Promise(async (resolve, reject) => {
17
18
  const interval = setInterval(async () => {
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import { spawn } from 'child_process';
3
- import { logError, logInfo } from '../console/logging.js';
3
+ import { logger } from '../console/logger.js';
4
4
  export async function installPackage(packageName, packageManager, asDevDependency, cwd = process.cwd()) {
5
5
  return new Promise((resolve, reject) => {
6
6
  const command = packageManager.name;
@@ -19,8 +19,8 @@ export async function installPackage(packageName, packageManager, asDevDependenc
19
19
  });
20
20
  }
21
21
  childProcess.on('error', (error) => {
22
- logError(chalk.red(`Installation error: ${error.message}`));
23
- logInfo(`Please manually install ${packageName} with: ${packageManager.name} ${packageManager.installCommand} ${packageName}`);
22
+ logger.error(chalk.red(`Installation error: ${error.message}`));
23
+ logger.info(`Please manually install ${packageName} with: ${packageManager.name} ${packageManager.installCommand} ${packageName}`);
24
24
  reject(error);
25
25
  });
26
26
  childProcess.on('close', (code) => {
@@ -28,11 +28,11 @@ export async function installPackage(packageName, packageManager, asDevDependenc
28
28
  resolve();
29
29
  }
30
30
  else {
31
- logError(chalk.red(`Installation failed with exit code ${code}`));
31
+ logger.error(chalk.red(`Installation failed with exit code ${code}`));
32
32
  if (errorOutput) {
33
- logError(chalk.red(`Error details: ${errorOutput}`));
33
+ logger.error(chalk.red(`Error details: ${errorOutput}`));
34
34
  }
35
- logInfo(`Please manually install ${packageName} with: ${packageManager.name} ${packageManager.installCommand} ${packageName}`);
35
+ logger.info(`Please manually install ${packageName} with: ${packageManager.name} ${packageManager.installCommand} ${packageName}`);
36
36
  reject(new Error(`Process exited with code ${code}`));
37
37
  }
38
38
  });
@@ -56,8 +56,8 @@ export async function installPackageGlobal(packageName, version) {
56
56
  });
57
57
  }
58
58
  childProcess.on('error', (error) => {
59
- logError(chalk.red(`Installation error: ${error.message}`));
60
- logInfo(`Please manually install ${packageName} with: npm install -g ${packageName}`);
59
+ logger.error(chalk.red(`Installation error: ${error.message}`));
60
+ logger.info(`Please manually install ${packageName} with: npm install -g ${packageName}`);
61
61
  reject(error);
62
62
  });
63
63
  childProcess.on('close', (code) => {
@@ -65,11 +65,11 @@ export async function installPackageGlobal(packageName, version) {
65
65
  resolve();
66
66
  }
67
67
  else {
68
- logError(chalk.red(`Installation failed with exit code ${code}`));
68
+ logger.error(chalk.red(`Installation failed with exit code ${code}`));
69
69
  if (errorOutput) {
70
- logError(chalk.red(`Error details: ${errorOutput}`));
70
+ logger.error(chalk.red(`Error details: ${errorOutput}`));
71
71
  }
72
- logInfo(`Please manually install ${packageName} with: npm install -g ${packageName}`);
72
+ logger.info(`Please manually install ${packageName} with: npm install -g ${packageName}`);
73
73
  reject(new Error(`Process exited with code ${code}`));
74
74
  }
75
75
  });
@@ -1,8 +1,9 @@
1
- import { logError } from '../console/logging.js';
1
+ import { logger } from '../console/logger.js';
2
2
  import chalk from 'chalk';
3
3
  import path from 'node:path';
4
4
  import fs from 'node:fs';
5
5
  import { fromPackageRoot } from '../fs/getPackageResource.js';
6
+ import { exitSync } from '../console/logging.js';
6
7
  // search for package.json such that we can run init in non-js projects
7
8
  export async function searchForPackageJson(cwd = process.cwd()) {
8
9
  // Get the current working directory (where the CLI is being run)
@@ -48,8 +49,8 @@ export async function updatePackageJson(packageJson, cwd = process.cwd()) {
48
49
  await fs.promises.writeFile(path.join(cwd, 'package.json'), JSON.stringify(packageJson, null, 2));
49
50
  }
50
51
  catch (error) {
51
- logError(chalk.red('Error updating package.json: ' + String(error)));
52
- process.exit(1);
52
+ logger.error(chalk.red('Error updating package.json: ' + String(error)));
53
+ exitSync(1);
53
54
  }
54
55
  }
55
56
  // check if a package is installed in the package.json file
@@ -1,11 +1,12 @@
1
1
  import { WorkflowStep } from './Workflow.js';
2
- import { createSpinner, logError, logErrorAndExit, } from '../console/logging.js';
2
+ import { logErrorAndExit } from '../console/logging.js';
3
+ import { logger } from '../console/logger.js';
3
4
  import chalk from 'chalk';
4
5
  import { getCurrentBranch, getIncomingBranches, getCheckedOutBranches, } from '../git/branches.js';
5
6
  import { ApiError } from 'generaltranslation/errors';
6
7
  // Step 1: Resolve the current branch id & update API with branch information
7
8
  export class BranchStep extends WorkflowStep {
8
- spinner = createSpinner('dots');
9
+ spinner = logger.createSpinner('dots');
9
10
  branchData;
10
11
  settings;
11
12
  gt;
@@ -70,7 +71,7 @@ export class BranchStep extends WorkflowStep {
70
71
  }
71
72
  else {
72
73
  if (!current) {
73
- logErrorAndExit('Failed to determine the current branch. Please specify a custom branch or enable automatic branch detection.');
74
+ return logErrorAndExit('Failed to determine the current branch. Please specify a custom branch or enable automatic branch detection.');
74
75
  }
75
76
  const currentBranch = branchData.branches.find((b) => b.name === current.currentBranchName);
76
77
  if (!currentBranch) {
@@ -83,7 +84,7 @@ export class BranchStep extends WorkflowStep {
83
84
  }
84
85
  catch (error) {
85
86
  if (error instanceof ApiError && error.code === 403) {
86
- logError('Failed to create branch. To enable branching, please upgrade your plan.');
87
+ logger.error('Failed to create branch. To enable branching, please upgrade your plan.');
87
88
  // retry with default branch
88
89
  const createBranchResult = await this.gt.createBranch({
89
90
  branchName: 'main', // name doesn't matter for default branch
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import { WorkflowStep } from './Workflow.js';
3
- import { createProgressBar, logError, logWarning } from '../console/logging.js';
3
+ import { logger } from '../console/logger.js';
4
4
  import { downloadFileBatch, } from '../api/downloadFileBatch.js';
5
5
  export class DownloadTranslationsStep extends WorkflowStep {
6
6
  gt;
@@ -12,7 +12,7 @@ export class DownloadTranslationsStep extends WorkflowStep {
12
12
  this.settings = settings;
13
13
  }
14
14
  async run({ fileTracker, resolveOutputPath, forceDownload, }) {
15
- this.spinner = createProgressBar(fileTracker.completed.size);
15
+ this.spinner = logger.createProgressBar(fileTracker.completed.size);
16
16
  this.spinner.start('Downloading files...');
17
17
  // Download ready files
18
18
  const success = await this.downloadFiles(fileTracker, resolveOutputPath, forceDownload);
@@ -73,7 +73,7 @@ export class DownloadTranslationsStep extends WorkflowStep {
73
73
  const batchResult = await this.downloadFilesWithRetry(fileTracker, batchFiles, forceDownload);
74
74
  this.spinner?.stop(chalk.green(`Downloaded ${batchResult.successful.length} files${batchResult.skipped.length > 0 ? `, skipped ${batchResult.skipped.length} files` : ''}`));
75
75
  if (batchResult.failed.length > 0) {
76
- logWarning(`Failed to download ${batchResult.failed.length} files: ${batchResult.failed.map((f) => f.inputPath).join('\n')}`);
76
+ logger.warn(`Failed to download ${batchResult.failed.length} files: ${batchResult.failed.map((f) => f.inputPath).join('\n')}`);
77
77
  }
78
78
  }
79
79
  else {
@@ -83,7 +83,7 @@ export class DownloadTranslationsStep extends WorkflowStep {
83
83
  }
84
84
  catch (error) {
85
85
  this.spinner?.stop(chalk.red('An error occurred while downloading translations'));
86
- logError(chalk.red('Error: ') + error);
86
+ logger.error(chalk.red('Error: ') + error);
87
87
  return false;
88
88
  }
89
89
  }
@@ -109,7 +109,7 @@ export class DownloadTranslationsStep extends WorkflowStep {
109
109
  }
110
110
  // Calculate exponential backoff delay
111
111
  const delay = initialDelay * Math.pow(2, retryCount);
112
- logError(chalk.yellow(`Retrying ${batchResult.failed.length} failed file(s) in ${delay}ms (attempt ${retryCount + 1}/${maxRetries})...`));
112
+ logger.error(chalk.yellow(`Retrying ${batchResult.failed.length} failed file(s) in ${delay}ms (attempt ${retryCount + 1}/${maxRetries})...`));
113
113
  // Wait before retrying
114
114
  await new Promise((resolve) => setTimeout(resolve, delay));
115
115
  remainingFiles = batchResult.failed;
@@ -1,11 +1,11 @@
1
1
  import { WorkflowStep } from './Workflow.js';
2
- import { createSpinner } from '../console/logging.js';
2
+ import { logger } from '../console/logger.js';
3
3
  import chalk from 'chalk';
4
4
  export class EnqueueStep extends WorkflowStep {
5
5
  gt;
6
6
  settings;
7
7
  force;
8
- spinner = createSpinner('dots');
8
+ spinner = logger.createSpinner('dots');
9
9
  result = null;
10
10
  constructor(gt, settings, force) {
11
11
  super();
@@ -1,7 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import { WorkflowStep } from './Workflow.js';
3
- import { createProgressBar, logError } from '../console/logging.js';
4
- import { TEMPLATE_FILE_NAME } from '../cli/commands/stage.js';
3
+ import { logger } from '../console/logger.js';
4
+ import { TEMPLATE_FILE_NAME } from '../utils/constants.js';
5
5
  export class PollTranslationJobsStep extends WorkflowStep {
6
6
  gt;
7
7
  spinner = null;
@@ -12,7 +12,7 @@ export class PollTranslationJobsStep extends WorkflowStep {
12
12
  }
13
13
  async run({ fileTracker, fileQueryData, jobData, timeoutDuration, forceRetranslation, }) {
14
14
  const startTime = Date.now();
15
- this.spinner = createProgressBar(fileQueryData.length);
15
+ this.spinner = logger.createProgressBar(fileQueryData.length);
16
16
  const spinnerMessage = forceRetranslation
17
17
  ? 'Waiting for retranslation...'
18
18
  : 'Waiting for translation...';
@@ -144,7 +144,7 @@ export class PollTranslationJobsStep extends WorkflowStep {
144
144
  }
145
145
  }
146
146
  catch (error) {
147
- logError(chalk.red('Error checking job status: ') + error);
147
+ logger.error(chalk.red('Error checking job status: ') + error);
148
148
  }
149
149
  }, 5000);
150
150
  }, msUntilNextInterval);
@@ -11,6 +11,6 @@ export declare class SetupStep extends WorkflowStep<FileReference[], FileReferen
11
11
  private files;
12
12
  private completed;
13
13
  constructor(gt: GT, settings: Settings, timeoutMs: number);
14
- run(files: FileReference[]): Promise<FileReference[]>;
14
+ run(files: FileReference[], force?: boolean): Promise<FileReference[]>;
15
15
  wait(): Promise<void>;
16
16
  }
@@ -1,11 +1,11 @@
1
1
  import { WorkflowStep } from './Workflow.js';
2
- import { createSpinner } from '../console/logging.js';
2
+ import { logger } from '../console/logger.js';
3
3
  import chalk from 'chalk';
4
4
  export class SetupStep extends WorkflowStep {
5
5
  gt;
6
6
  settings;
7
7
  timeoutMs;
8
- spinner = createSpinner('dots');
8
+ spinner = logger.createSpinner('dots');
9
9
  setupJobId = null;
10
10
  files = null;
11
11
  completed = false;
@@ -15,7 +15,7 @@ export class SetupStep extends WorkflowStep {
15
15
  this.settings = settings;
16
16
  this.timeoutMs = timeoutMs;
17
17
  }
18
- async run(files) {
18
+ async run(files, force = false) {
19
19
  this.files = files;
20
20
  this.spinner.start('Setting up project...');
21
21
  if (files.length === 0) {
@@ -24,6 +24,7 @@ export class SetupStep extends WorkflowStep {
24
24
  }
25
25
  const result = await this.gt.setupProject(files, {
26
26
  locales: this.settings.locales,
27
+ force,
27
28
  });
28
29
  if (result.status === 'completed') {
29
30
  this.completed = true;
@@ -1,10 +1,10 @@
1
1
  import { WorkflowStep } from './Workflow.js';
2
- import { createSpinner, logInfo } from '../console/logging.js';
2
+ import { logger } from '../console/logger.js';
3
3
  import chalk from 'chalk';
4
4
  export class UploadStep extends WorkflowStep {
5
5
  gt;
6
6
  settings;
7
- spinner = createSpinner('dots');
7
+ spinner = logger.createSpinner('dots');
8
8
  result = null;
9
9
  constructor(gt, settings) {
10
10
  super();
@@ -13,7 +13,7 @@ export class UploadStep extends WorkflowStep {
13
13
  }
14
14
  async run({ files, branchData, }) {
15
15
  if (files.length === 0) {
16
- logInfo('No files to upload found... skipping upload step');
16
+ logger.info('No files to upload found... skipping upload step');
17
17
  return [];
18
18
  }
19
19
  this.spinner.start(`Syncing ${files.length} file${files.length !== 1 ? 's' : ''} with General Translation API...`);
@@ -1,10 +1,10 @@
1
1
  import { WorkflowStep } from './Workflow.js';
2
- import { createSpinner } from '../console/logging.js';
2
+ import { logger } from '../console/logger.js';
3
3
  import chalk from 'chalk';
4
4
  import { collectAndSendUserEditDiffs } from '../api/collectUserEditDiffs.js';
5
5
  export class UserEditDiffsStep extends WorkflowStep {
6
6
  settings;
7
- spinner = createSpinner('dots');
7
+ spinner = logger.createSpinner('dots');
8
8
  completed = false;
9
9
  constructor(settings) {
10
10
  super();
@@ -3,7 +3,8 @@ import { gt } from '../utils/gt.js';
3
3
  import { clearLocaleDirs } from '../fs/clearLocaleDirs.js';
4
4
  import { PollTranslationJobsStep } from './PollJobsStep.js';
5
5
  import { DownloadTranslationsStep } from './DownloadStep.js';
6
- import { logError, logErrorAndExit } from '../console/logging.js';
6
+ import { logErrorAndExit } from '../console/logging.js';
7
+ import { logger } from '../console/logger.js';
7
8
  import { BranchStep } from './BranchStep.js';
8
9
  import chalk from 'chalk';
9
10
  /**
@@ -25,7 +26,7 @@ export async function downloadTranslations(fileVersionData, jobData, branchData,
25
26
  const branchResult = await branchStep.run();
26
27
  await branchStep.wait();
27
28
  if (!branchResult) {
28
- logErrorAndExit('Failed to resolve git branch information.');
29
+ return logErrorAndExit('Failed to resolve git branch information.');
29
30
  }
30
31
  branchData = branchResult;
31
32
  }
@@ -66,7 +67,7 @@ export async function downloadTranslations(fileVersionData, jobData, branchData,
66
67
  });
67
68
  await pollStep.wait();
68
69
  if (pollResult.fileTracker.failed.size > 0) {
69
- logError(`${chalk.red(`${pollResult.fileTracker.failed.size} file(s) failed to translate:`)}\n${Array.from(pollResult.fileTracker.failed.entries())
70
+ logger.error(`${chalk.red(`${pollResult.fileTracker.failed.size} file(s) failed to translate:`)}\n${Array.from(pollResult.fileTracker.failed.entries())
70
71
  .map(([key, value]) => `- ${value.fileName}`)
71
72
  .join('\n')}`);
72
73
  return false;
@@ -0,0 +1,13 @@
1
+ import { Settings, TranslateFlags } from '../types/index.js';
2
+ import { FileToUpload } from 'generaltranslation/types';
3
+ import { BranchData } from '../types/branch.js';
4
+ /**
5
+ * Sets up a project by uploading files running the setup step
6
+ * @param files - Array of file objects to upload
7
+ * @param options - The options for the API call
8
+ * @param settings - Settings configuration
9
+ * @returns The branch data
10
+ */
11
+ export declare function setupProject(files: FileToUpload[], options: TranslateFlags, settings: Settings): Promise<{
12
+ branchData: BranchData;
13
+ }>;
@@ -0,0 +1,48 @@
1
+ import { logErrorAndExit } from '../console/logging.js';
2
+ import { gt } from '../utils/gt.js';
3
+ import { UploadStep } from './UploadStep.js';
4
+ import { SetupStep } from './SetupStep.js';
5
+ import { BranchStep } from './BranchStep.js';
6
+ import { logCollectedFiles } from '../console/logging.js';
7
+ /**
8
+ * Helper: Calculate timeout with validation
9
+ */
10
+ function calculateTimeout(timeout) {
11
+ const value = timeout !== undefined ? Number(timeout) : 600;
12
+ return (Number.isFinite(value) ? value : 600) * 1000;
13
+ }
14
+ /**
15
+ * Sets up a project by uploading files running the setup step
16
+ * @param files - Array of file objects to upload
17
+ * @param options - The options for the API call
18
+ * @param settings - Settings configuration
19
+ * @returns The branch data
20
+ */
21
+ export async function setupProject(files, options, settings) {
22
+ try {
23
+ // Log files to be translated
24
+ logCollectedFiles(files);
25
+ // Calculate timeout for setup step
26
+ const timeoutMs = calculateTimeout(options.timeout);
27
+ // Create workflow with steps
28
+ const branchStep = new BranchStep(gt, settings);
29
+ const uploadStep = new UploadStep(gt, settings);
30
+ const setupStep = new SetupStep(gt, settings, timeoutMs);
31
+ // first run the branch step
32
+ const branchData = await branchStep.run();
33
+ await branchStep.wait();
34
+ if (!branchData) {
35
+ return logErrorAndExit('Failed to resolve git branch information.');
36
+ }
37
+ // then run the upload step
38
+ const uploadedFiles = await uploadStep.run({ files, branchData });
39
+ await uploadStep.wait();
40
+ // then run the setup step
41
+ await setupStep.run(uploadedFiles, options.force ?? false);
42
+ await setupStep.wait();
43
+ return { branchData };
44
+ }
45
+ catch (error) {
46
+ return logErrorAndExit('Failed to run project setup. ' + error);
47
+ }
48
+ }