gtx-cli 2.6.17 → 2.6.18

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.6.18
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1009](https://github.com/generaltranslation/gt/pull/1009) [`ffd6995`](https://github.com/generaltranslation/gt/commit/ffd6995d57e5444157071696e533ee29abcc2df4) Thanks [@pie575](https://github.com/pie575)! - added ai instructions on how to use the gt library
8
+
9
+ - [#1013](https://github.com/generaltranslation/gt/pull/1013) [`d01b29c`](https://github.com/generaltranslation/gt/commit/d01b29c0f5a4442f49a1ab6d8d5ee72403c6fa17) Thanks [@pie575](https://github.com/pie575)! - added flag to update-instructions add comments to ai files even if they weren't there before
10
+
11
+ - [#1007](https://github.com/generaltranslation/gt/pull/1007) [`7aaac80`](https://github.com/generaltranslation/gt/commit/7aaac80b67f4f6ffa8d9ef9aa00f6fc669596003) Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - chore: add gt-node support
12
+
3
13
  ## 2.6.17
4
14
 
5
15
  ### Patch Changes
@@ -30,4 +30,6 @@ export declare class BaseCLI {
30
30
  protected handleUploadCommand(settings: Settings & UploadOptions): Promise<void>;
31
31
  protected handleInitCommand(ranReactSetup: boolean, useDefaults?: boolean): Promise<void>;
32
32
  protected handleLoginCommand(options: LoginOptions): Promise<void>;
33
+ protected setupUpdateInstructionsCommand(): void;
34
+ protected promptAgentInstructions(useDefaults?: boolean): Promise<void>;
33
35
  }
package/dist/cli/base.js CHANGED
@@ -29,6 +29,8 @@ import processSharedStaticAssets, { mirrorAssetsToLocales, } from '../utils/shar
29
29
  import { setupLocadex } from '../locadex/setupFlow.js';
30
30
  import { detectFramework } from '../setup/detectFramework.js';
31
31
  import { getFrameworkDisplayName, getReactFrameworkLibrary, } from '../setup/frameworkUtils.js';
32
+ import { findAgentFiles, findAgentFilesWithInstructions, hasCursorRulesDir, CURSOR_GT_RULES_FILE, getAgentInstructions, appendAgentInstructions, } from '../setup/agentInstructions.js';
33
+ import { determineLibrary } from '../fs/determineFramework.js';
32
34
  export class BaseCLI {
33
35
  library;
34
36
  additionalModules;
@@ -43,6 +45,7 @@ export class BaseCLI {
43
45
  this.setupUploadCommand();
44
46
  this.setupLoginCommand();
45
47
  this.setupSendDiffsCommand();
48
+ this.setupUpdateInstructionsCommand();
46
49
  }
47
50
  // Init is never called in a child class
48
51
  init() {
@@ -158,13 +161,10 @@ export class BaseCLI {
158
161
  displayHeader('Authenticating with General Translation...');
159
162
  if (!options.keyType) {
160
163
  const packageJson = await searchForPackageJson();
161
- const isUsingGTNext = packageJson
162
- ? isPackageInstalled('gt-next', packageJson)
163
- : false;
164
- const isUsingGTReact = packageJson
165
- ? isPackageInstalled('gt-react', packageJson)
166
- : false;
167
- if (isUsingGTNext || isUsingGTReact) {
164
+ if (packageJson &&
165
+ (isPackageInstalled('gt-next', packageJson) ||
166
+ isPackageInstalled('gt-react', packageJson) ||
167
+ isPackageInstalled('gt-node', packageJson))) {
168
168
  options.keyType = 'development';
169
169
  }
170
170
  else {
@@ -209,6 +209,7 @@ export class BaseCLI {
209
209
  })();
210
210
  if (useAgent) {
211
211
  await setupLocadex(settings);
212
+ await this.promptAgentInstructions();
212
213
  logger.endCommand('Once installed, Locadex will open a PR to your repository. See the docs for more information: https://generaltranslation.com/docs/locadex');
213
214
  }
214
215
  else {
@@ -250,6 +251,7 @@ export class BaseCLI {
250
251
  }
251
252
  // Configure gt.config.json
252
253
  await this.handleInitCommand(ranReactSetup, useDefaults);
254
+ await this.promptAgentInstructions(useDefaults);
253
255
  logger.endCommand('Done! Check out our docs for more information on how to use General Translation: https://generaltranslation.com/docs');
254
256
  }
255
257
  });
@@ -294,14 +296,12 @@ export class BaseCLI {
294
296
  async handleInitCommand(ranReactSetup, useDefaults = false) {
295
297
  const { defaultLocale, locales } = await getDesiredLocales(); // Locales should still be asked for even if using defaults
296
298
  const packageJson = await searchForPackageJson();
297
- const isUsingGTNext = packageJson
298
- ? isPackageInstalled('gt-next', packageJson)
299
- : false;
300
- const isUsingGTReact = packageJson
301
- ? isPackageInstalled('gt-react', packageJson)
302
- : false;
303
299
  // Ask if using another i18n library
304
- const isUsingGT = isUsingGTNext || isUsingGTReact || ranReactSetup;
300
+ const gtInstalled = !!packageJson &&
301
+ (isPackageInstalled('gt-next', packageJson) ||
302
+ isPackageInstalled('gt-react', packageJson) ||
303
+ isPackageInstalled('gt-node', packageJson));
304
+ const isUsingGT = ranReactSetup || gtInstalled;
305
305
  // Ask where the translations are stored
306
306
  const usingCDN = await (async () => {
307
307
  if (!isUsingGT)
@@ -414,4 +414,69 @@ See https://generaltranslation.com/en/docs/next/guides/local-tx`);
414
414
  const credentials = await retrieveCredentials(settings, keyType);
415
415
  await setCredentials(credentials, keyType, settings.framework);
416
416
  }
417
+ setupUpdateInstructionsCommand() {
418
+ this.program
419
+ .command('update-instructions')
420
+ .description('Update GT usage instructions in AI agent files')
421
+ .option('--new', 'Add instructions to all agent files, even those without existing GT instructions')
422
+ .action(async (options) => {
423
+ const agentFiles = options.new
424
+ ? findAgentFiles()
425
+ : findAgentFilesWithInstructions();
426
+ if (options.new &&
427
+ hasCursorRulesDir() &&
428
+ !agentFiles.includes(CURSOR_GT_RULES_FILE)) {
429
+ agentFiles.push(CURSOR_GT_RULES_FILE);
430
+ }
431
+ if (agentFiles.length === 0) {
432
+ logger.warn(options.new
433
+ ? 'No agent files found. Create a CLAUDE.md or similar agent file first.'
434
+ : 'No agent files with GT instructions found. Use --new to add instructions to existing agent files.');
435
+ return;
436
+ }
437
+ const { library } = determineLibrary();
438
+ const instructions = getAgentInstructions(library);
439
+ let updatedCount = 0;
440
+ for (const file of agentFiles) {
441
+ if (appendAgentInstructions(file, instructions)) {
442
+ updatedCount++;
443
+ }
444
+ }
445
+ if (updatedCount > 0) {
446
+ logger.success(`Updated GT instructions in ${updatedCount} file${updatedCount > 1 ? 's' : ''}.`);
447
+ }
448
+ else {
449
+ logger.info('All agent instruction files are already up to date.');
450
+ }
451
+ });
452
+ }
453
+ async promptAgentInstructions(useDefaults = false) {
454
+ const agentFiles = findAgentFiles();
455
+ // Include .cursor/rules/gt-i18n.mdc if the directory exists but the file doesn't yet
456
+ if (hasCursorRulesDir() && !agentFiles.includes(CURSOR_GT_RULES_FILE)) {
457
+ agentFiles.push(CURSOR_GT_RULES_FILE);
458
+ }
459
+ if (agentFiles.length === 0)
460
+ return;
461
+ const addInstructions = useDefaults
462
+ ? true
463
+ : await promptConfirm({
464
+ message: `Found AI agent instruction files (${agentFiles.map((f) => path.basename(f)).join(', ')}). Would you like to add GT usage instructions?`,
465
+ defaultValue: true,
466
+ });
467
+ if (addInstructions) {
468
+ // Re-detect library since packages may have been installed during init
469
+ const { library } = determineLibrary();
470
+ const instructions = getAgentInstructions(library);
471
+ let updatedCount = 0;
472
+ for (const file of agentFiles) {
473
+ if (appendAgentInstructions(file, instructions)) {
474
+ updatedCount++;
475
+ }
476
+ }
477
+ if (updatedCount > 0) {
478
+ logger.success('Added GT instructions to agent files.');
479
+ }
480
+ }
481
+ }
417
482
  }
@@ -1,4 +1,15 @@
1
1
  import { Command } from 'commander';
2
2
  export declare function attachSharedFlags(command: Command): Command;
3
3
  export declare function attachTranslateFlags(command: Command): Command;
4
- export declare function attachAdditionalReactTranslateFlags(command: Command): Command;
4
+ /**
5
+ * Attaches flags necessary for validating a project
6
+ * @param command
7
+ * @returns The command with the validate flags attached
8
+ */
9
+ export declare function attachValidateFlags(command: Command): Command;
10
+ /**
11
+ * Attaches flags necessary for translating a project
12
+ * @param command
13
+ * @returns The command with the translate flags attached
14
+ */
15
+ export declare function attachInlineTranslateFlags(command: Command): Command;
package/dist/cli/flags.js CHANGED
@@ -40,12 +40,31 @@ export function attachTranslateFlags(command) {
40
40
  .option('--remote-name <name>', 'Specify a custom remote name to use for branch detection', DEFAULT_GIT_REMOTE_NAME);
41
41
  return command;
42
42
  }
43
- export function attachAdditionalReactTranslateFlags(command) {
44
- command
43
+ /**
44
+ * Attaches flags necessary for parsing inline content
45
+ * @param command - The command to attach the flags to
46
+ * @returns The command with the inline content parsing flags attached
47
+ */
48
+ function attachInlineContentParsingFlags(command) {
49
+ return command
45
50
  .option('--tsconfig, --jsconfig <path>', 'Path to custom jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
46
51
  .option('--dictionary <path>', 'Path to dictionary file')
47
52
  .option('--src <paths...>', "Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'")
48
- .option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
49
- .option('--ignore-errors', 'Ignore errors encountered while scanning for <T> tags', false);
50
- return command;
53
+ .option('--inline', 'Include inline content in translations (e.g., inline jsx translations, inline string translations, etc.)', true);
54
+ }
55
+ /**
56
+ * Attaches flags necessary for validating a project
57
+ * @param command
58
+ * @returns The command with the validate flags attached
59
+ */
60
+ export function attachValidateFlags(command) {
61
+ return attachInlineContentParsingFlags(command.option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json'])));
62
+ }
63
+ /**
64
+ * Attaches flags necessary for translating a project
65
+ * @param command
66
+ * @returns The command with the translate flags attached
67
+ */
68
+ export function attachInlineTranslateFlags(command) {
69
+ return attachInlineContentParsingFlags(command.option('--ignore-errors', 'Ignore errors encountered while scanning for inline content', false));
51
70
  }
@@ -0,0 +1,16 @@
1
+ import { Command } from 'commander';
2
+ import { BaseCLI } from './base.js';
3
+ import { SupportedLibraries, TranslateFlags, Options } from '../types/index.js';
4
+ /**
5
+ * Stand in for a CLI tool that does any sort of inline content translations
6
+ */
7
+ export declare class InlineCLI extends BaseCLI {
8
+ constructor(command: Command, library: 'gt-react' | 'gt-next' | 'gt-node', additionalModules?: SupportedLibraries[]);
9
+ init(): void;
10
+ protected setupStageCommand(): void;
11
+ protected setupTranslateCommand(): void;
12
+ protected setupValidateCommand(): void;
13
+ protected setupGenerateSourceCommand(): void;
14
+ protected handleGenerateSourceCommand(initOptions: TranslateFlags): Promise<void>;
15
+ protected handleValidate(initOptions: Options, files?: string[]): Promise<void>;
16
+ }
@@ -0,0 +1,128 @@
1
+ import { BaseCLI } from './base.js';
2
+ import { attachInlineTranslateFlags, attachTranslateFlags, attachValidateFlags, } from './flags.js';
3
+ import { displayHeader, exitSync } from '../console/logging.js';
4
+ import { logger } from '../console/logger.js';
5
+ import { intro } from '@clack/prompts';
6
+ import chalk from 'chalk';
7
+ import { resolveLocaleFiles } from '../fs/config/parseFilesConfig.js';
8
+ import { noFilesError } from '../console/index.js';
9
+ import { saveJSON } from '../fs/saveJSON.js';
10
+ import loadJSON from '../fs/loadJSON.js';
11
+ import { generateSettings } from '../config/generateSettings.js';
12
+ import { aggregateInlineTranslations } from '../translation/stage.js';
13
+ import { validateConfigExists } from '../config/validateSettings.js';
14
+ import { validateProject } from '../translation/validate.js';
15
+ /**
16
+ * Stand in for a CLI tool that does any sort of inline content translations
17
+ */
18
+ export class InlineCLI extends BaseCLI {
19
+ constructor(command, library, additionalModules) {
20
+ super(command, library, additionalModules);
21
+ }
22
+ init() {
23
+ this.setupStageCommand();
24
+ this.setupTranslateCommand();
25
+ this.setupGenerateSourceCommand();
26
+ this.setupValidateCommand();
27
+ }
28
+ setupStageCommand() {
29
+ attachInlineTranslateFlags(attachTranslateFlags(this.program
30
+ .command('stage')
31
+ .description('Submits the project to the General Translation API for translation. Translations created using this command will require human approval.'))).action(async (options) => {
32
+ displayHeader('Staging project for translation with approval required...');
33
+ await this.handleStage(options);
34
+ logger.endCommand('Done!');
35
+ });
36
+ }
37
+ setupTranslateCommand() {
38
+ attachInlineTranslateFlags(attachTranslateFlags(this.program
39
+ .command('translate')
40
+ .description('Scans the project for a dictionary and inline translations and sends the updates to the General Translation API for translation.'))).action(async (options) => {
41
+ displayHeader('Translating project...');
42
+ await this.handleTranslate(options);
43
+ logger.endCommand('Done!');
44
+ });
45
+ }
46
+ setupValidateCommand() {
47
+ attachValidateFlags(this.program
48
+ .command('validate [files...]')
49
+ .description('Scans the project for a dictionary and/or inline content and validates the project for errors.')).action(async (files, options) => {
50
+ // intro here since we don't want to show the ascii title
51
+ intro(chalk.cyan('Validating project...'));
52
+ await this.handleValidate(options, files);
53
+ logger.endCommand('Done!');
54
+ });
55
+ }
56
+ setupGenerateSourceCommand() {
57
+ attachInlineTranslateFlags(attachTranslateFlags(this.program
58
+ .command('generate')
59
+ .description('Generate a translation file for the source locale. This command should be used if you are handling your own translations.'))).action(async (initOptions) => {
60
+ displayHeader('Generating source templates...');
61
+ await this.handleGenerateSourceCommand(initOptions);
62
+ logger.endCommand('Done!');
63
+ });
64
+ }
65
+ async handleGenerateSourceCommand(initOptions) {
66
+ const settings = await generateSettings(initOptions);
67
+ const updates = await aggregateInlineTranslations(initOptions, settings, fallbackToGtReact(this.library));
68
+ // Convert updates to the proper data format
69
+ const newData = {};
70
+ for (const update of updates) {
71
+ const { source, metadata } = update;
72
+ const { hash, id } = metadata;
73
+ if (id) {
74
+ newData[id] = source;
75
+ }
76
+ else {
77
+ newData[hash] = source;
78
+ }
79
+ }
80
+ // Save source file if files.json is provided
81
+ if (settings.files && settings.files.placeholderPaths.gt) {
82
+ const translationFiles = resolveLocaleFiles(settings.files.placeholderPaths, settings.defaultLocale);
83
+ if (!translationFiles.gt) {
84
+ logger.error(noFilesError);
85
+ exitSync(1);
86
+ }
87
+ await saveJSON(translationFiles.gt, newData);
88
+ logger.step('Source file saved successfully!');
89
+ // Also save translations (after merging with existing translations)
90
+ for (const locale of settings.locales) {
91
+ const translationsFile = resolveLocaleFiles(settings.files.placeholderPaths, locale);
92
+ if (!translationsFile.gt) {
93
+ continue;
94
+ }
95
+ const existingTranslations = loadJSON(translationsFile.gt);
96
+ const mergedTranslations = {
97
+ ...newData,
98
+ ...existingTranslations,
99
+ };
100
+ // Filter out keys that don't exist in newData
101
+ const filteredTranslations = Object.fromEntries(Object.entries(mergedTranslations).filter(([key]) => newData[key]));
102
+ await saveJSON(translationsFile.gt, filteredTranslations);
103
+ }
104
+ logger.step('Merged translations successfully!');
105
+ }
106
+ }
107
+ async handleValidate(initOptions, files) {
108
+ validateConfigExists();
109
+ const settings = await generateSettings(initOptions);
110
+ // First run the base class's handleTranslate method
111
+ const options = { ...initOptions, ...settings };
112
+ // Fallback to gt-react
113
+ const pkg = fallbackToGtReact(this.library);
114
+ if (files && files.length > 0) {
115
+ // Validate specific files using createInlineUpdates
116
+ await validateProject(options, pkg, files);
117
+ }
118
+ else {
119
+ // Validate whole project as before
120
+ await validateProject(options, pkg);
121
+ }
122
+ }
123
+ }
124
+ function fallbackToGtReact(library) {
125
+ return ['gt-next', 'gt-node'].includes(library)
126
+ ? library
127
+ : 'gt-react';
128
+ }
@@ -3,8 +3,6 @@ import { ReactCLI } from './react.js';
3
3
  import { Command } from 'commander';
4
4
  export declare class NextCLI extends ReactCLI {
5
5
  constructor(command: Command, library: 'gt-next', additionalModules?: SupportedLibraries[]);
6
- init(): void;
7
- execute(): void;
8
6
  protected wrapContent(options: WrapOptions, framework: SupportedFrameworks, errors: string[], warnings: string[]): Promise<{
9
7
  filesUpdated: string[];
10
8
  }>;
package/dist/cli/next.js CHANGED
@@ -5,16 +5,6 @@ export class NextCLI extends ReactCLI {
5
5
  constructor(command, library, additionalModules) {
6
6
  super(command, library, additionalModules);
7
7
  }
8
- init() {
9
- this.setupSetupProjectCommand();
10
- this.setupStageCommand();
11
- this.setupTranslateCommand();
12
- this.setupGenerateSourceCommand();
13
- this.setupValidateCommand();
14
- }
15
- execute() {
16
- super.execute();
17
- }
18
8
  wrapContent(options, framework, errors, warnings) {
19
9
  return wrapContentNext(options, pkg, errors, warnings);
20
10
  }
@@ -0,0 +1,9 @@
1
+ import { Command } from 'commander';
2
+ import { SupportedLibraries } from '../types/index.js';
3
+ import { InlineCLI } from './inline.js';
4
+ /**
5
+ * CLI tool for managing translations with gt-node
6
+ */
7
+ export declare class NodeCLI extends InlineCLI {
8
+ constructor(command: Command, library: 'gt-node', additionalModules?: SupportedLibraries[]);
9
+ }
@@ -0,0 +1,9 @@
1
+ import { InlineCLI } from './inline.js';
2
+ /**
3
+ * CLI tool for managing translations with gt-node
4
+ */
5
+ export class NodeCLI extends InlineCLI {
6
+ constructor(command, library, additionalModules) {
7
+ super(command, library, additionalModules);
8
+ }
9
+ }
@@ -1,19 +1,12 @@
1
1
  import { Command } from 'commander';
2
- import { Options, SupportedFrameworks, WrapOptions, SupportedLibraries, TranslateFlags } from '../types/index.js';
3
- import { BaseCLI } from './base.js';
4
- export declare class ReactCLI extends BaseCLI {
2
+ import { SupportedFrameworks, WrapOptions, SupportedLibraries } from '../types/index.js';
3
+ import { InlineCLI } from './inline.js';
4
+ export declare class ReactCLI extends InlineCLI {
5
5
  constructor(command: Command, library: 'gt-react' | 'gt-next', additionalModules?: SupportedLibraries[]);
6
6
  init(): void;
7
- execute(): void;
8
7
  protected wrapContent(options: WrapOptions, framework: SupportedFrameworks, errors: string[], warnings: string[]): Promise<{
9
8
  filesUpdated: string[];
10
9
  }>;
11
10
  protected setupSetupProjectCommand(): void;
12
- protected setupStageCommand(): void;
13
- protected setupTranslateCommand(): void;
14
- protected setupValidateCommand(): void;
15
- protected setupGenerateSourceCommand(): void;
16
- protected handleGenerateSourceCommand(initOptions: TranslateFlags): Promise<void>;
17
11
  protected handleScanCommand(options: WrapOptions): Promise<void>;
18
- protected handleValidate(initOptions: Options, files?: string[]): Promise<void>;
19
12
  }
package/dist/cli/react.js CHANGED
@@ -1,40 +1,25 @@
1
1
  import { displayHeader, exitSync, promptConfirm } from '../console/logging.js';
2
2
  import { logger } from '../console/logger.js';
3
- import loadJSON from '../fs/loadJSON.js';
4
- import findFilepath from '../fs/findFilepath.js';
5
3
  import chalk from 'chalk';
6
4
  import { formatFiles } from '../hooks/postProcess.js';
7
- import { BaseCLI } from './base.js';
8
5
  import { wrapContentReact } from '../react/parse/wrapContent.js';
9
6
  import { generateSettings } from '../config/generateSettings.js';
10
- import { saveJSON } from '../fs/saveJSON.js';
11
- import { resolveLocaleFiles } from '../fs/config/parseFilesConfig.js';
12
- import { noFilesError } from '../console/index.js';
13
- import { aggregateReactTranslations } from '../translation/stage.js';
14
- import { validateConfigExists } from '../config/validateSettings.js';
15
- import { validateProject } from '../translation/validate.js';
16
- import { intro } from '@clack/prompts';
17
- import { attachAdditionalReactTranslateFlags, attachTranslateFlags, } from './flags.js';
7
+ import { attachInlineTranslateFlags, attachTranslateFlags } from './flags.js';
8
+ import { InlineCLI } from './inline.js';
18
9
  const pkg = 'gt-react';
19
- export class ReactCLI extends BaseCLI {
10
+ export class ReactCLI extends InlineCLI {
20
11
  constructor(command, library, additionalModules) {
21
12
  super(command, library, additionalModules);
22
13
  }
23
14
  init() {
15
+ super.init();
24
16
  this.setupSetupProjectCommand();
25
- this.setupStageCommand();
26
- this.setupTranslateCommand();
27
- this.setupGenerateSourceCommand();
28
- this.setupValidateCommand();
29
- }
30
- execute() {
31
- super.execute();
32
17
  }
33
18
  wrapContent(options, framework, errors, warnings) {
34
19
  return wrapContentReact(options, pkg, framework, errors, warnings);
35
20
  }
36
21
  setupSetupProjectCommand() {
37
- attachAdditionalReactTranslateFlags(attachTranslateFlags(this.program
22
+ attachInlineTranslateFlags(attachTranslateFlags(this.program
38
23
  .command('setup')
39
24
  .description('Upload source files and setup the project for translation'))).action(async (options) => {
40
25
  displayHeader('Uploading source files and setting up project...');
@@ -42,91 +27,6 @@ export class ReactCLI extends BaseCLI {
42
27
  logger.endCommand('Done!');
43
28
  });
44
29
  }
45
- setupStageCommand() {
46
- attachAdditionalReactTranslateFlags(attachTranslateFlags(this.program
47
- .command('stage')
48
- .description('Submits the project to the General Translation API for translation. Translations created using this command will require human approval.'))).action(async (options) => {
49
- displayHeader('Staging project for translation with approval required...');
50
- await this.handleStage(options);
51
- logger.endCommand('Done!');
52
- });
53
- }
54
- setupTranslateCommand() {
55
- attachAdditionalReactTranslateFlags(attachTranslateFlags(this.program
56
- .command('translate')
57
- .description('Scans the project for a dictionary and/or <T> tags, and sends the updates to the General Translation API for translation.'))).action(async (options) => {
58
- displayHeader('Translating project...');
59
- await this.handleTranslate(options);
60
- logger.endCommand('Done!');
61
- });
62
- }
63
- setupValidateCommand() {
64
- this.program
65
- .command('validate [files...]')
66
- .description('Scans the project for a dictionary and/or <T> tags, and validates the project for errors.')
67
- .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
68
- .option('--tsconfig, --jsconfig <path>', 'Path to jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
69
- .option('--dictionary <path>', 'Path to dictionary file')
70
- .option('--src <paths...>', "Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'")
71
- .option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
72
- .action(async (files, options) => {
73
- // intro here since we don't want to show the ascii title
74
- intro(chalk.cyan('Validating project...'));
75
- await this.handleValidate(options, files);
76
- logger.endCommand('Done!');
77
- });
78
- }
79
- setupGenerateSourceCommand() {
80
- attachAdditionalReactTranslateFlags(attachTranslateFlags(this.program
81
- .command('generate')
82
- .description('Generate a translation file for the source locale. This command should be used if you are handling your own translations.'))).action(async (initOptions) => {
83
- displayHeader('Generating source templates...');
84
- await this.handleGenerateSourceCommand(initOptions);
85
- logger.endCommand('Done!');
86
- });
87
- }
88
- async handleGenerateSourceCommand(initOptions) {
89
- const settings = await generateSettings(initOptions);
90
- const updates = await aggregateReactTranslations(initOptions, settings, this.library === 'gt-next' ? 'gt-next' : 'gt-react');
91
- // Convert updates to the proper data format
92
- const newData = {};
93
- for (const update of updates) {
94
- const { source, metadata } = update;
95
- const { hash, id } = metadata;
96
- if (id) {
97
- newData[id] = source;
98
- }
99
- else {
100
- newData[hash] = source;
101
- }
102
- }
103
- // Save source file if files.json is provided
104
- if (settings.files && settings.files.placeholderPaths.gt) {
105
- const translationFiles = resolveLocaleFiles(settings.files.placeholderPaths, settings.defaultLocale);
106
- if (!translationFiles.gt) {
107
- logger.error(noFilesError);
108
- exitSync(1);
109
- }
110
- await saveJSON(translationFiles.gt, newData);
111
- logger.step('Source file saved successfully!');
112
- // Also save translations (after merging with existing translations)
113
- for (const locale of settings.locales) {
114
- const translationsFile = resolveLocaleFiles(settings.files.placeholderPaths, locale);
115
- if (!translationsFile.gt) {
116
- continue;
117
- }
118
- const existingTranslations = loadJSON(translationsFile.gt);
119
- const mergedTranslations = {
120
- ...newData,
121
- ...existingTranslations,
122
- };
123
- // Filter out keys that don't exist in newData
124
- const filteredTranslations = Object.fromEntries(Object.entries(mergedTranslations).filter(([key]) => newData[key]));
125
- await saveJSON(translationsFile.gt, filteredTranslations);
126
- }
127
- logger.step('Merged translations successfully!');
128
- }
129
- }
130
30
  async handleScanCommand(options) {
131
31
  // Ask user for confirmation using inquirer
132
32
  const answer = await promptConfirm({
@@ -168,19 +68,4 @@ export class ReactCLI extends BaseCLI {
168
68
  .join('\n'));
169
69
  }
170
70
  }
171
- async handleValidate(initOptions, files) {
172
- validateConfigExists();
173
- const settings = await generateSettings(initOptions);
174
- // First run the base class's handleTranslate method
175
- const options = { ...initOptions, ...settings };
176
- const pkg = this.library === 'gt-next' ? 'gt-next' : 'gt-react';
177
- if (files && files.length > 0) {
178
- // Validate specific files using createInlineUpdates
179
- await validateProject(options, pkg, files);
180
- }
181
- else {
182
- // Validate whole project as before
183
- await validateProject(options, pkg);
184
- }
185
- }
186
71
  }
@@ -1,7 +1,7 @@
1
1
  import { logErrorAndExit } from '../../console/logging.js';
2
2
  import { invalidConfigurationError } from '../../console/index.js';
3
3
  import { aggregateFiles } from '../../formats/files/translate.js';
4
- import { aggregateReactTranslations } from '../../translation/stage.js';
4
+ import { aggregateInlineTranslations } from '../../translation/stage.js';
5
5
  import { hashStringSync } from '../../utils/hash.js';
6
6
  import { TEMPLATE_FILE_NAME, TEMPLATE_FILE_ID } from '../../utils/constants.js';
7
7
  export async function collectFiles(options, settings, library) {
@@ -9,8 +9,10 @@ export async function collectFiles(options, settings, library) {
9
9
  const allFiles = await aggregateFiles(settings);
10
10
  // Parse for React components
11
11
  let reactComponents = 0;
12
- if (library === 'gt-react' || library === 'gt-next') {
13
- const updates = await aggregateReactTranslations(options, settings, library);
12
+ if (library === 'gt-react' ||
13
+ library === 'gt-next' ||
14
+ library === 'gt-node') {
15
+ const updates = await aggregateInlineTranslations(options, settings, library);
14
16
  if (updates.length > 0) {
15
17
  if (!settings.publish && !settings.files?.placeholderPaths.gt) {
16
18
  logErrorAndExit(invalidConfigurationError);
@@ -27,6 +27,9 @@ export function determineLibrary() {
27
27
  else if (dependencies['gt-react']) {
28
28
  library = 'gt-react';
29
29
  }
30
+ else if (dependencies['gt-node']) {
31
+ library = 'gt-node';
32
+ }
30
33
  else if (dependencies['next-intl']) {
31
34
  library = 'next-intl';
32
35
  }
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.6.17";
1
+ export declare const PACKAGE_VERSION = "2.6.18";
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const PACKAGE_VERSION = '2.6.17';
2
+ export const PACKAGE_VERSION = '2.6.18';
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import { BaseCLI } from './cli/base.js';
2
2
  import { NextCLI } from './cli/next.js';
3
3
  import { ReactCLI } from './cli/react.js';
4
4
  import { determineLibrary } from './fs/determineFramework.js';
5
+ import { NodeCLI } from './cli/node.js';
5
6
  export function main(program) {
6
7
  program.name('gtx-cli');
7
8
  const { library, additionalModules } = determineLibrary();
@@ -12,6 +13,9 @@ export function main(program) {
12
13
  else if (library === 'gt-react') {
13
14
  cli = new ReactCLI(program, library, additionalModules);
14
15
  }
16
+ else if (library === 'gt-node') {
17
+ cli = new NodeCLI(program, library, additionalModules);
18
+ }
15
19
  else {
16
20
  cli = new BaseCLI(program, library, additionalModules);
17
21
  }