gtx-cli 1.2.30-alpha.1 → 1.2.30-alpha.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,11 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 1.2.30
4
+
5
+ ### Patch Changes
6
+
7
+ - [#409](https://github.com/generaltranslation/gt/pull/409) [`557f74d`](https://github.com/generaltranslation/gt/commit/557f74da58ebd84ca50c1961fc6dfecd63bb7797) Thanks [@brian-lou](https://github.com/brian-lou)! - Make locadex more reliable + improve validation
8
+
3
9
  ## 1.2.29
4
10
 
5
11
  ### Patch Changes
@@ -1,9 +1,16 @@
1
1
  import { Settings } from '../types/index.js';
2
- import { FileFormats, DataFormat } from '../types/data.js';
2
+ import { FileExtension, DataFormat } from '../types/data.js';
3
+ /**
4
+ * File object structure
5
+ * @param content - The content of the file
6
+ * @param fileName - The name of the file
7
+ * @param fileExtension - The format of the file (JSON, MDX, MD, etc.)
8
+ * @param dataFormat - The format of the data within the file
9
+ */
3
10
  export interface FileToTranslate {
4
11
  content: string;
5
12
  fileName: string;
6
- fileFormat: FileFormats;
13
+ fileExtension: FileExtension;
7
14
  dataFormat: DataFormat;
8
15
  }
9
16
  type ApiOptions = Settings & {
@@ -19,8 +19,8 @@ export async function sendFiles(files, options) {
19
19
  // Add each file to the form data
20
20
  files.forEach((file, index) => {
21
21
  formData.append(`file${index}`, new Blob([file.content]), file.fileName);
22
- formData.append(`fileFormat${index}`, file.fileFormat);
23
- formData.append(`fileDataFormat${index}`, file.dataFormat); // Only used when translating JSON files
22
+ formData.append(`fileExtension${index}`, file.fileExtension);
23
+ formData.append(`dataFormat${index}`, file.dataFormat); // Only used when translating JSON files
24
24
  formData.append(`fileName${index}`, file.fileName);
25
25
  });
26
26
  // Add number of files
package/dist/cli/base.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { createOrUpdateConfig } from '../fs/config/setupConfig.js';
2
- import findFilepath, { findFilepaths } from '../fs/findFilepath.js';
2
+ import findFilepath from '../fs/findFilepath.js';
3
3
  import { displayHeader, promptText, logErrorAndExit, endCommand, promptConfirm, promptMultiSelect, logSuccess, logInfo, startCommand, createSpinner, logMessage, } from '../console/logging.js';
4
4
  import path from 'node:path';
5
5
  import fs from 'node:fs';
6
6
  import { generateSettings } from '../config/generateSettings.js';
7
7
  import chalk from 'chalk';
8
8
  import { translateFiles } from '../formats/files/translate.js';
9
- import { FILE_EXT_TO_FORMAT } from '../formats/files/supportedFiles.js';
9
+ import { FILE_EXT_TO_EXT_LABEL } from '../formats/files/supportedFiles.js';
10
10
  import { handleSetupReactCommand } from '../setup/wizard.js';
11
11
  import { isPackageInstalled, searchForPackageJson, } from '../utils/packageJson.js';
12
12
  import { getDesiredLocales } from '../setup/userInput.js';
@@ -94,7 +94,7 @@ export class BaseCLI {
94
94
  this.program
95
95
  .command('init')
96
96
  .description('Run the setup wizard to configure your project for General Translation')
97
- .option('--src <paths...>', "Filepath to directory containing the app's source code, by default ./src || ./app || ./pages || ./components", findFilepaths(['./src', './app', './pages', './components']))
97
+ .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}'")
98
98
  .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
99
99
  .action(async (options) => {
100
100
  displayHeader('Running setup wizard...');
@@ -139,7 +139,7 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
139
139
  this.program
140
140
  .command('setup')
141
141
  .description('Run the setup to configure your Next.js or React project for General Translation')
142
- .option('--src <paths...>', "Filepath to directory containing the app's source code, by default ./src || ./app || ./pages || ./components", findFilepaths(['./src', './app', './pages', './components']))
142
+ .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}'")
143
143
  .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
144
144
  .action(async (options) => {
145
145
  displayHeader('Running React setup wizard...');
@@ -210,24 +210,24 @@ See the docs for more information: https://generaltranslation.com/docs/react/tut
210
210
  const message = !isUsingGT
211
211
  ? 'What is the format of your language resource files? Select as many as applicable.\nAdditionally, you can translate any other files you have in your project.'
212
212
  : `${chalk.dim('(Optional)')} Do you have any separate files you would like to translate? For example, extra Markdown files for docs.`;
213
- const dataFormats = await promptMultiSelect({
213
+ const fileExtensions = await promptMultiSelect({
214
214
  message,
215
215
  options: [
216
- { value: 'json', label: 'JSON' },
217
- { value: 'md', label: 'Markdown' },
218
- { value: 'mdx', label: 'MDX' },
219
- { value: 'ts', label: 'TypeScript' },
220
- { value: 'js', label: 'JavaScript' },
216
+ { value: 'json', label: FILE_EXT_TO_EXT_LABEL.json },
217
+ { value: 'md', label: FILE_EXT_TO_EXT_LABEL.md },
218
+ { value: 'mdx', label: FILE_EXT_TO_EXT_LABEL.mdx },
219
+ { value: 'ts', label: FILE_EXT_TO_EXT_LABEL.ts },
220
+ { value: 'js', label: FILE_EXT_TO_EXT_LABEL.js },
221
221
  ],
222
222
  required: !isUsingGT,
223
223
  });
224
224
  const files = {};
225
- for (const dataFormat of dataFormats) {
225
+ for (const fileExtension of fileExtensions) {
226
226
  const paths = await promptText({
227
- message: `${chalk.cyan(FILE_EXT_TO_FORMAT[dataFormat])}: Please enter a space-separated list of glob patterns matching the location of the ${FILE_EXT_TO_FORMAT[dataFormat]} files you would like to translate.\nMake sure to include [locale] in the patterns.\nSee https://generaltranslation.com/docs/cli/reference/config#include for more information.`,
228
- defaultValue: `./**/[locale]/*.${dataFormat}`,
227
+ message: `${chalk.cyan(FILE_EXT_TO_EXT_LABEL[fileExtension])}: Please enter a space-separated list of glob patterns matching the location of the ${FILE_EXT_TO_EXT_LABEL[fileExtension]} files you would like to translate.\nMake sure to include [locale] in the patterns.\nSee https://generaltranslation.com/docs/cli/reference/config#include for more information.`,
228
+ defaultValue: `./**/[locale]/*.${fileExtension}`,
229
229
  });
230
- files[dataFormat] = {
230
+ files[fileExtension] = {
231
231
  include: paths.split(' '),
232
232
  };
233
233
  }
package/dist/cli/react.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { displayHeader, endCommand, logError, logErrorAndExit, logStep, logSuccess, logWarning, promptConfirm, } from '../console/logging.js';
2
2
  import loadJSON from '../fs/loadJSON.js';
3
- import findFilepath, { findFilepaths } from '../fs/findFilepath.js';
3
+ import findFilepath from '../fs/findFilepath.js';
4
4
  import chalk from 'chalk';
5
5
  import { formatFiles } from '../hooks/postProcess.js';
6
6
  import { BaseCLI } from './base.js';
@@ -45,7 +45,7 @@ export class ReactCLI extends BaseCLI {
45
45
  .option('--version-id <id>', 'Version ID for the translation service')
46
46
  .option('--tsconfig, --jsconfig <path>', 'Path to jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
47
47
  .option('--dictionary <path>', 'Path to dictionary file')
48
- .option('--src <paths...>', "Filepath to directory containing the app's source code, by default ./src || ./app || ./pages || ./components")
48
+ .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}'")
49
49
  .option('--default-language, --default-locale <locale>', 'Default locale (e.g., en)')
50
50
  .option('--new, --locales <locales...>', 'Space-separated list of locales (e.g., en fr es)')
51
51
  .option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
@@ -68,7 +68,7 @@ export class ReactCLI extends BaseCLI {
68
68
  .option('--version-id <id>', 'Version ID for the translation service')
69
69
  .option('--tsconfig, --jsconfig <path>', 'Path to jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
70
70
  .option('--dictionary <path>', 'Path to dictionary file')
71
- .option('--src <paths...>', "Filepath to directory containing the app's source code, by default ./src || ./app || ./pages || ./components")
71
+ .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}'")
72
72
  .option('--default-language, --default-locale <locale>', 'Default locale (e.g., en)')
73
73
  .option('--new, --locales <locales...>', 'Space-separated list of locales (e.g., en fr es)')
74
74
  .option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
@@ -88,7 +88,7 @@ export class ReactCLI extends BaseCLI {
88
88
  .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
89
89
  .option('--tsconfig, --jsconfig <path>', 'Path to jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
90
90
  .option('--dictionary <path>', 'Path to dictionary file')
91
- .option('--src <paths...>', "Filepath to directory containing the app's source code, by default ./src || ./app || ./pages || ./components")
91
+ .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}'")
92
92
  .option('--inline', 'Include inline <T> tags in addition to dictionary file', true)
93
93
  .action(async (options) => {
94
94
  // intro here since we don't want to show the ascii title
@@ -101,7 +101,7 @@ export class ReactCLI extends BaseCLI {
101
101
  this.program
102
102
  .command('generate')
103
103
  .description('Generate a translation file for the source locale. The -t flag must be provided. This command should be used if you are handling your own translations.')
104
- .option('--src <paths...>', "Filepath to directory containing the app's source code, by default ./src || ./app || ./pages || ./components")
104
+ .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}'")
105
105
  .option('--tsconfig, --jsconfig <path>', 'Path to jsconfig or tsconfig file', findFilepath(['./tsconfig.json', './jsconfig.json']))
106
106
  .option('--dictionary <path>', 'Path to dictionary file')
107
107
  .option('--default-language, --default-locale <locale>', 'Source locale (e.g., en)')
@@ -118,7 +118,7 @@ export class ReactCLI extends BaseCLI {
118
118
  this.program
119
119
  .command('scan')
120
120
  .description('Scans the project and wraps all JSX elements in the src directory with a <T> tag, with unique ids')
121
- .option('--src <paths...>', "Filepath to directory containing the app's source code, by default ./src || ./app || ./pages || ./components", findFilepaths(['./src', './app', './pages', './components']))
121
+ .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}'")
122
122
  .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
123
123
  .option('--disable-ids', 'Disable id generation for the <T> tags', false)
124
124
  .option('--disable-formatting', 'Disable formatting of edited files', false)
@@ -153,7 +153,7 @@ export class ReactCLI extends BaseCLI {
153
153
  .join('\n')));
154
154
  }
155
155
  else {
156
- logErrorAndExit(chalk.red(`CLI tool encountered errors while scanning for translatable content. ${chalk.dim('To ignore these errors, re-run with --ignore-errors')}\n` +
156
+ logErrorAndExit(chalk.red(`CLI tool encountered errors while scanning for translatable content. ${chalk.gray('To ignore these errors, re-run with --ignore-errors')}\n` +
157
157
  errors
158
158
  .map((error) => chalk.red('• Error: ') + chalk.white(error))
159
159
  .join('\n')));
@@ -1,4 +1,5 @@
1
1
  import { Settings } from '../types/index.js';
2
+ export declare const DEFAULT_SRC_PATTERNS: string[];
2
3
  /**
3
4
  * Generates settings from any
4
5
  * @param options - The options to generate settings from
@@ -4,11 +4,16 @@ import { defaultBaseUrl, libraryDefaultLocale, } from 'generaltranslation/intern
4
4
  import fs from 'node:fs';
5
5
  import { createOrUpdateConfig } from '../fs/config/setupConfig.js';
6
6
  import { resolveFiles } from '../fs/config/parseFilesConfig.js';
7
- import { findFilepaths } from '../fs/findFilepath.js';
8
7
  import { validateSettings } from './validateSettings.js';
9
8
  import { GT_DASHBOARD_URL } from '../utils/constants.js';
10
9
  import { resolveProjectId } from '../fs/utils.js';
11
10
  import path from 'node:path';
11
+ export const DEFAULT_SRC_PATTERNS = [
12
+ 'src/**/*.{js,jsx,ts,tsx}',
13
+ 'app/**/*.{js,jsx,ts,tsx}',
14
+ 'pages/**/*.{js,jsx,ts,tsx}',
15
+ 'components/**/*.{js,jsx,ts,tsx}',
16
+ ];
12
17
  /**
13
18
  * Generates settings from any
14
19
  * @param options - The options to generate settings from
@@ -68,14 +73,7 @@ export async function generateSettings(options, cwd = process.cwd()) {
68
73
  // For human review, always stage the project
69
74
  mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;
70
75
  // Populate src if not provided
71
- mergedOptions.src =
72
- mergedOptions.src ||
73
- findFilepaths([
74
- path.join(cwd, './src'),
75
- path.join(cwd, './app'),
76
- path.join(cwd, './pages'),
77
- path.join(cwd, './components'),
78
- ]);
76
+ mergedOptions.src = mergedOptions.src || DEFAULT_SRC_PATTERNS;
79
77
  // Resolve all glob patterns in the files object
80
78
  mergedOptions.files = mergedOptions.files
81
79
  ? resolveFiles(mergedOptions.files, mergedOptions.defaultLocale, cwd)
@@ -13,8 +13,7 @@ export declare const noLocalesError = "No locales found! Please provide a list o
13
13
  export declare const noDefaultLocaleError = "No default locale found! Please provide a default locale, or specify it in your gt.config.json file.";
14
14
  export declare const noFilesError = "Incorrect or missing files configuration! Please make sure your files are configured correctly in your gt.config.json file.";
15
15
  export declare const noSourceFileError = "No source file found! Please double check your translations directory and default locale.";
16
- export declare const noDataFormatError = "No data format found! Please make sure your translationsDir parameter ends with a supported file extension.";
17
- export declare const noSupportedDataFormatError = "Unsupported data format! Please make sure your translationsDir parameter ends with a supported file extension.";
16
+ export declare const noSupportedFormatError = "Unsupported data format! Please make sure your translationsDir parameter ends with a supported file extension.";
18
17
  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.";
19
18
  export declare const devApiKeyError = "You are using a development API key. Please use a production API key to use the General Translation API.\nYou can generate a production API key with the command: npx gtx-cli auth -t production";
20
19
  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.";
@@ -16,8 +16,7 @@ export const noLocalesError = `No locales found! Please provide a list of locale
16
16
  export const noDefaultLocaleError = `No default locale found! Please provide a default locale, or specify it in your gt.config.json file.`;
17
17
  export const noFilesError = `Incorrect or missing files configuration! Please make sure your files are configured correctly in your gt.config.json file.`;
18
18
  export const noSourceFileError = `No source file found! Please double check your translations directory and default locale.`;
19
- export const noDataFormatError = `No data format found! Please make sure your translationsDir parameter ends with a supported file extension.`;
20
- export const noSupportedDataFormatError = `Unsupported data format! Please make sure your translationsDir parameter ends with a supported file extension.`;
19
+ export const noSupportedFormatError = `Unsupported data format! Please make sure your translationsDir parameter ends with a supported file extension.`;
21
20
  export const noApiKeyError = `No API key found! Please provide an API key using the --api-key flag or set the GT_API_KEY environment variable.`;
22
21
  export const devApiKeyError = `You are using a development API key. Please use a production API key to use the General Translation API.\nYou can generate a production API key with the command: npx gtx-cli auth -t production`;
23
22
  export 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.`;
@@ -12,7 +12,11 @@ export declare function displayProjectId(projectId: string): void;
12
12
  export declare function displayResolvedPaths(resolvedPaths: [string, string][]): void;
13
13
  export declare function displayCreatedConfigFile(configFilepath: string): void;
14
14
  export declare function displayUpdatedConfigFile(configFilepath: string): void;
15
- export declare function createSpinner(indicator?: 'dots' | 'timer'): import("@clack/prompts").SpinnerResult;
15
+ export declare function createSpinner(indicator?: 'dots' | 'timer'): {
16
+ start: (msg?: string) => void;
17
+ stop: (msg?: string, code?: number) => void;
18
+ message: (msg?: string) => void;
19
+ };
16
20
  export declare function createOraSpinner(indicator?: 'dots' | 'circleHalves'): Promise<import("ora").Ora>;
17
21
  export declare function promptText({ message, defaultValue, validate, }: {
18
22
  message: string;
@@ -1,5 +1,5 @@
1
1
  export declare const SUPPORTED_FILE_EXTENSIONS: readonly ["json", "mdx", "md", "ts", "js"];
2
- export declare const FILE_EXT_TO_FORMAT: {
2
+ export declare const FILE_EXT_TO_EXT_LABEL: {
3
3
  json: string;
4
4
  mdx: string;
5
5
  md: string;
@@ -5,7 +5,7 @@ export const SUPPORTED_FILE_EXTENSIONS = [
5
5
  'ts',
6
6
  'js',
7
7
  ];
8
- export const FILE_EXT_TO_FORMAT = {
8
+ export const FILE_EXT_TO_EXT_LABEL = {
9
9
  json: 'JSON',
10
10
  mdx: 'MDX',
11
11
  md: 'Markdown',
@@ -6,7 +6,6 @@ import { TranslateOptions } from '../../cli/base.js';
6
6
  * @param filePaths - Resolved file paths for different file types
7
7
  * @param placeholderPaths - Placeholder paths for translated files
8
8
  * @param transformPaths - Transform paths for file naming
9
- * @param fileFormat - Format of the files
10
9
  * @param dataFormat - Format of the data within the files
11
10
  * @param options - Translation options including API settings
12
11
  * @returns Promise that resolves when translation is complete
@@ -1,6 +1,6 @@
1
1
  import { checkFileTranslations } from '../../api/checkFileTranslations.js';
2
2
  import { sendFiles } from '../../api/sendFiles.js';
3
- import { noSupportedDataFormatError, noLocalesError, noDefaultLocaleError, noApiKeyError, noProjectIdError, devApiKeyError, } from '../../console/index.js';
3
+ import { noSupportedFormatError, noLocalesError, noDefaultLocaleError, noApiKeyError, noProjectIdError, devApiKeyError, } from '../../console/index.js';
4
4
  import { logErrorAndExit, createSpinner, logError, logSuccess, } from '../../console/logging.js';
5
5
  import { resolveLocaleFiles } from '../../fs/config/parseFilesConfig.js';
6
6
  import { getRelative, readFile } from '../../fs/findFilepath.js';
@@ -16,7 +16,6 @@ const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
16
16
  * @param filePaths - Resolved file paths for different file types
17
17
  * @param placeholderPaths - Placeholder paths for translated files
18
18
  * @param transformPaths - Transform paths for file naming
19
- * @param fileFormat - Format of the files
20
19
  * @param dataFormat - Format of the data within the files
21
20
  * @param options - Translation options including API settings
22
21
  * @returns Promise that resolves when translation is complete
@@ -27,7 +26,7 @@ export async function translateFiles(filePaths, placeholderPaths, transformPaths
27
26
  // Process JSON files
28
27
  if (filePaths.json) {
29
28
  if (!SUPPORTED_DATA_FORMATS.includes(dataFormat)) {
30
- logErrorAndExit(noSupportedDataFormatError);
29
+ logErrorAndExit(noSupportedFormatError);
31
30
  }
32
31
  const jsonFiles = filePaths.json.map((filePath) => {
33
32
  const content = readFile(filePath);
@@ -38,7 +37,7 @@ export async function translateFiles(filePaths, placeholderPaths, transformPaths
38
37
  return {
39
38
  content,
40
39
  fileName: relativePath,
41
- fileFormat: 'JSON',
40
+ fileExtension: 'JSON',
42
41
  dataFormat,
43
42
  };
44
43
  });
@@ -54,7 +53,7 @@ export async function translateFiles(filePaths, placeholderPaths, transformPaths
54
53
  return {
55
54
  content,
56
55
  fileName: relativePath,
57
- fileFormat: fileType.toUpperCase(),
56
+ fileExtension: fileType.toUpperCase(),
58
57
  dataFormat,
59
58
  };
60
59
  });
@@ -0,0 +1,2 @@
1
+ export declare const IGNORED_PATTERNS: string[];
2
+ export declare function matchFiles(cwd: string, patterns: string[]): string[];
@@ -0,0 +1,10 @@
1
+ import fg from 'fast-glob';
2
+ export const IGNORED_PATTERNS = ['**/.*', '**/.*/**'];
3
+ export function matchFiles(cwd, patterns) {
4
+ return fg.sync(patterns, {
5
+ cwd,
6
+ absolute: true,
7
+ onlyFiles: true,
8
+ ignore: IGNORED_PATTERNS,
9
+ });
10
+ }
@@ -6,12 +6,13 @@ import generateModule from '@babel/generator';
6
6
  // Handle CommonJS/ESM interop
7
7
  const traverse = traverseModule.default || traverseModule;
8
8
  const generate = generateModule.default || generateModule;
9
- import { getFiles } from '../../fs/findJsxFilepath.js';
10
9
  import { isMeaningful } from '../../react/jsx/evaluateJsx.js';
11
10
  import { handleJsxElement } from '../../react/jsx/wrapJsx.js';
12
11
  import { getRelativePath } from '../../fs/findFilepath.js';
13
12
  import { isHtmlElement, isBodyElement, hasGTProviderChild, addDynamicLangAttribute, makeParentFunctionAsync, } from '../jsx/utils.js';
14
13
  import { generateImportMap, createImports, } from '../../react/jsx/utils/parseAst.js';
14
+ import { matchFiles } from '../../fs/matchFiles.js';
15
+ import { DEFAULT_SRC_PATTERNS } from '../../config/generateSettings.js';
15
16
  const IMPORT_MAP = {
16
17
  T: { name: 'T', source: 'gt-next' },
17
18
  Var: { name: 'Var', source: 'gt-next' },
@@ -28,13 +29,12 @@ const IMPORT_MAP = {
28
29
  * @returns An object containing the updates and errors
29
30
  */
30
31
  export async function wrapContentNext(options, pkg, errors, warnings) {
31
- const srcDirectory = options.src || ['./'];
32
- const files = srcDirectory.flatMap((dir) => getFiles(dir));
32
+ const files = matchFiles(process.cwd(), options.src || DEFAULT_SRC_PATTERNS);
33
33
  const filesUpdated = [];
34
34
  for (const file of files) {
35
35
  const code = await fs.promises.readFile(file, 'utf8');
36
36
  // Create relative path from src directory and remove extension
37
- const relativePath = getRelativePath(file, srcDirectory[0]);
37
+ const relativePath = getRelativePath(file, process.cwd());
38
38
  let ast;
39
39
  try {
40
40
  ast = parse(code, {
@@ -13,28 +13,28 @@ export default function addGTIdentifierToSyntaxTree(tree, startingIndex = 0) {
13
13
  return {
14
14
  variable: 'variable',
15
15
  id: indexObject.index,
16
- key: getVariableName({ ...props, 'data-_gt': generaltranslation }, 'variable'),
16
+ key: getVariableName(props, 'variable', indexObject.index),
17
17
  };
18
18
  }
19
19
  else if (type === 'Num') {
20
20
  return {
21
21
  variable: 'number',
22
22
  id: indexObject.index,
23
- key: getVariableName({ ...props, 'data-_gt': generaltranslation }, 'number'),
23
+ key: getVariableName(props, 'number', indexObject.index),
24
24
  };
25
25
  }
26
26
  else if (type === 'Currency') {
27
27
  return {
28
28
  variable: 'currency',
29
29
  id: indexObject.index,
30
- key: getVariableName({ ...props, 'data-_gt': generaltranslation }, 'currency'),
30
+ key: getVariableName(props, 'currency', indexObject.index),
31
31
  };
32
32
  }
33
33
  else if (type === 'DateTime') {
34
34
  return {
35
35
  variable: 'datetime',
36
36
  id: indexObject.index,
37
- key: getVariableName({ ...props, 'data-_gt': generaltranslation }, 'datetime'),
37
+ key: getVariableName(props, 'datetime', indexObject.index),
38
38
  };
39
39
  }
40
40
  else if (type === '' || type === 'React.Fragment') {
@@ -1,4 +1,3 @@
1
- import { splitStringToContent } from 'generaltranslation';
2
1
  import * as t from '@babel/types';
3
2
  import { isStaticExpression } from '../evaluateJsx.js';
4
3
  import { warnNonStaticExpressionSync, warnTemplateLiteralSync, } from '../../../console/index.js';
@@ -39,8 +38,6 @@ export function parseStrings(importName, path, updates, errors, file) {
39
38
  const source = arg.type === 'StringLiteral'
40
39
  ? arg.value
41
40
  : arg.quasis[0].value.raw;
42
- // split the string into content (same as runtime behavior)
43
- const content = splitStringToContent(source);
44
41
  // get metadata and id from options
45
42
  const options = tPath.parent.arguments[1];
46
43
  const metadata = {};
@@ -63,8 +60,8 @@ export function parseStrings(importName, path, updates, errors, file) {
63
60
  });
64
61
  }
65
62
  updates.push({
66
- dataFormat: 'JSX',
67
- source: content,
63
+ dataFormat: 'ICU',
64
+ source,
68
65
  metadata,
69
66
  });
70
67
  }
@@ -1,4 +1,3 @@
1
- import { splitStringToContent } from 'generaltranslation';
2
1
  import * as t from '@babel/types';
3
2
  import { isStaticExpression } from '../evaluateJsx.js';
4
3
  import { warnNonStaticExpressionSync, warnNonStringSync, warnTemplateLiteralSync, } from '../../../console/index.js';
@@ -30,8 +29,6 @@ function processTranslationCall(tPath, updates, errors, file) {
30
29
  if (arg.type === 'StringLiteral' ||
31
30
  (t.isTemplateLiteral(arg) && arg.expressions.length === 0)) {
32
31
  const source = arg.type === 'StringLiteral' ? arg.value : arg.quasis[0].value.raw;
33
- // split the string into content (same as runtime behavior)
34
- const content = splitStringToContent(source);
35
32
  // get metadata and id from options
36
33
  const options = tPath.parent.arguments[1];
37
34
  const metadata = {};
@@ -53,8 +50,8 @@ function processTranslationCall(tPath, updates, errors, file) {
53
50
  });
54
51
  }
55
52
  updates.push({
56
- dataFormat: 'JSX',
57
- source: content,
53
+ dataFormat: 'ICU',
54
+ source,
58
55
  metadata,
59
56
  });
60
57
  }
@@ -1,3 +1,3 @@
1
1
  import { BuildOptions } from 'esbuild';
2
- import { Options, Updates } from '../../types/index.js';
3
- export default function createDictionaryUpdates(options: Options, dictionaryPath: string, esbuildConfig?: BuildOptions): Promise<Updates>;
2
+ import { Updates } from '../../types/index.js';
3
+ export default function createDictionaryUpdates(dictionaryPath: string, esbuildConfig?: BuildOptions): Promise<Updates>;
@@ -3,12 +3,11 @@ import path from 'node:path';
3
3
  import os from 'os';
4
4
  import { build } from 'esbuild';
5
5
  import flattenDictionary from '../utils/flattenDictionary.js';
6
- import { splitStringToContent } from 'generaltranslation';
7
6
  import loadJSON from '../../fs/loadJSON.js';
8
- import { hashJsxChildren } from 'generaltranslation/id';
7
+ import { hashSource } from 'generaltranslation/id';
9
8
  import getEntryAndMetadata from '../utils/getEntryAndMetadata.js';
10
9
  import { logError } from '../../console/logging.js';
11
- export default async function createDictionaryUpdates(options, dictionaryPath, esbuildConfig) {
10
+ export default async function createDictionaryUpdates(dictionaryPath, esbuildConfig) {
12
11
  let dictionary;
13
12
  // ---- HANDLE JSON STRING DICTIONARY ----- //
14
13
  if (dictionaryPath.endsWith('.json')) {
@@ -46,22 +45,21 @@ export default async function createDictionaryUpdates(options, dictionaryPath, e
46
45
  for (const id of Object.keys(dictionary)) {
47
46
  const { entry, metadata: props, // context, etc.
48
47
  } = getEntryAndMetadata(dictionary[id]);
49
- const source = splitStringToContent(entry);
50
48
  const context = props?.context;
51
49
  const metadata = {
52
50
  id,
53
51
  ...(context && { context }),
54
52
  // This hash isn't actually used by the GT API, just for consistency sake
55
- hash: hashJsxChildren({
56
- source,
53
+ hash: hashSource({
54
+ source: entry,
57
55
  ...(context && { context }),
58
56
  ...(id && { id }),
59
- dataFormat: 'JSX',
57
+ dataFormat: 'ICU',
60
58
  }),
61
59
  };
62
60
  updates.push({
63
- dataFormat: 'JSX',
64
- source,
61
+ dataFormat: 'ICU',
62
+ source: entry,
65
63
  metadata,
66
64
  });
67
65
  }
@@ -1,50 +1,23 @@
1
1
  import fs from 'node:fs';
2
- import path from 'node:path';
3
2
  import { parse } from '@babel/parser';
4
3
  import traverseModule from '@babel/traverse';
5
4
  // Handle CommonJS/ESM interop
6
5
  const traverse = traverseModule.default || traverseModule;
7
- import { hashJsxChildren } from 'generaltranslation/id';
6
+ import { hashSource } from 'generaltranslation/id';
8
7
  import { parseJSXElement } from '../jsx/utils/parseJsx.js';
9
8
  import { parseStrings } from '../jsx/utils/parseStringFunction.js';
10
9
  import { extractImportName } from '../jsx/utils/parseAst.js';
11
10
  import { logError } from '../../console/logging.js';
12
11
  import { validateStringFunction } from '../jsx/utils/validateStringFunction.js';
13
12
  import { GT_TRANSLATION_FUNCS } from '../jsx/utils/constants.js';
13
+ import { matchFiles } from '../../fs/matchFiles.js';
14
+ import { DEFAULT_SRC_PATTERNS } from '../../config/generateSettings.js';
14
15
  export default async function createInlineUpdates(options, pkg, validate) {
15
16
  const updates = [];
16
17
  const errors = [];
17
18
  // Use the provided app directory or default to the current directory
18
- const srcDirectory = options.src || ['./'];
19
- // Define the file extensions to look for
20
- const extensions = ['.js', '.jsx', '.tsx', '.ts'];
21
- /**
22
- * Recursively scan the directory and collect all files with the specified extensions,
23
- * excluding files or directories that start with a dot (.)
24
- * @param dir - The directory to scan
25
- * @returns An array of file paths
26
- */
27
- function getFiles(dir) {
28
- let files = [];
29
- const items = fs.readdirSync(dir);
30
- for (const item of items) {
31
- // Skip hidden files and directories
32
- if (item.startsWith('.'))
33
- continue;
34
- const fullPath = path.join(dir, item);
35
- const stat = fs.statSync(fullPath);
36
- if (stat.isDirectory()) {
37
- // Recursively scan subdirectories
38
- files = files.concat(getFiles(fullPath));
39
- }
40
- else if (extensions.includes(path.extname(item))) {
41
- // Add files with the specified extensions
42
- files.push(fullPath);
43
- }
44
- }
45
- return files;
46
- }
47
- const files = srcDirectory.flatMap((dir) => getFiles(dir));
19
+ const filePatterns = options.src || DEFAULT_SRC_PATTERNS;
20
+ const files = matchFiles(process.cwd(), filePatterns);
48
21
  for (const file of files) {
49
22
  const code = await fs.promises.readFile(file, 'utf8');
50
23
  let ast;
@@ -129,11 +102,11 @@ export default async function createInlineUpdates(options, pkg, validate) {
129
102
  // Post-process to add a hash to each update
130
103
  await Promise.all(updates.map(async (update) => {
131
104
  const context = update.metadata.context;
132
- const hash = hashJsxChildren({
105
+ const hash = hashSource({
133
106
  source: update.source,
134
107
  ...(context && { context }),
135
108
  ...(update.metadata.id && { id: update.metadata.id }),
136
- dataFormat: 'JSX',
109
+ dataFormat: update.dataFormat,
137
110
  });
138
111
  update.metadata.hash = hash;
139
112
  }));
@@ -7,11 +7,12 @@ import generateModule from '@babel/generator';
7
7
  // Handle CommonJS/ESM interop
8
8
  const traverse = traverseModule.default || traverseModule;
9
9
  const generate = generateModule.default || generateModule;
10
- import { getFiles } from '../../fs/findJsxFilepath.js';
11
10
  import { isMeaningful } from '../jsx/evaluateJsx.js';
12
11
  import { handleJsxElement } from '../jsx/wrapJsx.js';
13
12
  import { getRelativePath } from '../../fs/findFilepath.js';
14
13
  import { generateImportMap, createImports, } from '../jsx/utils/parseAst.js';
14
+ import { DEFAULT_SRC_PATTERNS } from '../../config/generateSettings.js';
15
+ import { matchFiles } from '../../fs/matchFiles.js';
15
16
  const IMPORT_MAP = {
16
17
  T: { name: 'T', source: 'gt-react' },
17
18
  Var: { name: 'Var', source: 'gt-react' },
@@ -28,8 +29,8 @@ const IMPORT_MAP = {
28
29
  * @returns An object containing the updates and errors
29
30
  */
30
31
  export async function wrapContentReact(options, pkg, framework, errors, warnings) {
31
- const srcDirectory = options.src || ['./'];
32
- const files = srcDirectory.flatMap((dir) => getFiles(dir));
32
+ const filePatterns = options.src || DEFAULT_SRC_PATTERNS;
33
+ const files = matchFiles(process.cwd(), filePatterns);
33
34
  const filesUpdated = [];
34
35
  for (const file of files) {
35
36
  const baseFileName = path.basename(file);
@@ -40,7 +41,7 @@ export async function wrapContentReact(options, pkg, framework, errors, warnings
40
41
  : './' + configPath;
41
42
  const code = await fs.promises.readFile(file, 'utf8');
42
43
  // Create relative path from src directory and remove extension
43
- const relativePath = getRelativePath(file, srcDirectory[0]);
44
+ const relativePath = getRelativePath(file, process.cwd());
44
45
  let ast;
45
46
  try {
46
47
  ast = parse(code, {
@@ -1,2 +1,2 @@
1
1
  export declare const baseVariablePrefix = "_gt_";
2
- export declare function getVariableName(props: Record<string, any> | undefined, variableType: string): string;
2
+ export declare function getVariableName(props: Record<string, any> | undefined, variableType: 'variable' | 'number' | 'datetime' | 'currency', id: number): string;
@@ -5,9 +5,9 @@ const defaultVariableNames = {
5
5
  currency: 'cost',
6
6
  };
7
7
  export const baseVariablePrefix = '_gt_';
8
- export function getVariableName(props = {}, variableType) {
8
+ export function getVariableName(props = {}, variableType, id) {
9
9
  if (props.name)
10
10
  return props.name;
11
11
  const baseVariableName = defaultVariableNames[variableType] || 'value';
12
- return `${baseVariablePrefix}${baseVariableName}_${props['data-_gt']?.id}`;
12
+ return `${baseVariablePrefix}${baseVariableName}_${id}`;
13
13
  }
@@ -24,7 +24,7 @@ export async function createUpdates(options, sourceDictionary, pkg, validate) {
24
24
  if (sourceDictionary.endsWith('.json')) {
25
25
  updates = [
26
26
  ...updates,
27
- ...(await createDictionaryUpdates(options, sourceDictionary)),
27
+ ...(await createDictionaryUpdates(sourceDictionary)),
28
28
  ];
29
29
  }
30
30
  else {
@@ -42,7 +42,7 @@ export async function createUpdates(options, sourceDictionary, pkg, validate) {
42
42
  }
43
43
  updates = [
44
44
  ...updates,
45
- ...(await createDictionaryUpdates(options, sourceDictionary, esbuildConfig)),
45
+ ...(await createDictionaryUpdates(sourceDictionary, esbuildConfig)),
46
46
  ];
47
47
  }
48
48
  }
@@ -35,7 +35,7 @@ export async function stageProject(settings, pkg) {
35
35
  .join('')));
36
36
  }
37
37
  else {
38
- logErrorAndExit(chalk.red(`Error: CLI tool encountered ${errors.length} syntax errors while scanning for translatable content. ${chalk.dim('To ignore these errors, re-run with --ignore-errors')}\n` +
38
+ logErrorAndExit(chalk.red(`Error: CLI tool encountered ${errors.length} syntax errors while scanning for translatable content. ${chalk.gray('To ignore these errors, re-run with --ignore-errors')}\n` +
39
39
  errors
40
40
  .map((error) => chalk.red('• ') + chalk.white(error) + '\n')
41
41
  .join('')));
@@ -18,7 +18,7 @@ export type FlattenedJSONDictionary = {
18
18
  [key: string]: string;
19
19
  };
20
20
  export type DataFormat = 'JSX' | 'ICU' | 'I18NEXT';
21
- export type FileFormats = 'JSON' | 'YAML' | 'MDX' | 'MD' | 'TS' | 'JS';
21
+ export type FileExtension = 'JSON' | 'YAML' | 'MDX' | 'MD' | 'TS' | 'JS';
22
22
  export type JsxChildren = string | string[] | any;
23
23
  export type Translations = {
24
24
  [key: string]: JsxChildren;
@@ -30,7 +30,7 @@ export type Options = {
30
30
  stageTranslations?: boolean;
31
31
  };
32
32
  export type WrapOptions = {
33
- src: string[];
33
+ src?: string[];
34
34
  config: string;
35
35
  skipTs: boolean;
36
36
  disableIds: boolean;
@@ -38,11 +38,11 @@ export type WrapOptions = {
38
38
  addGTProvider: boolean;
39
39
  };
40
40
  export type SetupOptions = {
41
- src: string[];
41
+ src?: string[];
42
42
  config: string;
43
43
  };
44
44
  export type GenerateSourceOptions = {
45
- src: string[];
45
+ src?: string[];
46
46
  config: string;
47
47
  defaultLocale: string;
48
48
  dictionary?: string;
@@ -97,6 +97,6 @@ export type Settings = {
97
97
  _versionId?: string;
98
98
  version?: string;
99
99
  description?: string;
100
- src?: string[];
100
+ src: string[];
101
101
  framework?: SupportedFrameworks;
102
102
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "1.2.30-alpha.1",
3
+ "version": "1.2.30-alpha.18",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "files": [
@@ -86,7 +86,7 @@
86
86
  "esbuild": "^0.25.4",
87
87
  "fast-glob": "^3.3.3",
88
88
  "form-data": "^4.0.2",
89
- "generaltranslation": "^6.2.10",
89
+ "generaltranslation": "^7.0.0-alpha.18",
90
90
  "open": "^10.1.1",
91
91
  "ora": "^8.2.0",
92
92
  "resolve": "^1.22.10",
@@ -1,7 +0,0 @@
1
- /**
2
- * Recursively scan the directory and collect all files with the specified extensions,
3
- * excluding files or directories that start with a dot (.)
4
- * @param dir - The directory to scan
5
- * @returns An array of file paths
6
- */
7
- export declare function getFiles(dir: string): string[];
@@ -1,30 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- // Define the file extensions to look for
4
- const extensions = ['.js', '.jsx', '.tsx'];
5
- /**
6
- * Recursively scan the directory and collect all files with the specified extensions,
7
- * excluding files or directories that start with a dot (.)
8
- * @param dir - The directory to scan
9
- * @returns An array of file paths
10
- */
11
- export function getFiles(dir) {
12
- let files = [];
13
- const items = fs.readdirSync(dir);
14
- for (const item of items) {
15
- // Skip hidden files and directories
16
- if (item.startsWith('.'))
17
- continue;
18
- const fullPath = path.join(dir, item);
19
- const stat = fs.statSync(fullPath);
20
- if (stat.isDirectory()) {
21
- // Recursively scan subdirectories
22
- files = files.concat(getFiles(fullPath));
23
- }
24
- else if (extensions.includes(path.extname(item))) {
25
- // Add files with the specified extensions
26
- files.push(fullPath);
27
- }
28
- }
29
- return files;
30
- }