gtx-cli 1.2.7 → 1.2.8-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,9 @@ export type TranslateOptions = {
7
7
  projectId?: string;
8
8
  dryRun: boolean;
9
9
  };
10
+ export type LoginOptions = {
11
+ keyType?: 'development' | 'production';
12
+ };
10
13
  export declare class BaseCLI {
11
14
  protected library: SupportedLibraries;
12
15
  protected additionalModules: SupportedLibraries[];
@@ -21,5 +24,5 @@ export declare class BaseCLI {
21
24
  protected handleGenericTranslate(settings: Settings & TranslateOptions): Promise<void>;
22
25
  protected handleSetupReactCommand(options: SetupOptions): Promise<void>;
23
26
  protected handleInitCommand(ranReactSetup: boolean): Promise<void>;
24
- protected handleLoginCommand(): Promise<void>;
27
+ protected handleLoginCommand(options: LoginOptions): Promise<void>;
25
28
  }
package/dist/cli/base.js CHANGED
@@ -98,10 +98,32 @@ class BaseCLI {
98
98
  .command('auth')
99
99
  .description('Generate a General Translation API key and project ID')
100
100
  .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
101
- .action(async () => {
101
+ .option('-t, --key-type <type>', 'Type of key to generate, production | development')
102
+ .action(async (options) => {
102
103
  (0, console_1.displayHeader)('Authenticating with General Translation...');
103
- await this.handleLoginCommand();
104
- (0, console_1.endCommand)('Done!');
104
+ if (!options.keyType) {
105
+ const packageJson = await (0, packageJson_1.searchForPackageJson)();
106
+ const isUsingGTNext = packageJson
107
+ ? (0, packageJson_1.isPackageInstalled)('gt-next', packageJson)
108
+ : false;
109
+ const isUsingGTReact = packageJson
110
+ ? (0, packageJson_1.isPackageInstalled)('gt-react', packageJson)
111
+ : false;
112
+ if (isUsingGTNext || isUsingGTReact) {
113
+ options.keyType = 'development';
114
+ }
115
+ else {
116
+ options.keyType = 'production';
117
+ }
118
+ }
119
+ else {
120
+ if (options.keyType !== 'development' &&
121
+ options.keyType !== 'production') {
122
+ (0, console_1.logErrorAndExit)('Invalid key type, must be development or production');
123
+ }
124
+ }
125
+ await this.handleLoginCommand(options);
126
+ (0, console_1.endCommand)(`Done! A ${options.keyType} key has been generated and saved to your .env.local file.`);
105
127
  });
106
128
  }
107
129
  setupInitCommand() {
@@ -109,7 +131,7 @@ class BaseCLI {
109
131
  .command('init')
110
132
  .description('Run the setup wizard to configure your project for General Translation')
111
133
  .option('--src <paths...>', "Filepath to directory containing the app's source code, by default ./src || ./app || ./pages || ./components", (0, findFilepath_1.findFilepaths)(['./src', './app', './pages', './components']))
112
- .option('--config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
134
+ .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
113
135
  .action(async (options) => {
114
136
  (0, console_1.displayHeader)('Running setup wizard...');
115
137
  const packageJson = await (0, packageJson_1.searchForPackageJson)();
@@ -154,7 +176,7 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
154
176
  .command('setup')
155
177
  .description('Run the setup to configure your Next.js or React project for General Translation')
156
178
  .option('--src <paths...>', "Filepath to directory containing the app's source code, by default ./src || ./app || ./pages || ./components", (0, findFilepath_1.findFilepaths)(['./src', './app', './pages', './components']))
157
- .option('--config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
179
+ .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
158
180
  .action(async (options) => {
159
181
  (0, console_1.displayHeader)('Running React setup wizard...');
160
182
  await this.handleSetupReactCommand(options);
@@ -207,12 +229,12 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
207
229
  ? await (0, console_1.promptConfirm)({
208
230
  message: `Auto-detected that you're using gt-next or gt-react. Would you like to use the General Translation CDN to store your translations?\nSee ${isUsingGTNext
209
231
  ? 'https://generaltranslation.com/docs/next/reference/local-tx'
210
- : 'https://generaltranslation.com/docs/react/reference/local-tx'} for more information.\nIf you answer no, we'll setup your project to store translations locally.`,
232
+ : 'https://generaltranslation.com/docs/react/reference/local-tx'} for more information.\nIf you answer no, we'll configure the CLI tool to download completed translations.`,
211
233
  defaultValue: true,
212
234
  })
213
235
  : false;
214
236
  if (isUsingGT && !usingCDN) {
215
- (0, console_1.logMessage)(`To block translations from being published, please disable the project setting on the dashboard: ${chalk_1.default.cyan('https://dash.generaltranslation.com/settings/project')}`);
237
+ (0, console_1.logMessage)(`To prevent translations from being published, please disable the project setting on the dashboard: ${chalk_1.default.cyan('https://dash.generaltranslation.com/settings/project')}`);
216
238
  }
217
239
  // Ask where the translations are stored
218
240
  const translationsDir = isUsingGT && !usingCDN
@@ -276,18 +298,22 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
276
298
  // Set credentials
277
299
  if (!(0, credentials_2.areCredentialsSet)()) {
278
300
  const loginQuestion = await (0, console_1.promptConfirm)({
279
- message: 'Would you like the wizard to automatically generate an API key and project ID for you?',
301
+ message: `Would you like the wizard to automatically generate a ${isUsingGT ? 'development' : 'production'} API key and project ID for you?`,
280
302
  defaultValue: true,
281
303
  });
282
304
  if (loginQuestion) {
283
- await this.handleLoginCommand();
305
+ const settings = await (0, generateSettings_1.generateSettings)({});
306
+ const keyType = isUsingGT ? 'development' : 'production';
307
+ const credentials = await (0, credentials_1.retrieveCredentials)(settings, keyType);
308
+ await (0, credentials_1.setCredentials)(credentials, keyType, settings.framework);
284
309
  }
285
310
  }
286
311
  }
287
- async handleLoginCommand() {
312
+ async handleLoginCommand(options) {
288
313
  const settings = await (0, generateSettings_1.generateSettings)({});
289
- const credentials = await (0, credentials_1.retrieveCredentials)(settings);
290
- await (0, credentials_1.setCredentials)(credentials);
314
+ const keyType = options.keyType || 'production';
315
+ const credentials = await (0, credentials_1.retrieveCredentials)(settings, keyType);
316
+ await (0, credentials_1.setCredentials)(credentials, keyType, settings.framework);
291
317
  }
292
318
  }
293
319
  exports.BaseCLI = BaseCLI;
package/dist/cli/react.js CHANGED
@@ -77,7 +77,7 @@ class ReactCLI extends base_1.BaseCLI {
77
77
  commander_1.program
78
78
  .command('stage')
79
79
  .description('Submits the project to the General Translation API for translation. Translations created using this command will require human approval.')
80
- .option('--config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
80
+ .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
81
81
  .option('--api-key <key>', 'API key for General Translation cloud service')
82
82
  .option('--project-id <id>', 'Project ID for the translation service', (0, utils_1.resolveProjectId)())
83
83
  .option('--version-id <id>', 'Version ID for the translation service')
@@ -100,7 +100,7 @@ class ReactCLI extends base_1.BaseCLI {
100
100
  commander_1.program
101
101
  .command('translate')
102
102
  .description('Scans the project for a dictionary and/or <T> tags, and sends the updates to the General Translation API for translation.')
103
- .option('--config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
103
+ .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
104
104
  .option('--api-key <key>', 'API key for General Translation cloud service')
105
105
  .option('--project-id <id>', 'Project ID for the translation service', (0, utils_1.resolveProjectId)())
106
106
  .option('--version-id <id>', 'Version ID for the translation service')
@@ -141,7 +141,7 @@ class ReactCLI extends base_1.BaseCLI {
141
141
  .command('scan')
142
142
  .description('Scans the project and wraps all JSX elements in the src directory with a <T> tag, with unique ids')
143
143
  .option('--src <paths...>', "Filepath to directory containing the app's source code, by default ./src || ./app || ./pages || ./components", (0, findFilepath_1.findFilepaths)(['./src', './app', './pages', './components']))
144
- .option('--config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
144
+ .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', (0, findFilepath_1.default)(['gt.config.json']))
145
145
  .option('--disable-ids', 'Disable id generation for the <T> tags', false)
146
146
  .option('--disable-formatting', 'Disable formatting of edited files', false)
147
147
  .action(async (options) => {
@@ -168,14 +168,16 @@ class ReactCLI extends base_1.BaseCLI {
168
168
  const { updates, errors } = await (0, parse_1.createUpdates)(options, options.dictionary, this.library === 'gt-next' ? 'gt-next' : 'gt-react');
169
169
  if (errors.length > 0) {
170
170
  if (options.ignoreErrors) {
171
- (0, console_1.logWarning)(chalk_1.default.red(`CLI tool encountered errors while scanning for ${chalk_1.default.green('<T>')} tags. These components will not be translated.\n` +
171
+ (0, console_1.logWarning)(chalk_1.default.yellow(`CLI tool encountered errors while scanning for translatable content. These components will not be translated.\n` +
172
172
  errors
173
- .map((error) => chalk_1.default.yellow('• Warning: ') + error)
173
+ .map((error) => chalk_1.default.yellow('• Warning: ') + chalk_1.default.white(error))
174
174
  .join('\n')));
175
175
  }
176
176
  else {
177
- (0, console_1.logErrorAndExit)(chalk_1.default.red(`CLI tool encountered errors while scanning for ${chalk_1.default.green('<T>')} tags. ${chalk_1.default.gray('To ignore these errors, re-run with --ignore-errors')}\n` +
178
- errors.map((error) => chalk_1.default.red('• Error: ') + error).join('\n')));
177
+ (0, console_1.logErrorAndExit)(chalk_1.default.red(`CLI tool encountered errors while scanning for translatable content. ${chalk_1.default.gray('To ignore these errors, re-run with --ignore-errors')}\n` +
178
+ errors
179
+ .map((error) => chalk_1.default.red('• Error: ') + chalk_1.default.white(error))
180
+ .join('\n')));
179
181
  }
180
182
  }
181
183
  // Convert updates to the proper data format
@@ -86,6 +86,7 @@ async function generateSettings(options) {
86
86
  projectId: mergedOptions.projectId,
87
87
  defaultLocale: mergedOptions.defaultLocale,
88
88
  locales: mergedOptions.locales?.length > 0 ? mergedOptions.locales : undefined,
89
+ framework: mergedOptions.framework,
89
90
  });
90
91
  }
91
92
  (0, validateSettings_1.validateSettings)(mergedOptions);
@@ -0,0 +1,5 @@
1
+ export declare function colorizeFilepath(filepath: string): string;
2
+ export declare function colorizeComponent(component: string): string;
3
+ export declare function colorizeIdString(id: string): string;
4
+ export declare function colorizeContent(content: string): string;
5
+ export declare function colorizeLine(line: string): string;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.colorizeFilepath = colorizeFilepath;
7
+ exports.colorizeComponent = colorizeComponent;
8
+ exports.colorizeIdString = colorizeIdString;
9
+ exports.colorizeContent = colorizeContent;
10
+ exports.colorizeLine = colorizeLine;
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ function colorizeFilepath(filepath) {
13
+ return chalk_1.default.cyan(filepath);
14
+ }
15
+ function colorizeComponent(component) {
16
+ return chalk_1.default.yellow(component);
17
+ }
18
+ function colorizeIdString(id) {
19
+ return chalk_1.default.yellow(id);
20
+ }
21
+ function colorizeContent(content) {
22
+ return chalk_1.default.yellow(content);
23
+ }
24
+ function colorizeLine(line) {
25
+ return chalk_1.default.gray(line);
26
+ }
@@ -1,11 +1,12 @@
1
1
  export * from './logging';
2
2
  export declare const warnApiKeyInConfigSync: (optionsFilepath: string) => string;
3
- export declare const warnVariablePropSync: (file: string, attrName: string, value: string) => string;
4
- export declare const warnNoIdSync: (file: string) => string;
5
- export declare const warnHasUnwrappedExpressionSync: (file: string, id: string, unwrappedExpressions: string[]) => string;
6
- export declare const warnNonStaticExpressionSync: (file: string, attrName: string, value: string) => string;
7
- export declare const warnTemplateLiteralSync: (file: string, value: string) => string;
8
- export declare const warnTernarySync: (file: string) => string;
3
+ export declare const warnVariablePropSync: (file: string, attrName: string, value: string, location?: string) => string;
4
+ export declare const warnHasUnwrappedExpressionSync: (file: string, unwrappedExpressions: string[], id?: string, location?: string) => string;
5
+ export declare const warnNonStaticExpressionSync: (file: string, attrName: string, value: string, location?: string) => string;
6
+ export declare const warnTemplateLiteralSync: (file: string, value: string, location?: string) => string;
7
+ export declare const warnNonStringSync: (file: string, value: string, location?: string) => string;
8
+ export declare const warnTernarySync: (file: string, location?: string) => string;
9
+ export declare const withLocation: (file: string, message: string, location?: string) => string;
9
10
  export declare const noLocalesError = "No locales found! Please provide a list of locales to translate to, or specify them in your gt.config.json file.";
10
11
  export declare const noDefaultLocaleError = "No default locale found! Please provide a default locale, or specify it in your gt.config.json file.";
11
12
  export declare const noFilesError = "Incorrect or missing files configuration! Please make sure your files are configured correctly in your gt.config.json file.";
@@ -13,5 +14,6 @@ export declare const noSourceFileError = "No source file found! Please double ch
13
14
  export declare const noDataFormatError = "No data format found! Please make sure your translationsDir parameter ends with a supported file extension.";
14
15
  export declare const noSupportedDataFormatError = "Unsupported data format! Please make sure your translationsDir parameter ends with a supported file extension.";
15
16
  export declare const noApiKeyError = "No API key found! Please provide an API key using the --api-key flag or set the GT_API_KEY environment variable.";
17
+ export declare const devApiKeyError = "You are using a development API key. Please use a production API key to send files to General Translation.\nYou can generate a production API key with the command: npx gtx-cli auth -t production";
16
18
  export declare const noProjectIdError = "No project ID found! Please provide a project ID using the --project-id flag, specify it in your gt.config.json file, or set the GT_PROJECT_ID environment variable.";
17
19
  export declare const noVersionIdError = "No version ID found! Please provide a version ID using the --version-id flag or specify it in your gt.config.json file as the _versionId property.";
@@ -14,24 +14,27 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.noVersionIdError = exports.noProjectIdError = exports.noApiKeyError = exports.noSupportedDataFormatError = exports.noDataFormatError = exports.noSourceFileError = exports.noFilesError = exports.noDefaultLocaleError = exports.noLocalesError = exports.warnTernarySync = exports.warnTemplateLiteralSync = exports.warnNonStaticExpressionSync = exports.warnHasUnwrappedExpressionSync = exports.warnNoIdSync = exports.warnVariablePropSync = exports.warnApiKeyInConfigSync = void 0;
17
+ exports.noVersionIdError = exports.noProjectIdError = exports.devApiKeyError = exports.noApiKeyError = exports.noSupportedDataFormatError = exports.noDataFormatError = exports.noSourceFileError = exports.noFilesError = exports.noDefaultLocaleError = exports.noLocalesError = exports.withLocation = exports.warnTernarySync = exports.warnNonStringSync = exports.warnTemplateLiteralSync = exports.warnNonStaticExpressionSync = exports.warnHasUnwrappedExpressionSync = exports.warnVariablePropSync = exports.warnApiKeyInConfigSync = void 0;
18
18
  // Export all logging functions
19
19
  __exportStar(require("./logging"), exports);
20
+ const colors_1 = require("./colors");
20
21
  // Synchronous wrappers for backward compatibility
21
- const warnApiKeyInConfigSync = (optionsFilepath) => `Found apiKey in "${optionsFilepath}". Your API key is exposed! Please remove it from the file and include it as an environment variable.`;
22
+ const warnApiKeyInConfigSync = (optionsFilepath) => `${(0, colors_1.colorizeFilepath)(optionsFilepath)}: Your API key is exposed! Please remove it from the file and include it as an environment variable.`;
22
23
  exports.warnApiKeyInConfigSync = warnApiKeyInConfigSync;
23
- const warnVariablePropSync = (file, attrName, value) => `Found <T> component in ${file} with variable ${attrName}: "${value}". Change "${attrName}" to ensure this content is translated.`;
24
+ const warnVariablePropSync = (file, attrName, value, location) => (0, exports.withLocation)(file, `${(0, colors_1.colorizeComponent)('<T>')} component has dynamic attribute ${(0, colors_1.colorizeIdString)(attrName)} with value: ${(0, colors_1.colorizeContent)(value)}. Change ${(0, colors_1.colorizeIdString)(attrName)} to ensure this content is translated.`, location);
24
25
  exports.warnVariablePropSync = warnVariablePropSync;
25
- const warnNoIdSync = (file) => `Found <T> component in ${file} with no id. Add an id to ensure the content is translated.`;
26
- exports.warnNoIdSync = warnNoIdSync;
27
- const warnHasUnwrappedExpressionSync = (file, id, unwrappedExpressions) => `<T> with id "${id}" in ${file} has children: ${unwrappedExpressions.join(', ')} that could change at runtime. Use a variable component like <Var> to translate this properly.`;
26
+ const warnHasUnwrappedExpressionSync = (file, unwrappedExpressions, id, location) => (0, exports.withLocation)(file, `${(0, colors_1.colorizeComponent)('<T>')} component${id ? ` with id ${(0, colors_1.colorizeIdString)(id)}` : ''} has children that could change at runtime. Use a variable component like ${(0, colors_1.colorizeComponent)('<Var>')} to ensure this content is translated.\n${(0, colors_1.colorizeContent)(unwrappedExpressions.join('\n'))}`, location);
28
27
  exports.warnHasUnwrappedExpressionSync = warnHasUnwrappedExpressionSync;
29
- const warnNonStaticExpressionSync = (file, attrName, value) => `Found non-static expression in ${file} for attribute ${attrName}: "${value}". Change "${attrName}" to ensure this content is translated.`;
28
+ const warnNonStaticExpressionSync = (file, attrName, value, location) => (0, exports.withLocation)(file, `Found non-static expression for attribute ${(0, colors_1.colorizeIdString)(attrName)}: ${(0, colors_1.colorizeContent)(value)}. Change "${(0, colors_1.colorizeIdString)(attrName)}" to ensure this content is translated.`, location);
30
29
  exports.warnNonStaticExpressionSync = warnNonStaticExpressionSync;
31
- const warnTemplateLiteralSync = (file, value) => `Found template literal with quasis (${value}) in ${file}. Change the template literal to a string to ensure this content is translated.`;
30
+ const warnTemplateLiteralSync = (file, value, location) => (0, exports.withLocation)(file, `Found template literal with quasis (${(0, colors_1.colorizeContent)(value)}). Change the template literal to a string to ensure this content is translated.`, location);
32
31
  exports.warnTemplateLiteralSync = warnTemplateLiteralSync;
33
- const warnTernarySync = (file) => `Found ternary expression in ${file}. A Branch component may be more appropriate here.`;
32
+ const warnNonStringSync = (file, value, location) => (0, exports.withLocation)(file, `Found non-string literal (${(0, colors_1.colorizeContent)(value)}). Change the value to a string literal to ensure this content is translated.`, location);
33
+ exports.warnNonStringSync = warnNonStringSync;
34
+ const warnTernarySync = (file, location) => (0, exports.withLocation)(file, 'Found ternary expression. A Branch component may be more appropriate here.', location);
34
35
  exports.warnTernarySync = warnTernarySync;
36
+ const withLocation = (file, message, location) => `${(0, colors_1.colorizeFilepath)(file)}${location ? ` (${(0, colors_1.colorizeLine)(location)})` : ''}: ${message}`;
37
+ exports.withLocation = withLocation;
35
38
  // Re-export error messages
36
39
  exports.noLocalesError = `No locales found! Please provide a list of locales to translate to, or specify them in your gt.config.json file.`;
37
40
  exports.noDefaultLocaleError = `No default locale found! Please provide a default locale, or specify it in your gt.config.json file.`;
@@ -40,5 +43,6 @@ exports.noSourceFileError = `No source file found! Please double check your tran
40
43
  exports.noDataFormatError = `No data format found! Please make sure your translationsDir parameter ends with a supported file extension.`;
41
44
  exports.noSupportedDataFormatError = `Unsupported data format! Please make sure your translationsDir parameter ends with a supported file extension.`;
42
45
  exports.noApiKeyError = `No API key found! Please provide an API key using the --api-key flag or set the GT_API_KEY environment variable.`;
46
+ exports.devApiKeyError = `You are using a development API key. Please use a production API key to send files to General Translation.\nYou can generate a production API key with the command: npx gtx-cli auth -t production`;
43
47
  exports.noProjectIdError = `No project ID found! Please provide a project ID using the --project-id flag, specify it in your gt.config.json file, or set the GT_PROJECT_ID environment variable.`;
44
48
  exports.noVersionIdError = `No version ID found! Please provide a version ID using the --version-id flag or specify it in your gt.config.json file as the _versionId property.`;
@@ -84,6 +84,9 @@ async function translateFiles(filePaths, placeholderPaths, transformPaths, dataF
84
84
  if (!options.apiKey) {
85
85
  (0, console_1.logErrorAndExit)(console_1.noApiKeyError);
86
86
  }
87
+ if (options.apiKey.startsWith('gtx-dev-')) {
88
+ (0, console_1.logErrorAndExit)(console_1.devApiKeyError);
89
+ }
87
90
  if (!options.projectId) {
88
91
  (0, console_1.logErrorAndExit)(console_1.noProjectIdError);
89
92
  }
@@ -1,4 +1,4 @@
1
- import { FilesOptions } from '../../types';
1
+ import { FilesOptions, SupportedFrameworks } from '../../types';
2
2
  /**
3
3
  * Checks if the config file exists.
4
4
  * If yes, make sure make sure projectId is correct
@@ -11,4 +11,5 @@ export default function createOrUpdateConfig(configFilepath: string, options: {
11
11
  defaultLocale?: string;
12
12
  locales?: string[];
13
13
  files?: FilesOptions;
14
+ framework?: SupportedFrameworks;
14
15
  }): Promise<string>;
@@ -19,6 +19,7 @@ async function createOrUpdateConfig(configFilepath, options) {
19
19
  ...(options.projectId && { projectId: options.projectId }),
20
20
  ...(options.defaultLocale && { defaultLocale: options.defaultLocale }),
21
21
  ...(options.files && { files: options.files }),
22
+ ...(options.framework && { framework: options.framework }),
22
23
  };
23
24
  try {
24
25
  // if file exists
@@ -91,7 +91,7 @@ function parseStrings(importName, path, updates, errors, file) {
91
91
  t.isExpression(prop.value)) {
92
92
  const result = (0, evaluateJsx_1.isStaticExpression)(prop.value);
93
93
  if (!result.isStatic) {
94
- errors.push((0, console_1.warnNonStaticExpressionSync)(file, attribute, (0, generator_1.default)(prop.value).code));
94
+ errors.push((0, console_1.warnNonStaticExpressionSync)(file, attribute, (0, generator_1.default)(prop.value).code, `${prop.loc?.start?.line}:${prop.loc?.start?.column}`));
95
95
  }
96
96
  if (result.isStatic && result.value) {
97
97
  metadata[attribute] = result.value;
@@ -108,7 +108,7 @@ function parseStrings(importName, path, updates, errors, file) {
108
108
  }
109
109
  else if (t.isTemplateLiteral(arg)) {
110
110
  // warn if template literal
111
- errors.push((0, console_1.warnTemplateLiteralSync)(file, (0, generator_1.default)(arg).code));
111
+ errors.push((0, console_1.warnTemplateLiteralSync)(file, (0, generator_1.default)(arg).code, `${arg.loc?.start?.line}:${arg.loc?.start?.column}`));
112
112
  }
113
113
  }
114
114
  });
@@ -205,7 +205,7 @@ function parseJSXElement(importAliases, node, updates, errors, file) {
205
205
  if (attrName === 'id' || attrName === 'context') {
206
206
  const staticAnalysis = (0, evaluateJsx_1.isStaticExpression)(expr);
207
207
  if (!staticAnalysis.isStatic) {
208
- errors.push((0, console_1.warnVariablePropSync)(file, attrName, code));
208
+ errors.push((0, console_1.warnVariablePropSync)(file, attrName, code, `${expr.loc?.start?.line}:${expr.loc?.start?.column}`));
209
209
  }
210
210
  // Use the static value if available
211
211
  if (staticAnalysis.isStatic && staticAnalysis.value !== undefined) {
@@ -231,7 +231,7 @@ function parseJSXElement(importAliases, node, updates, errors, file) {
231
231
  const id = componentObj.props.id;
232
232
  // If we found an unwrapped expression, skip
233
233
  if (unwrappedExpressions.length > 0) {
234
- errors.push((0, console_1.warnHasUnwrappedExpressionSync)(file, id, unwrappedExpressions));
234
+ errors.push((0, console_1.warnHasUnwrappedExpressionSync)(file, unwrappedExpressions, id, `${node.loc?.start?.line}:${node.loc?.start?.column}`));
235
235
  }
236
236
  if (errors.length > 0)
237
237
  return;
@@ -3,8 +3,8 @@ import { Updates } from '../../../types';
3
3
  export declare const attributes: string[];
4
4
  /**
5
5
  * For the following example code:
6
- * const tx = useGT();
7
- * tx('string to translate', { id: 'exampleId', context: 'exampleContext' });
6
+ * const t = useGT();
7
+ * t('string to translate', { id: 'exampleId', context: 'exampleContext' });
8
8
  *
9
9
  * This function will find all call expressions of useGT(), then find all call expressions
10
10
  * of the subsequent tx() calls, and append the content and metadata to the updates array.
@@ -46,8 +46,8 @@ const generator_1 = __importDefault(require("@babel/generator"));
46
46
  exports.attributes = ['id', 'context'];
47
47
  /**
48
48
  * For the following example code:
49
- * const tx = useGT();
50
- * tx('string to translate', { id: 'exampleId', context: 'exampleContext' });
49
+ * const t = useGT();
50
+ * t('string to translate', { id: 'exampleId', context: 'exampleContext' });
51
51
  *
52
52
  * This function will find all call expressions of useGT(), then find all call expressions
53
53
  * of the subsequent tx() calls, and append the content and metadata to the updates array.
@@ -91,7 +91,7 @@ function parseStrings(importName, path, updates, errors, file) {
91
91
  t.isExpression(prop.value)) {
92
92
  const result = (0, evaluateJsx_1.isStaticExpression)(prop.value);
93
93
  if (!result.isStatic) {
94
- errors.push((0, console_1.warnNonStaticExpressionSync)(file, attribute, (0, generator_1.default)(prop.value).code));
94
+ errors.push((0, console_1.warnNonStaticExpressionSync)(file, attribute, (0, generator_1.default)(prop.value).code, `${prop.loc?.start?.line}:${prop.loc?.start?.column}`));
95
95
  }
96
96
  if (result.isStatic && result.value) {
97
97
  metadata[attribute] = result.value;
@@ -108,7 +108,10 @@ function parseStrings(importName, path, updates, errors, file) {
108
108
  }
109
109
  else if (t.isTemplateLiteral(arg)) {
110
110
  // warn if template literal
111
- errors.push((0, console_1.warnTemplateLiteralSync)(file, (0, generator_1.default)(arg).code));
111
+ errors.push((0, console_1.warnTemplateLiteralSync)(file, (0, generator_1.default)(arg).code, `${arg.loc?.start?.line}:${arg.loc?.start?.column}`));
112
+ }
113
+ else {
114
+ errors.push((0, console_1.warnNonStringSync)(file, (0, generator_1.default)(arg).code, `${arg.loc?.start?.line}:${arg.loc?.start?.column}`));
112
115
  }
113
116
  }
114
117
  });
@@ -86,7 +86,7 @@ function wrapJsxExpression(node, options, isMeaningful, mark) {
86
86
  }
87
87
  // Warn about ternary (should use branch instead)
88
88
  if (result.wrappedInT && !mark) {
89
- options.warnings.push((0, console_1.warnTernarySync)(options.file));
89
+ options.warnings.push((0, console_1.warnTernarySync)(options.file, `${consequent.loc?.start?.line}:${consequent.loc?.start?.column}`));
90
90
  }
91
91
  }
92
92
  else if (t.isConditionalExpression(consequent) ||
@@ -10,7 +10,6 @@ const console_2 = require("../console");
10
10
  const chalk_1 = __importDefault(require("chalk"));
11
11
  const console_3 = require("../console");
12
12
  const findFilepath_1 = __importDefault(require("../fs/findFilepath"));
13
- const generateSettings_1 = require("../config/generateSettings");
14
13
  const postProcess_2 = require("../hooks/postProcess");
15
14
  const handleInitGT_1 = __importDefault(require("../next/parse/handleInitGT"));
16
15
  const packageJson_1 = require("../utils/packageJson");
@@ -18,6 +17,7 @@ const wrapContent_1 = __importDefault(require("../react/parse/wrapContent"));
18
17
  const wrapContent_2 = __importDefault(require("../next/parse/wrapContent"));
19
18
  const packageManager_1 = require("../utils/packageManager");
20
19
  const installPackage_1 = require("../utils/installPackage");
20
+ const setupConfig_1 = __importDefault(require("../fs/config/setupConfig"));
21
21
  async function handleSetupReactCommand(options) {
22
22
  // Ask user for confirmation using inquirer
23
23
  const answer = await (0, console_3.promptConfirm)({
@@ -50,6 +50,10 @@ Make sure you have committed or stashed any changes. Do you want to continue?`),
50
50
  Please let us know what you would like to see supported at https://github.com/generaltranslation/gt/issues`);
51
51
  process.exit(0);
52
52
  }
53
+ // ----- Create a starter gt.config.json file -----
54
+ await (0, setupConfig_1.default)(options.config || 'gt.config.json', {
55
+ framework: frameworkType,
56
+ });
53
57
  const packageJson = await (0, packageJson_1.getPackageJson)();
54
58
  // Check if gt-next or gt-react is installed
55
59
  if (frameworkType === 'next-app' &&
@@ -68,8 +72,6 @@ Please let us know what you would like to see supported at https://github.com/ge
68
72
  await (0, installPackage_1.installPackage)('gt-react', packageManager);
69
73
  spinner.stop(chalk_1.default.green('Automatically installed gt-react.'));
70
74
  }
71
- // ----- Create a starter gt.config.json file -----
72
- await (0, generateSettings_1.generateSettings)(options);
73
75
  let errors = [];
74
76
  let warnings = [];
75
77
  let filesUpdated = [];
@@ -34,15 +34,15 @@ async function stageProject(settings, pkg) {
34
34
  const { updates, errors } = await (0, parse_1.createUpdates)(settings, settings.dictionary, pkg);
35
35
  if (errors.length > 0) {
36
36
  if (settings.ignoreErrors) {
37
- (0, logging_1.logWarning)(chalk_1.default.red(`CLI tool encountered errors while scanning for ${chalk_1.default.green('<T>')} tags. These components will not be translated.\n` +
37
+ (0, logging_1.logWarning)(chalk_1.default.yellow(`Warning: CLI tool encountered syntax errors while scanning for translatable content. These components will not be translated.\n` +
38
38
  errors
39
- .map((error) => chalk_1.default.yellow('• Warning: ') + error + '\n')
39
+ .map((error) => chalk_1.default.yellow('• ') + chalk_1.default.white(error) + '\n')
40
40
  .join('')));
41
41
  }
42
42
  else {
43
- (0, errors_1.logErrorAndExit)(chalk_1.default.red(`CLI tool encountered errors while scanning for ${chalk_1.default.green('<T>')} tags. ${chalk_1.default.gray('To ignore these errors, re-run with --ignore-errors')}\n` +
43
+ (0, errors_1.logErrorAndExit)(chalk_1.default.red(`Error: CLI tool encountered syntax errors while scanning for translatable content. ${chalk_1.default.gray('To ignore these errors, re-run with --ignore-errors')}\n` +
44
44
  errors
45
- .map((error) => chalk_1.default.red('• Error: ') + error + '\n')
45
+ .map((error) => chalk_1.default.red('• ') + chalk_1.default.white(error) + '\n')
46
46
  .join('')));
47
47
  }
48
48
  }
@@ -64,6 +64,9 @@ async function stageProject(settings, pkg) {
64
64
  if (!settings.apiKey) {
65
65
  (0, errors_1.logErrorAndExit)(errors_2.noApiKeyError);
66
66
  }
67
+ if (settings.apiKey.startsWith('gtx-dev-')) {
68
+ (0, errors_1.logErrorAndExit)(errors_1.devApiKeyError);
69
+ }
67
70
  if (!settings.projectId) {
68
71
  (0, errors_1.logErrorAndExit)(errors_2.noProjectIdError);
69
72
  }
@@ -97,4 +97,5 @@ export type Settings = {
97
97
  version?: string;
98
98
  description?: string;
99
99
  src?: string[];
100
+ framework?: SupportedFrameworks;
100
101
  };
@@ -1,12 +1,12 @@
1
- import { Settings } from '../types';
1
+ import { Settings, SupportedFrameworks } from '../types';
2
2
  type Credentials = {
3
3
  apiKey: string;
4
4
  projectId: string;
5
5
  };
6
- export declare function retrieveCredentials(settings: Settings): Promise<Credentials>;
7
- export declare function generateCredentialsSession(url: string): Promise<{
6
+ export declare function retrieveCredentials(settings: Settings, keyType: 'development' | 'production'): Promise<Credentials>;
7
+ export declare function generateCredentialsSession(url: string, keyType: 'development' | 'production'): Promise<{
8
8
  sessionId: string;
9
9
  }>;
10
10
  export declare function areCredentialsSet(): string | undefined;
11
- export declare function setCredentials(credentials: Credentials): Promise<void>;
11
+ export declare function setCredentials(credentials: Credentials, type: 'development' | 'production', framework?: SupportedFrameworks): Promise<void>;
12
12
  export {};
@@ -12,9 +12,9 @@ const node_path_1 = __importDefault(require("node:path"));
12
12
  const node_fs_1 = __importDefault(require("node:fs"));
13
13
  const chalk_1 = __importDefault(require("chalk"));
14
14
  // Fetches project ID and API key by opening the dashboard in the browser
15
- async function retrieveCredentials(settings) {
15
+ async function retrieveCredentials(settings, keyType) {
16
16
  // Generate a session ID
17
- const { sessionId } = await generateCredentialsSession(settings.baseUrl);
17
+ const { sessionId } = await generateCredentialsSession(settings.baseUrl, keyType);
18
18
  const urlToOpen = `${settings.dashboardUrl}/cli/wizard/${sessionId}`;
19
19
  await import('open').then((open) => open.default(urlToOpen, {
20
20
  wait: false,
@@ -53,12 +53,15 @@ async function retrieveCredentials(settings) {
53
53
  spinner.stop('Received credentials');
54
54
  return credentials;
55
55
  }
56
- async function generateCredentialsSession(url) {
56
+ async function generateCredentialsSession(url, keyType) {
57
57
  const res = await fetch(`${url}/cli/wizard/session`, {
58
58
  method: 'POST',
59
59
  headers: {
60
60
  'Content-Type': 'application/json',
61
61
  },
62
+ body: JSON.stringify({
63
+ keyType,
64
+ }),
62
65
  });
63
66
  if (!res.ok) {
64
67
  (0, console_1.logErrorAndExit)('Failed to generate credentials session');
@@ -70,7 +73,7 @@ function areCredentialsSet() {
70
73
  return process.env.GT_PROJECT_ID && process.env.GT_API_KEY;
71
74
  }
72
75
  // Sets the credentials in .env.local file
73
- async function setCredentials(credentials) {
76
+ async function setCredentials(credentials, type, framework) {
74
77
  const envFile = node_path_1.default.join(process.cwd(), '.env.local');
75
78
  let envContent = '';
76
79
  // Check if .env.local exists, create it if it doesn't
@@ -95,8 +98,29 @@ async function setCredentials(credentials) {
95
98
  envContent = await node_fs_1.default.promises.readFile(envFile, 'utf8');
96
99
  }
97
100
  // Always append the credentials to the file
98
- envContent += `\nGT_PROJECT_ID=${credentials.projectId}\n`;
99
- envContent += `GT_API_KEY=${credentials.apiKey}\n`;
101
+ let prefix = '';
102
+ if (framework === 'next-pages') {
103
+ prefix = 'NEXT_PUBLIC_';
104
+ }
105
+ else if (framework === 'vite') {
106
+ prefix = 'VITE_';
107
+ }
108
+ else if (framework === 'gatsby') {
109
+ prefix = 'GATSBY_';
110
+ }
111
+ else if (framework === 'react') {
112
+ prefix = 'REACT_APP_';
113
+ }
114
+ else if (framework === 'redwood') {
115
+ prefix = 'REDWOOD_ENV_';
116
+ }
117
+ envContent += `\n${prefix}GT_PROJECT_ID=${credentials.projectId}\n`;
118
+ if (type === 'development') {
119
+ envContent += `${prefix || ''}GT_DEV_API_KEY=${credentials.apiKey}\n`;
120
+ }
121
+ else {
122
+ envContent += `GT_API_KEY=${credentials.apiKey}\n`;
123
+ }
100
124
  // Ensure we don't have excessive newlines
101
125
  envContent = envContent.replace(/\n{3,}/g, '\n\n').trim() + '\n';
102
126
  // Write the updated content back to the file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "1.2.7",
3
+ "version": "1.2.8-alpha.2",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "scripts": {
@@ -69,7 +69,7 @@
69
69
  "chalk": "^4.1.2",
70
70
  "commander": "^12.1.0",
71
71
  "dotenv": "^16.4.5",
72
- "esbuild": "^0.23.1",
72
+ "esbuild": "^0.25.4",
73
73
  "fast-glob": "^3.3.3",
74
74
  "form-data": "^4.0.2",
75
75
  "generaltranslation": "^6.2.4",