gtx-cli 2.4.5 → 2.4.7

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,17 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.4.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [#767](https://github.com/generaltranslation/gt/pull/767) [`b27a947`](https://github.com/generaltranslation/gt/commit/b27a947a46d2ad802278d79d45d25cdccd7193d5) Thanks [@brian-lou](https://github.com/brian-lou)! - Fix monorepo in-line string resolution
8
+
9
+ ## 2.4.6
10
+
11
+ ### Patch Changes
12
+
13
+ - [#763](https://github.com/generaltranslation/gt/pull/763) [`b6a79a8`](https://github.com/generaltranslation/gt/commit/b6a79a868630725eb1106faaa2c385c305891e9c) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Allow lists for overrides in gt.config.json
14
+
3
15
  ## 2.4.5
4
16
 
5
17
  ### Patch Changes
@@ -155,6 +155,10 @@ export async function generateSettings(flags, cwd = process.cwd()) {
155
155
  }
156
156
  }
157
157
  }
158
+ // Add parsing options if not provided
159
+ mergedOptions.parsingOptions = mergedOptions.parsingOptions || {};
160
+ mergedOptions.parsingOptions.conditionNames = mergedOptions.parsingOptions
161
+ .conditionNames || ['browser', 'module', 'import', 'require', 'default'];
158
162
  // if there's no existing config file, creates one
159
163
  // does not include the API key to avoid exposing it
160
164
  if (!fs.existsSync(mergedOptions.config)) {
@@ -44,6 +44,39 @@ export function createFileMapping(filePaths, placeholderPaths, transformPaths, t
44
44
  return path.join(directory, transformedFileName);
45
45
  });
46
46
  }
47
+ else if (Array.isArray(transformPath)) {
48
+ // transformPath is an array of TransformOption objects
49
+ const targetLocaleProperties = getLocaleProperties(locale);
50
+ const defaultLocaleProperties = getLocaleProperties(defaultLocale);
51
+ translatedFiles = translatedFiles.map((filePath) => {
52
+ const relativePath = getRelative(filePath);
53
+ // Try each transform in order until one matches
54
+ for (const transform of transformPath) {
55
+ if (!transform.replace || typeof transform.replace !== 'string') {
56
+ continue;
57
+ }
58
+ // Replace all locale property placeholders in the replace string
59
+ const replaceString = replaceLocalePlaceholders(transform.replace, targetLocaleProperties);
60
+ if (transform.match && typeof transform.match === 'string') {
61
+ // Replace locale placeholders in the match string using defaultLocale properties
62
+ let matchString = transform.match;
63
+ matchString = replaceLocalePlaceholders(matchString, defaultLocaleProperties);
64
+ const regex = new RegExp(matchString);
65
+ if (regex.test(relativePath)) {
66
+ // This transform matches, apply it and break
67
+ const transformedPath = relativePath.replace(new RegExp(matchString, 'g'), replaceString);
68
+ return path.resolve(transformedPath);
69
+ }
70
+ }
71
+ else {
72
+ // No match provided: treat as a direct replacement (override)
73
+ return path.resolve(replaceString);
74
+ }
75
+ }
76
+ // If no transforms matched, return the original path
77
+ return filePath;
78
+ });
79
+ }
47
80
  else {
48
81
  // transformPath is an object
49
82
  const targetLocaleProperties = getLocaleProperties(locale);
@@ -21,7 +21,7 @@ export declare function resolveFiles(files: FilesOptions, locale: string, locale
21
21
  placeholderPaths: ResolvedFiles;
22
22
  transformPaths: TransformFiles;
23
23
  };
24
- export declare function expandGlobPatterns(cwd: string, includePatterns: string[], excludePatterns: string[], locale: string, locales: string[], transformPatterns?: TransformOption | string, compositePatterns?: string[]): {
24
+ export declare function expandGlobPatterns(cwd: string, includePatterns: string[], excludePatterns: string[], locale: string, locales: string[], transformPatterns?: TransformOption | string | TransformOption[], compositePatterns?: string[]): {
25
25
  resolvedPaths: string[];
26
26
  placeholderPaths: string[];
27
27
  };
@@ -41,8 +41,9 @@ export function resolveFiles(files, locale, locales, cwd, compositePatterns) {
41
41
  // ==== TRANSFORMS ==== //
42
42
  const transform = files[fileType]?.transform;
43
43
  if (transform &&
44
- !Array.isArray(transform) &&
45
- (typeof transform === 'string' || typeof transform === 'object')) {
44
+ (typeof transform === 'string' ||
45
+ typeof transform === 'object' ||
46
+ Array.isArray(transform))) {
46
47
  transformPaths[fileType] = transform;
47
48
  }
48
49
  // ==== PLACEHOLDERS ==== //
@@ -1,5 +1,10 @@
1
1
  import { NodePath } from '@babel/traverse';
2
2
  import { Updates } from '../../../types/index.js';
3
+ import type { ParsingConfigOptions } from '../../../types/parsing.js';
4
+ /**
5
+ * Clears all caches. Useful for testing or when file system changes.
6
+ */
7
+ export declare function clearParsingCaches(): void;
3
8
  /**
4
9
  * Main entry point for parsing translation strings from useGT() and getGT() calls.
5
10
  *
@@ -13,4 +18,4 @@ import { Updates } from '../../../types/index.js';
13
18
  * - const { home } = getInfo(t); // getInfo is imported from './constants'
14
19
  * - This will parse constants.ts to find translation calls within getInfo function
15
20
  */
16
- export declare function parseStrings(importName: string, originalName: string, path: NodePath, updates: Updates, errors: string[], file: string): void;
21
+ export declare function parseStrings(importName: string, originalName: string, path: NodePath, updates: Updates, errors: string[], file: string, parsingOptions: ParsingConfigOptions): void;
@@ -12,6 +12,27 @@ import path from 'node:path';
12
12
  import { parse } from '@babel/parser';
13
13
  import { createMatchPath, loadConfig } from 'tsconfig-paths';
14
14
  import resolve from 'resolve';
15
+ import enhancedResolve from 'enhanced-resolve';
16
+ const { ResolverFactory } = enhancedResolve;
17
+ /**
18
+ * Cache for resolved import paths to avoid redundant I/O operations.
19
+ * Key: `${currentFile}::${importPath}`
20
+ * Value: resolved absolute path or null
21
+ */
22
+ const resolveImportPathCache = new Map();
23
+ /**
24
+ * Cache for processed functions to avoid re-parsing the same files.
25
+ * Key: `${filePath}::${functionName}::${argIndex}`
26
+ * Value: boolean indicating whether the function was found and processed
27
+ */
28
+ const processFunctionCache = new Map();
29
+ /**
30
+ * Clears all caches. Useful for testing or when file system changes.
31
+ */
32
+ export function clearParsingCaches() {
33
+ resolveImportPathCache.clear();
34
+ processFunctionCache.clear();
35
+ }
15
36
  /**
16
37
  * Processes a single translation function call (e.g., t('hello world', { id: 'greeting' })).
17
38
  * Extracts the translatable string content and metadata, then adds it to the updates array.
@@ -71,12 +92,16 @@ function processTranslationCall(tPath, updates, errors, file, ignoreAdditionalDa
71
92
  }
72
93
  }
73
94
  /**
74
- * Extracts the parameter name from a function parameter node, handling TypeScript annotations.
95
+ * Extracts the parameter name from a function parameter node, handling TypeScript annotations and default values.
75
96
  */
76
97
  function extractParameterName(param) {
77
98
  if (t.isIdentifier(param)) {
78
99
  return param.name;
79
100
  }
101
+ // Handle parameters with default values: (gt = () => {})
102
+ if (t.isAssignmentPattern(param) && t.isIdentifier(param.left)) {
103
+ return param.left.name;
104
+ }
80
105
  return null;
81
106
  }
82
107
  /**
@@ -149,7 +174,7 @@ function resolveVariableAliases(scope, variableName, visited = new Set()) {
149
174
  * This covers both direct translation calls (t('hello')) and prop drilling
150
175
  * where the translation callback is passed to other functions (getData(t)).
151
176
  */
152
- function handleFunctionCall(tPath, updates, errors, file, importMap, ignoreAdditionalData, ignoreDynamicContent) {
177
+ function handleFunctionCall(tPath, updates, errors, file, importMap, ignoreAdditionalData, ignoreDynamicContent, parsingOptions) {
153
178
  if (tPath.parent.type === 'CallExpression' &&
154
179
  tPath.parent.callee === tPath.node) {
155
180
  // Direct translation call: t('hello')
@@ -165,7 +190,7 @@ function handleFunctionCall(tPath, updates, errors, file, importMap, ignoreAddit
165
190
  const calleeBinding = tPath.scope.getBinding(callee.name);
166
191
  if (calleeBinding && calleeBinding.path.isFunction()) {
167
192
  const functionPath = calleeBinding.path;
168
- processFunctionIfMatches(callee.name, argIndex, functionPath.node, functionPath, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent);
193
+ processFunctionIfMatches(callee.name, argIndex, functionPath.node, functionPath, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent, parsingOptions);
169
194
  }
170
195
  // Handle arrow functions assigned to variables: const getData = (t) => {...}
171
196
  else if (calleeBinding &&
@@ -174,14 +199,14 @@ function handleFunctionCall(tPath, updates, errors, file, importMap, ignoreAddit
174
199
  (t.isArrowFunctionExpression(calleeBinding.path.node.init) ||
175
200
  t.isFunctionExpression(calleeBinding.path.node.init))) {
176
201
  const initPath = calleeBinding.path.get('init');
177
- processFunctionIfMatches(callee.name, argIndex, calleeBinding.path.node.init, initPath, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent);
202
+ processFunctionIfMatches(callee.name, argIndex, calleeBinding.path.node.init, initPath, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent, parsingOptions);
178
203
  }
179
204
  // If not found locally, check if it's an imported function
180
205
  else if (importMap.has(callee.name)) {
181
206
  const importPath = importMap.get(callee.name);
182
- const resolvedPath = resolveImportPath(file, importPath);
207
+ const resolvedPath = resolveImportPath(file, importPath, parsingOptions);
183
208
  if (resolvedPath) {
184
- findFunctionInFile(resolvedPath, callee.name, argIndex, updates, errors, ignoreAdditionalData, ignoreDynamicContent);
209
+ processFunctionInFile(resolvedPath, callee.name, argIndex, updates, errors, ignoreAdditionalData, ignoreDynamicContent, parsingOptions);
185
210
  }
186
211
  }
187
212
  }
@@ -192,12 +217,12 @@ function handleFunctionCall(tPath, updates, errors, file, importMap, ignoreAddit
192
217
  * Validates the function has enough parameters and traces how the translation callback
193
218
  * is used within that function's body.
194
219
  */
195
- function processFunctionIfMatches(_functionName, argIndex, functionNode, functionPath, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent) {
220
+ function processFunctionIfMatches(_functionName, argIndex, functionNode, functionPath, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent, parsingOptions) {
196
221
  if (functionNode.params.length > argIndex) {
197
222
  const param = functionNode.params[argIndex];
198
223
  const paramName = extractParameterName(param);
199
224
  if (paramName) {
200
- findFunctionParameterUsage(functionPath, paramName, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent);
225
+ findFunctionParameterUsage(functionPath, paramName, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent, parsingOptions);
201
226
  }
202
227
  }
203
228
  }
@@ -209,7 +234,7 @@ function processFunctionIfMatches(_functionName, argIndex, functionNode, functio
209
234
  * Example: In function getInfo(t) { return t('hello'); }, this finds the t('hello') call.
210
235
  * Example: In function getData(t) { return getFooter(t); }, this finds and traces into getFooter.
211
236
  */
212
- function findFunctionParameterUsage(functionPath, parameterName, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent) {
237
+ function findFunctionParameterUsage(functionPath, parameterName, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent, parsingOptions) {
213
238
  // Look for the function body and find all usages of the parameter
214
239
  if (functionPath.isFunction()) {
215
240
  const functionScope = functionPath.scope;
@@ -224,7 +249,7 @@ function findFunctionParameterUsage(functionPath, parameterName, updates, errors
224
249
  const binding = functionScope.bindings[name];
225
250
  if (binding) {
226
251
  binding.referencePaths.forEach((refPath) => {
227
- handleFunctionCall(refPath, updates, errors, file, importMap, ignoreAdditionalData, ignoreDynamicContent);
252
+ handleFunctionCall(refPath, updates, errors, file, importMap, ignoreAdditionalData, ignoreDynamicContent, parsingOptions);
228
253
  });
229
254
  }
230
255
  });
@@ -239,44 +264,82 @@ function findFunctionParameterUsage(functionPath, parameterName, updates, errors
239
264
  * - '@/components/ui/button' -> '/full/path/to/src/components/ui/button.tsx'
240
265
  * - '@shared/utils' -> '/full/path/to/packages/utils/index.ts'
241
266
  */
242
- function resolveImportPath(currentFile, importPath) {
267
+ function resolveImportPath(currentFile, importPath, parsingOptions) {
268
+ // Check cache first
269
+ const cacheKey = `${currentFile}::${importPath}`;
270
+ if (resolveImportPathCache.has(cacheKey)) {
271
+ return resolveImportPathCache.get(cacheKey);
272
+ }
243
273
  const basedir = path.dirname(currentFile);
244
274
  const extensions = ['.tsx', '.ts', '.jsx', '.js'];
275
+ const mainFields = ['module', 'main'];
276
+ let result = null;
245
277
  // 1. Try tsconfig-paths resolution first (handles TypeScript path mapping)
246
278
  const tsConfigResult = loadConfig(basedir);
247
279
  if (tsConfigResult.resultType === 'success') {
248
- const matchPath = createMatchPath(tsConfigResult.absoluteBaseUrl, tsConfigResult.paths, ['main', 'module', 'browser']);
280
+ const matchPath = createMatchPath(tsConfigResult.absoluteBaseUrl, tsConfigResult.paths, mainFields);
249
281
  // First try without any extension
250
282
  let tsResolved = matchPath(importPath);
251
283
  if (tsResolved && fs.existsSync(tsResolved)) {
252
- return tsResolved;
284
+ result = tsResolved;
285
+ resolveImportPathCache.set(cacheKey, result);
286
+ return result;
253
287
  }
254
288
  // Then try with each extension
255
289
  for (const ext of extensions) {
256
290
  tsResolved = matchPath(importPath + ext);
257
291
  if (tsResolved && fs.existsSync(tsResolved)) {
258
- return tsResolved;
292
+ result = tsResolved;
293
+ resolveImportPathCache.set(cacheKey, result);
294
+ return result;
259
295
  }
260
296
  // Also try the resolved path with extension
261
297
  tsResolved = matchPath(importPath);
262
298
  if (tsResolved) {
263
299
  const resolvedWithExt = tsResolved + ext;
264
300
  if (fs.existsSync(resolvedWithExt)) {
265
- return resolvedWithExt;
301
+ result = resolvedWithExt;
302
+ resolveImportPathCache.set(cacheKey, result);
303
+ return result;
266
304
  }
267
305
  }
268
306
  }
269
307
  }
270
- // 2. Fallback to Node.js resolution (handles relative paths and node_modules)
308
+ // 2. Try enhanced-resolve (handles package.json exports field and modern resolution)
309
+ try {
310
+ const resolver = ResolverFactory.createResolver({
311
+ useSyncFileSystemCalls: true,
312
+ fileSystem: fs,
313
+ extensions,
314
+ // Include 'development' condition to resolve to source files in monorepos
315
+ conditionNames: parsingOptions.conditionNames, // defaults to ['browser', 'module', 'import', 'require', 'default']. See generateSettings.ts for more details
316
+ exportsFields: ['exports'],
317
+ mainFields,
318
+ });
319
+ const resolved = resolver.resolveSync({}, basedir, importPath);
320
+ if (resolved) {
321
+ result = resolved;
322
+ resolveImportPathCache.set(cacheKey, result);
323
+ return result;
324
+ }
325
+ }
326
+ catch {
327
+ // Fall through to next resolution strategy
328
+ }
329
+ // 3. Fallback to Node.js resolution (handles relative paths and node_modules)
271
330
  try {
272
- return resolve.sync(importPath, { basedir, extensions });
331
+ result = resolve.sync(importPath, { basedir, extensions });
332
+ resolveImportPathCache.set(cacheKey, result);
333
+ return result;
273
334
  }
274
335
  catch {
275
336
  // If resolution fails, try to manually replace .js/.jsx with .ts/.tsx for source files
276
337
  if (importPath.endsWith('.js')) {
277
338
  const tsPath = importPath.replace(/\.js$/, '.ts');
278
339
  try {
279
- return resolve.sync(tsPath, { basedir, extensions });
340
+ result = resolve.sync(tsPath, { basedir, extensions });
341
+ resolveImportPathCache.set(cacheKey, result);
342
+ return result;
280
343
  }
281
344
  catch {
282
345
  // Continue to return null
@@ -285,12 +348,15 @@ function resolveImportPath(currentFile, importPath) {
285
348
  else if (importPath.endsWith('.jsx')) {
286
349
  const tsxPath = importPath.replace(/\.jsx$/, '.tsx');
287
350
  try {
288
- return resolve.sync(tsxPath, { basedir, extensions });
351
+ result = resolve.sync(tsxPath, { basedir, extensions });
352
+ resolveImportPathCache.set(cacheKey, result);
353
+ return result;
289
354
  }
290
355
  catch {
291
356
  // Continue to return null
292
357
  }
293
358
  }
359
+ resolveImportPathCache.set(cacheKey, null);
294
360
  return null;
295
361
  }
296
362
  }
@@ -302,19 +368,34 @@ function resolveImportPath(currentFile, importPath) {
302
368
  * - function getInfo(t) { ... }
303
369
  * - export function getInfo(t) { ... }
304
370
  * - const getInfo = (t) => { ... }
371
+ *
372
+ * If the function is not found in the file, follows re-exports (export * from './other')
305
373
  */
306
- function findFunctionInFile(filePath, functionName, argIndex, updates, errors, ignoreAdditionalData, ignoreDynamicContent) {
374
+ function processFunctionInFile(filePath, functionName, argIndex, updates, errors, ignoreAdditionalData, ignoreDynamicContent, parsingOptions, visited = new Set()) {
375
+ // Check cache first to avoid redundant parsing
376
+ const cacheKey = `${filePath}::${functionName}::${argIndex}`;
377
+ if (processFunctionCache.has(cacheKey)) {
378
+ return;
379
+ }
380
+ // Prevent infinite loops from circular re-exports
381
+ if (visited.has(filePath)) {
382
+ return;
383
+ }
384
+ visited.add(filePath);
307
385
  try {
308
386
  const code = fs.readFileSync(filePath, 'utf8');
309
387
  const ast = parse(code, {
310
388
  sourceType: 'module',
311
389
  plugins: ['jsx', 'typescript'],
312
390
  });
391
+ let found = false;
392
+ const reExports = [];
313
393
  traverse(ast, {
314
394
  // Handle function declarations: function getInfo(t) { ... }
315
395
  FunctionDeclaration(path) {
316
396
  if (path.node.id?.name === functionName) {
317
- processFunctionIfMatches(functionName, argIndex, path.node, path, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent);
397
+ found = true;
398
+ processFunctionIfMatches(functionName, argIndex, path.node, path, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent, parsingOptions);
318
399
  }
319
400
  },
320
401
  // Handle variable declarations: const getInfo = (t) => { ... }
@@ -324,14 +405,52 @@ function findFunctionInFile(filePath, functionName, argIndex, updates, errors, i
324
405
  path.node.init &&
325
406
  (t.isArrowFunctionExpression(path.node.init) ||
326
407
  t.isFunctionExpression(path.node.init))) {
408
+ found = true;
327
409
  const initPath = path.get('init');
328
- processFunctionIfMatches(functionName, argIndex, path.node.init, initPath, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent);
410
+ processFunctionIfMatches(functionName, argIndex, path.node.init, initPath, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent, parsingOptions);
411
+ }
412
+ },
413
+ // Collect re-exports: export * from './other'
414
+ ExportAllDeclaration(path) {
415
+ if (t.isStringLiteral(path.node.source)) {
416
+ reExports.push(path.node.source.value);
417
+ }
418
+ },
419
+ // Collect named re-exports: export { foo } from './other'
420
+ ExportNamedDeclaration(path) {
421
+ if (path.node.source && t.isStringLiteral(path.node.source)) {
422
+ // Check if this export includes our function
423
+ const exportsFunction = path.node.specifiers.some((spec) => {
424
+ if (t.isExportSpecifier(spec)) {
425
+ const exportedName = t.isIdentifier(spec.exported)
426
+ ? spec.exported.name
427
+ : spec.exported.value;
428
+ return exportedName === functionName;
429
+ }
430
+ return false;
431
+ });
432
+ if (exportsFunction) {
433
+ reExports.push(path.node.source.value);
434
+ }
329
435
  }
330
436
  },
331
437
  });
438
+ // If function not found, follow re-exports
439
+ if (!found && reExports.length > 0) {
440
+ for (const reExportPath of reExports) {
441
+ const resolvedPath = resolveImportPath(filePath, reExportPath, parsingOptions);
442
+ if (resolvedPath) {
443
+ processFunctionInFile(resolvedPath, functionName, argIndex, updates, errors, ignoreAdditionalData, ignoreDynamicContent, parsingOptions, visited);
444
+ }
445
+ }
446
+ }
447
+ // Mark this function search as processed in the cache
448
+ processFunctionCache.set(cacheKey, found);
332
449
  }
333
450
  catch {
334
451
  // Silently skip files that can't be parsed or accessed
452
+ // Still mark as processed to avoid retrying failed parses
453
+ processFunctionCache.set(cacheKey, false);
335
454
  }
336
455
  }
337
456
  /**
@@ -347,7 +466,7 @@ function findFunctionInFile(filePath, functionName, argIndex, updates, errors, i
347
466
  * - const { home } = getInfo(t); // getInfo is imported from './constants'
348
467
  * - This will parse constants.ts to find translation calls within getInfo function
349
468
  */
350
- export function parseStrings(importName, originalName, path, updates, errors, file) {
469
+ export function parseStrings(importName, originalName, path, updates, errors, file, parsingOptions) {
351
470
  // First, collect all imports in this file to track cross-file function calls
352
471
  const importMap = buildImportMap(path.scope.getProgramParent().path);
353
472
  const referencePaths = path.scope.bindings[importName]?.referencePaths || [];
@@ -402,7 +521,7 @@ export function parseStrings(importName, originalName, path, updates, errors, fi
402
521
  allTranslationNames.forEach((name) => {
403
522
  const tReferencePaths = variableScope.bindings[name]?.referencePaths || [];
404
523
  for (const tPath of tReferencePaths) {
405
- handleFunctionCall(tPath, updates, errors, file, importMap, ignoreAdditionalData, ignoreDynamicContent);
524
+ handleFunctionCall(tPath, updates, errors, file, importMap, ignoreAdditionalData, ignoreDynamicContent, parsingOptions);
406
525
  }
407
526
  });
408
527
  }
@@ -1,5 +1,6 @@
1
1
  import { Updates } from '../../types/index.js';
2
- export declare function createInlineUpdates(pkg: 'gt-react' | 'gt-next', validate: boolean, filePatterns: string[] | undefined): Promise<{
2
+ import type { ParsingConfigOptions } from '../../types/parsing.js';
3
+ export declare function createInlineUpdates(pkg: 'gt-react' | 'gt-next', validate: boolean, filePatterns: string[] | undefined, parsingOptions: ParsingConfigOptions): Promise<{
3
4
  updates: Updates;
4
5
  errors: string[];
5
6
  warnings: string[];
@@ -11,7 +11,7 @@ import { logError } from '../../console/logging.js';
11
11
  import { GT_TRANSLATION_FUNCS, INLINE_TRANSLATION_HOOK, INLINE_TRANSLATION_HOOK_ASYNC, INLINE_MESSAGE_HOOK, INLINE_MESSAGE_HOOK_ASYNC, MSG_TRANSLATION_HOOK, } from '../jsx/utils/constants.js';
12
12
  import { matchFiles } from '../../fs/matchFiles.js';
13
13
  import { DEFAULT_SRC_PATTERNS } from '../../config/generateSettings.js';
14
- export async function createInlineUpdates(pkg, validate, filePatterns) {
14
+ export async function createInlineUpdates(pkg, validate, filePatterns, parsingOptions) {
15
15
  const updates = [];
16
16
  const errors = [];
17
17
  const warnings = new Set();
@@ -91,7 +91,7 @@ export async function createInlineUpdates(pkg, validate, filePatterns) {
91
91
  });
92
92
  // Process translation functions asynchronously
93
93
  for (const { localName: name, originalName, path } of translationPaths) {
94
- parseStrings(name, originalName, path, updates, errors, file);
94
+ parseStrings(name, originalName, path, updates, errors, file, parsingOptions);
95
95
  }
96
96
  // Parse <T> components
97
97
  traverse(ast, {
@@ -1,4 +1,5 @@
1
1
  import { Updates, TranslateFlags } from '../types/index.js';
2
+ import type { ParsingConfigOptions } from '../types/parsing.js';
2
3
  /**
3
4
  * Searches for gt-react or gt-next dictionary files and creates updates for them,
4
5
  * as well as inline updates for <T> tags and useGT()/getGT() calls
@@ -8,7 +9,7 @@ import { Updates, TranslateFlags } from '../types/index.js';
8
9
  * @param pkg - The package name
9
10
  * @returns An object containing the updates and errors
10
11
  */
11
- export declare function createUpdates(options: TranslateFlags, src: string[] | undefined, sourceDictionary: string | undefined, pkg: 'gt-react' | 'gt-next', validate: boolean): Promise<{
12
+ export declare function createUpdates(options: TranslateFlags, src: string[] | undefined, sourceDictionary: string | undefined, pkg: 'gt-react' | 'gt-next', validate: boolean, parsingOptions: ParsingConfigOptions): Promise<{
12
13
  updates: Updates;
13
14
  errors: string[];
14
15
  warnings: string[];
@@ -14,7 +14,7 @@ import chalk from 'chalk';
14
14
  * @param pkg - The package name
15
15
  * @returns An object containing the updates and errors
16
16
  */
17
- export async function createUpdates(options, src, sourceDictionary, pkg, validate) {
17
+ export async function createUpdates(options, src, sourceDictionary, pkg, validate, parsingOptions) {
18
18
  let updates = [];
19
19
  let errors = [];
20
20
  let warnings = [];
@@ -48,7 +48,7 @@ export async function createUpdates(options, src, sourceDictionary, pkg, validat
48
48
  }
49
49
  }
50
50
  // Scan through project for <T> tags
51
- const { updates: newUpdates, errors: newErrors, warnings: newWarnings, } = await createInlineUpdates(pkg, validate, src);
51
+ const { updates: newUpdates, errors: newErrors, warnings: newWarnings, } = await createInlineUpdates(pkg, validate, src, parsingOptions);
52
52
  errors = [...errors, ...newErrors];
53
53
  warnings = [...warnings, ...newWarnings];
54
54
  updates = [...updates, ...newUpdates];
@@ -15,7 +15,7 @@ export async function aggregateReactTranslations(options, settings, library) {
15
15
  ]);
16
16
  }
17
17
  // ---- CREATING UPDATES ---- //
18
- const { updates, errors, warnings } = await createUpdates(options, settings.src, options.dictionary, library, false);
18
+ const { updates, errors, warnings } = await createUpdates(options, settings.src, options.dictionary, library, false, settings.parsingOptions);
19
19
  if (warnings.length > 0) {
20
20
  logWarning(chalk.yellow(`CLI tool encountered ${warnings.length} warnings while scanning for translatable content.\n` +
21
21
  warnings
@@ -7,7 +7,7 @@ import { createInlineUpdates } from '../react/parse/createInlineUpdates.js';
7
7
  export async function validateProject(settings, pkg, files) {
8
8
  if (files && files.length > 0) {
9
9
  // Validate specific files using createInlineUpdates
10
- const { errors, updates } = await createInlineUpdates(pkg, true, files);
10
+ const { errors, updates } = await createInlineUpdates(pkg, true, files, settings.parsingOptions);
11
11
  if (errors.length > 0) {
12
12
  logErrorAndExit(chalk.red(`Error: CLI tool encountered ${errors.length} syntax errors:\n` +
13
13
  errors
@@ -27,7 +27,7 @@ export async function validateProject(settings, pkg, files) {
27
27
  './src/dictionary.ts',
28
28
  ]);
29
29
  }
30
- const { updates, errors, warnings } = await createUpdates(settings, settings.src, settings.dictionary, pkg, true);
30
+ const { updates, errors, warnings } = await createUpdates(settings, settings.src, settings.dictionary, pkg, true, settings.parsingOptions);
31
31
  if (warnings.length > 0) {
32
32
  logWarning(chalk.yellow(`CLI tool encountered ${warnings.length} warnings while scanning for translatable content.`) +
33
33
  '\n' +
@@ -1,5 +1,6 @@
1
1
  import { CustomMapping } from 'generaltranslation/types';
2
2
  import { SUPPORTED_FILE_EXTENSIONS } from '../formats/files/supportedFiles.js';
3
+ import { ParsingConfigOptions } from './parsing.js';
3
4
  export type { Updates } from 'generaltranslation/types';
4
5
  export type Options = {
5
6
  config: string;
@@ -93,13 +94,13 @@ export type TransformOption = {
93
94
  replace: string;
94
95
  };
95
96
  export type TransformFiles = {
96
- [K in SupportedFileExtension]?: TransformOption | string;
97
+ [K in SupportedFileExtension]?: TransformOption | string | TransformOption[];
97
98
  };
98
99
  export type FilesOptions = {
99
100
  [K in SupportedFileExtension]?: {
100
101
  include: string[];
101
102
  exclude?: string[];
102
- transform?: string | TransformOption;
103
+ transform?: string | TransformOption | TransformOption[];
103
104
  };
104
105
  } & {
105
106
  gt?: {
@@ -130,6 +131,7 @@ export type Settings = {
130
131
  framework?: SupportedFrameworks;
131
132
  options?: AdditionalOptions;
132
133
  modelProvider?: string;
134
+ parsingOptions: ParsingConfigOptions;
133
135
  };
134
136
  export type AdditionalOptions = {
135
137
  jsonSchema?: {
@@ -0,0 +1,3 @@
1
+ export type ParsingConfigOptions = {
2
+ conditionNames: string[];
3
+ };
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "2.4.5",
3
+ "version": "2.4.7",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "files": [
@@ -71,6 +71,7 @@
71
71
  "chalk": "^5.4.1",
72
72
  "commander": "^12.1.0",
73
73
  "dotenv": "^16.4.5",
74
+ "enhanced-resolve": "^5.18.3",
74
75
  "esbuild": "^0.25.4",
75
76
  "fast-glob": "^3.3.3",
76
77
  "fast-json-stable-stringify": "^2.1.0",