gtx-cli 1.2.28 → 1.2.30-alpha.1
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 +6 -0
- package/dist/cli/react.js +1 -1
- package/dist/console/index.d.ts +3 -0
- package/dist/console/index.js +3 -0
- package/dist/hooks/postProcess.js +6 -6
- package/dist/react/jsx/utils/constants.d.ts +2 -0
- package/dist/react/jsx/utils/constants.js +14 -0
- package/dist/react/jsx/utils/parseJsx.d.ts +2 -1
- package/dist/react/jsx/utils/parseJsx.js +70 -60
- package/dist/react/jsx/utils/validateStringFunction.d.ts +7 -0
- package/dist/react/jsx/utils/validateStringFunction.js +30 -0
- package/dist/react/parse/createInlineUpdates.d.ts +1 -1
- package/dist/react/parse/createInlineUpdates.js +22 -17
- package/dist/translation/parse.d.ts +1 -1
- package/dist/translation/parse.js +2 -2
- package/dist/translation/stage.js +1 -1
- package/dist/translation/validate.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
+
## 1.2.29
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#400](https://github.com/generaltranslation/gt/pull/400) [`cf9c724`](https://github.com/generaltranslation/gt/commit/cf9c72488f74db5ccd7c4dca2650d75e3484d1f2) Thanks [@brian-lou](https://github.com/brian-lou)! - Reorder linter detection preference
|
|
8
|
+
|
|
3
9
|
## 1.2.28
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/cli/react.js
CHANGED
|
@@ -144,7 +144,7 @@ export class ReactCLI extends BaseCLI {
|
|
|
144
144
|
}
|
|
145
145
|
// User has to provide a dictionary file
|
|
146
146
|
// will not read from settings.files.resolvedPaths.json
|
|
147
|
-
const { updates, errors } = await createUpdates(options, options.dictionary, this.library === 'gt-next' ? 'gt-next' : 'gt-react');
|
|
147
|
+
const { updates, errors } = await createUpdates(options, options.dictionary, this.library === 'gt-next' ? 'gt-next' : 'gt-react', false);
|
|
148
148
|
if (errors.length > 0) {
|
|
149
149
|
if (options.ignoreErrors) {
|
|
150
150
|
logWarning(chalk.yellow(`CLI tool encountered errors while scanning for translatable content. These components will not be translated.\n` +
|
package/dist/console/index.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
export declare const warnApiKeyInConfigSync: (optionsFilepath: string) => string;
|
|
2
2
|
export declare const warnVariablePropSync: (file: string, attrName: string, value: string, location?: string) => string;
|
|
3
3
|
export declare const warnHasUnwrappedExpressionSync: (file: string, unwrappedExpressions: string[], id?: string, location?: string) => string;
|
|
4
|
+
export declare const warnNestedTComponent: (file: string, location?: string) => string;
|
|
4
5
|
export declare const warnNonStaticExpressionSync: (file: string, attrName: string, value: string, location?: string) => string;
|
|
5
6
|
export declare const warnTemplateLiteralSync: (file: string, value: string, location?: string) => string;
|
|
6
7
|
export declare const warnNonStringSync: (file: string, value: string, location?: string) => string;
|
|
8
|
+
export declare const warnAsyncUseGT: (file: string, location?: string) => string;
|
|
9
|
+
export declare const warnSyncGetGT: (file: string, location?: string) => string;
|
|
7
10
|
export declare const warnTernarySync: (file: string, location?: string) => string;
|
|
8
11
|
export declare const withLocation: (file: string, message: string, location?: string) => string;
|
|
9
12
|
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.";
|
package/dist/console/index.js
CHANGED
|
@@ -3,9 +3,12 @@ import { colorizeFilepath, colorizeComponent, colorizeIdString, colorizeContent,
|
|
|
3
3
|
export const warnApiKeyInConfigSync = (optionsFilepath) => `${colorizeFilepath(optionsFilepath)}: Your API key is exposed! Please remove it from the file and include it as an environment variable.`;
|
|
4
4
|
export const warnVariablePropSync = (file, attrName, value, location) => withLocation(file, `${colorizeComponent('<T>')} component has dynamic attribute ${colorizeIdString(attrName)} with value: ${colorizeContent(value)}. Change ${colorizeIdString(attrName)} to ensure this content is translated.`, location);
|
|
5
5
|
export const warnHasUnwrappedExpressionSync = (file, unwrappedExpressions, id, location) => withLocation(file, `${colorizeComponent('<T>')} component${id ? ` with id ${colorizeIdString(id)}` : ''} has children that could change at runtime. Use a variable component like ${colorizeComponent('<Var>')} to ensure this content is translated.\n${colorizeContent(unwrappedExpressions.join('\n'))}`, location);
|
|
6
|
+
export const warnNestedTComponent = (file, location) => withLocation(file, `Found nested <T> component. <T> components cannot be directly nested.`, location);
|
|
6
7
|
export const warnNonStaticExpressionSync = (file, attrName, value, location) => withLocation(file, `Found non-static expression for attribute ${colorizeIdString(attrName)}: ${colorizeContent(value)}. Change "${colorizeIdString(attrName)}" to ensure this content is translated.`, location);
|
|
7
8
|
export const warnTemplateLiteralSync = (file, value, location) => withLocation(file, `Found template literal with quasis (${colorizeContent(value)}). Change the template literal to a string to ensure this content is translated.`, location);
|
|
8
9
|
export const warnNonStringSync = (file, value, location) => withLocation(file, `Found non-string literal (${colorizeContent(value)}). Change the value to a string literal to ensure this content is translated.`, location);
|
|
10
|
+
export const warnAsyncUseGT = (file, location) => withLocation(file, `Found useGT() in an async function. Use getGT() instead, or make the function synchronous.`, location);
|
|
11
|
+
export const warnSyncGetGT = (file, location) => withLocation(file, `Found getGT() in a synchronous function. Use useGT() instead, or make the function async.`, location);
|
|
9
12
|
export const warnTernarySync = (file, location) => withLocation(file, 'Found ternary expression. A Branch component may be more appropriate here.', location);
|
|
10
13
|
export const withLocation = (file, message, location) => `${colorizeFilepath(file)}${location ? ` (${colorizeLine(location)})` : ''}: ${message}`;
|
|
11
14
|
// Re-export error messages
|
|
@@ -9,6 +9,12 @@ export async function detectFormatter() {
|
|
|
9
9
|
return 'prettier';
|
|
10
10
|
}
|
|
11
11
|
catch { }
|
|
12
|
+
// Try ESLint
|
|
13
|
+
try {
|
|
14
|
+
await import('eslint');
|
|
15
|
+
return 'eslint';
|
|
16
|
+
}
|
|
17
|
+
catch { }
|
|
12
18
|
// Try Biome
|
|
13
19
|
try {
|
|
14
20
|
return await new Promise((resolve, reject) => {
|
|
@@ -29,12 +35,6 @@ export async function detectFormatter() {
|
|
|
29
35
|
});
|
|
30
36
|
}
|
|
31
37
|
catch { }
|
|
32
|
-
// Try ESLint
|
|
33
|
-
try {
|
|
34
|
-
await import('eslint');
|
|
35
|
-
return 'eslint';
|
|
36
|
-
}
|
|
37
|
-
catch { }
|
|
38
38
|
return null;
|
|
39
39
|
}
|
|
40
40
|
export async function formatFiles(filesUpdated, formatter) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// GT translation functions
|
|
2
|
+
export const GT_TRANSLATION_FUNCS = [
|
|
3
|
+
'useGT',
|
|
4
|
+
'getGT',
|
|
5
|
+
'T',
|
|
6
|
+
'Var',
|
|
7
|
+
'DateTime',
|
|
8
|
+
'Currency',
|
|
9
|
+
'Num',
|
|
10
|
+
'Branch',
|
|
11
|
+
'Plural',
|
|
12
|
+
];
|
|
13
|
+
// Valid variable components
|
|
14
|
+
export const VARIABLE_COMPONENTS = ['Var', 'DateTime', 'Currency', 'Num'];
|
|
@@ -7,7 +7,8 @@ import * as t from '@babel/types';
|
|
|
7
7
|
* @param updates - The updates array
|
|
8
8
|
* @param errors - The errors array
|
|
9
9
|
* @param file - The file name
|
|
10
|
+
* @param insideT - Whether the current node is inside a <T> component
|
|
10
11
|
* @returns The built JSX tree
|
|
11
12
|
*/
|
|
12
|
-
export declare function buildJSXTree(importAliases: Record<string, string>, node: any, unwrappedExpressions: string[], updates: Updates, errors: string[], file: string): any;
|
|
13
|
+
export declare function buildJSXTree(importAliases: Record<string, string>, node: any, unwrappedExpressions: string[], updates: Updates, errors: string[], file: string, insideT: boolean): any;
|
|
13
14
|
export declare function parseJSXElement(importAliases: Record<string, string>, node: t.JSXElement, updates: Updates, errors: string[], file: string): void;
|
|
@@ -3,12 +3,11 @@ import generateModule from '@babel/generator';
|
|
|
3
3
|
const generate = generateModule.default || generateModule;
|
|
4
4
|
import * as t from '@babel/types';
|
|
5
5
|
import addGTIdentifierToSyntaxTree from '../../data-_gt/addGTIdentifierToSyntaxTree.js';
|
|
6
|
-
import { warnHasUnwrappedExpressionSync, warnVariablePropSync, } from '../../../console/index.js';
|
|
6
|
+
import { warnHasUnwrappedExpressionSync, warnVariablePropSync, warnNestedTComponent, } from '../../../console/index.js';
|
|
7
7
|
import { isAcceptedPluralForm } from 'generaltranslation/internal';
|
|
8
8
|
import { handleChildrenWhitespace } from '../trimJsxStringChildren.js';
|
|
9
9
|
import { isStaticExpression } from '../evaluateJsx.js';
|
|
10
|
-
|
|
11
|
-
const VARIABLE_COMPONENTS = ['Var', 'DateTime', 'Currency', 'Num'];
|
|
10
|
+
import { VARIABLE_COMPONENTS } from './constants.js';
|
|
12
11
|
/**
|
|
13
12
|
* Builds a JSX tree from a given node, recursively handling children.
|
|
14
13
|
* @param node - The node to build the tree from
|
|
@@ -16,9 +15,10 @@ const VARIABLE_COMPONENTS = ['Var', 'DateTime', 'Currency', 'Num'];
|
|
|
16
15
|
* @param updates - The updates array
|
|
17
16
|
* @param errors - The errors array
|
|
18
17
|
* @param file - The file name
|
|
18
|
+
* @param insideT - Whether the current node is inside a <T> component
|
|
19
19
|
* @returns The built JSX tree
|
|
20
20
|
*/
|
|
21
|
-
export function buildJSXTree(importAliases, node, unwrappedExpressions, updates, errors, file) {
|
|
21
|
+
export function buildJSXTree(importAliases, node, unwrappedExpressions, updates, errors, file, insideT) {
|
|
22
22
|
if (t.isJSXExpressionContainer(node)) {
|
|
23
23
|
// Skip JSX comments
|
|
24
24
|
if (t.isJSXEmptyExpression(node.expression)) {
|
|
@@ -59,6 +59,11 @@ export function buildJSXTree(importAliases, node, unwrappedExpressions, updates,
|
|
|
59
59
|
}
|
|
60
60
|
// Convert from alias to original name
|
|
61
61
|
const componentType = importAliases[typeName ?? ''];
|
|
62
|
+
if (componentType === 'T' && insideT) {
|
|
63
|
+
// Add error: No nested <T> components are allowed
|
|
64
|
+
errors.push(warnNestedTComponent(file, `${element.loc?.start?.line}:${element.loc?.start?.column}`));
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
62
67
|
// If this JSXElement is one of the recognized variable components,
|
|
63
68
|
const elementIsVariable = VARIABLE_COMPONENTS.includes(componentType);
|
|
64
69
|
const props = {};
|
|
@@ -81,7 +86,7 @@ export function buildJSXTree(importAliases, node, unwrappedExpressions, updates,
|
|
|
81
86
|
unwrappedExpressions.push(generate(attr.value).code);
|
|
82
87
|
}
|
|
83
88
|
}
|
|
84
|
-
attrValue = buildJSXTree(importAliases, attr.value.expression, unwrappedExpressions, updates, errors, file);
|
|
89
|
+
attrValue = buildJSXTree(importAliases, attr.value.expression, unwrappedExpressions, updates, errors, file, true);
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
props[attrName] = attrValue;
|
|
@@ -97,7 +102,7 @@ export function buildJSXTree(importAliases, node, unwrappedExpressions, updates,
|
|
|
97
102
|
};
|
|
98
103
|
}
|
|
99
104
|
const children = element.children
|
|
100
|
-
.map((child) => buildJSXTree(importAliases, child, unwrappedExpressions, updates, errors, file))
|
|
105
|
+
.map((child) => buildJSXTree(importAliases, child, unwrappedExpressions, updates, errors, file, true))
|
|
101
106
|
.filter((child) => child !== null && child !== '');
|
|
102
107
|
if (children.length === 1) {
|
|
103
108
|
props.children = children[0];
|
|
@@ -115,7 +120,7 @@ export function buildJSXTree(importAliases, node, unwrappedExpressions, updates,
|
|
|
115
120
|
// If it's a JSX fragment
|
|
116
121
|
else if (t.isJSXFragment(node)) {
|
|
117
122
|
const children = node.children
|
|
118
|
-
.map((child) => buildJSXTree(importAliases, child, unwrappedExpressions, updates, errors, file))
|
|
123
|
+
.map((child) => buildJSXTree(importAliases, child, unwrappedExpressions, updates, errors, file, true))
|
|
119
124
|
.filter((child) => child !== null && child !== '');
|
|
120
125
|
const props = {};
|
|
121
126
|
if (children.length === 1) {
|
|
@@ -152,65 +157,70 @@ export function parseJSXElement(importAliases, node, updates, errors, file) {
|
|
|
152
157
|
const openingElement = node.openingElement;
|
|
153
158
|
const name = openingElement.name;
|
|
154
159
|
// Only proceed if it's <T> ...
|
|
155
|
-
if (name.type === 'JSXIdentifier' && importAliases[name.name] === 'T') {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
componentObj.props[attrName] = code;
|
|
188
|
-
}
|
|
160
|
+
if (!(name.type === 'JSXIdentifier' && importAliases[name.name] === 'T')) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const componentErrors = [];
|
|
164
|
+
const componentObj = { props: {} };
|
|
165
|
+
// We'll track this flag to know if any unwrapped {variable} is found in children
|
|
166
|
+
const unwrappedExpressions = [];
|
|
167
|
+
// Gather <T>'s props
|
|
168
|
+
openingElement.attributes.forEach((attr) => {
|
|
169
|
+
if (!t.isJSXAttribute(attr))
|
|
170
|
+
return;
|
|
171
|
+
const attrName = attr.name.name;
|
|
172
|
+
if (typeof attrName !== 'string')
|
|
173
|
+
return;
|
|
174
|
+
if (attr.value) {
|
|
175
|
+
// If it's a plain string literal like id="hello"
|
|
176
|
+
if (t.isStringLiteral(attr.value)) {
|
|
177
|
+
componentObj.props[attrName] = attr.value.value;
|
|
178
|
+
}
|
|
179
|
+
// If it's an expression container like id={"hello"}, id={someVar}, etc.
|
|
180
|
+
else if (t.isJSXExpressionContainer(attr.value)) {
|
|
181
|
+
const expr = attr.value.expression;
|
|
182
|
+
const code = generate(expr).code;
|
|
183
|
+
// Only check for static expressions on id and context props
|
|
184
|
+
if (attrName === 'id' || attrName === 'context') {
|
|
185
|
+
const staticAnalysis = isStaticExpression(expr);
|
|
186
|
+
if (!staticAnalysis.isStatic) {
|
|
187
|
+
componentErrors.push(warnVariablePropSync(file, attrName, code, `${expr.loc?.start?.line}:${expr.loc?.start?.column}`));
|
|
188
|
+
}
|
|
189
|
+
// Use the static value if available
|
|
190
|
+
if (staticAnalysis.isStatic && staticAnalysis.value !== undefined) {
|
|
191
|
+
componentObj.props[attrName] = staticAnalysis.value;
|
|
189
192
|
}
|
|
190
193
|
else {
|
|
191
|
-
//
|
|
194
|
+
// Only store the code if we couldn't extract a static value
|
|
192
195
|
componentObj.props[attrName] = code;
|
|
193
196
|
}
|
|
194
197
|
}
|
|
198
|
+
else {
|
|
199
|
+
// For other attributes that aren't id or context
|
|
200
|
+
componentObj.props[attrName] = code;
|
|
201
|
+
}
|
|
195
202
|
}
|
|
196
|
-
});
|
|
197
|
-
// Build the JSX tree for this component
|
|
198
|
-
const initialTree = buildJSXTree(importAliases, node, unwrappedExpressions, updates, errors, file).props.children;
|
|
199
|
-
const whitespaceHandledTree = handleChildrenWhitespace(initialTree);
|
|
200
|
-
const tree = addGTIdentifierToSyntaxTree(whitespaceHandledTree);
|
|
201
|
-
componentObj.tree = tree.length === 1 ? tree[0] : tree;
|
|
202
|
-
const id = componentObj.props.id;
|
|
203
|
-
// If we found an unwrapped expression, skip
|
|
204
|
-
if (unwrappedExpressions.length > 0) {
|
|
205
|
-
errors.push(warnHasUnwrappedExpressionSync(file, unwrappedExpressions, id, `${node.loc?.start?.line}:${node.loc?.start?.column}`));
|
|
206
203
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
204
|
+
});
|
|
205
|
+
// Build the JSX tree for this component
|
|
206
|
+
const initialTree = buildJSXTree(importAliases, node, unwrappedExpressions, updates, componentErrors, file, false)?.props?.children;
|
|
207
|
+
if (componentErrors.length > 0) {
|
|
208
|
+
errors.push(...componentErrors);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const whitespaceHandledTree = handleChildrenWhitespace(initialTree);
|
|
212
|
+
const tree = addGTIdentifierToSyntaxTree(whitespaceHandledTree);
|
|
213
|
+
componentObj.tree = tree.length === 1 ? tree[0] : tree;
|
|
214
|
+
const id = componentObj.props.id;
|
|
215
|
+
// If we found an unwrapped expression, skip
|
|
216
|
+
if (unwrappedExpressions.length > 0) {
|
|
217
|
+
errors.push(warnHasUnwrappedExpressionSync(file, unwrappedExpressions, id, `${node.loc?.start?.line}:${node.loc?.start?.column}`));
|
|
218
|
+
return;
|
|
215
219
|
}
|
|
220
|
+
// <T> is valid here
|
|
221
|
+
updates.push({
|
|
222
|
+
dataFormat: 'JSX',
|
|
223
|
+
source: componentObj.tree,
|
|
224
|
+
metadata: componentObj.props,
|
|
225
|
+
});
|
|
216
226
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NodePath } from '@babel/traverse';
|
|
2
|
+
import { Updates } from '../../../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Validate useGT() / await getGT() calls
|
|
5
|
+
* 1. Validates that the call does not violate the rules of React (no hooks in async functions)
|
|
6
|
+
*/
|
|
7
|
+
export declare function validateStringFunction(localImportName: string, path: NodePath, updates: Updates, errors: string[], file: string, originalImportName: string): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { warnAsyncUseGT, warnSyncGetGT } from '../../../console/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validate useGT() / await getGT() calls
|
|
4
|
+
* 1. Validates that the call does not violate the rules of React (no hooks in async functions)
|
|
5
|
+
*/
|
|
6
|
+
export function validateStringFunction(localImportName, path, updates, errors, file, originalImportName) {
|
|
7
|
+
// Get the root program node to traverse the entire file
|
|
8
|
+
const program = path.scope.getProgramParent().path;
|
|
9
|
+
program.traverse({
|
|
10
|
+
CallExpression(callPath) {
|
|
11
|
+
if (callPath.node.callee.type === 'Identifier' &&
|
|
12
|
+
callPath.node.callee.name === localImportName) {
|
|
13
|
+
// Check the function scope
|
|
14
|
+
const functionScope = callPath.getFunctionParent();
|
|
15
|
+
if (originalImportName === 'useGT') {
|
|
16
|
+
// useGT should NOT be in an async function
|
|
17
|
+
if (functionScope && functionScope.node.async) {
|
|
18
|
+
errors.push(warnAsyncUseGT(file, `${callPath.node.loc?.start?.line}:${callPath.node.loc?.start?.column}`));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else if (originalImportName === 'getGT') {
|
|
22
|
+
// getGT should be in an async function
|
|
23
|
+
if (!functionScope || !functionScope.node.async) {
|
|
24
|
+
errors.push(warnSyncGetGT(file, `${callPath.node.loc?.start?.line}:${callPath.node.loc?.start?.column}`));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Options, Updates } from '../../types/index.js';
|
|
2
|
-
export default function createInlineUpdates(options: Options, pkg: 'gt-react' | 'gt-next'): Promise<{
|
|
2
|
+
export default function createInlineUpdates(options: Options, pkg: 'gt-react' | 'gt-next', validate: boolean): Promise<{
|
|
3
3
|
updates: Updates;
|
|
4
4
|
errors: string[];
|
|
5
5
|
}>;
|
|
@@ -9,7 +9,9 @@ import { parseJSXElement } from '../jsx/utils/parseJsx.js';
|
|
|
9
9
|
import { parseStrings } from '../jsx/utils/parseStringFunction.js';
|
|
10
10
|
import { extractImportName } from '../jsx/utils/parseAst.js';
|
|
11
11
|
import { logError } from '../../console/logging.js';
|
|
12
|
-
|
|
12
|
+
import { validateStringFunction } from '../jsx/utils/validateStringFunction.js';
|
|
13
|
+
import { GT_TRANSLATION_FUNCS } from '../jsx/utils/constants.js';
|
|
14
|
+
export default async function createInlineUpdates(options, pkg, validate) {
|
|
13
15
|
const updates = [];
|
|
14
16
|
const errors = [];
|
|
15
17
|
// Use the provided app directory or default to the current directory
|
|
@@ -56,27 +58,20 @@ export default async function createInlineUpdates(options, pkg) {
|
|
|
56
58
|
logError(`Error parsing file ${file}: ${error}`);
|
|
57
59
|
continue;
|
|
58
60
|
}
|
|
59
|
-
const translationFuncs = [
|
|
60
|
-
'useGT',
|
|
61
|
-
'getGT',
|
|
62
|
-
'T',
|
|
63
|
-
'Var',
|
|
64
|
-
'DateTime',
|
|
65
|
-
'Currency',
|
|
66
|
-
'Num',
|
|
67
|
-
'Branch',
|
|
68
|
-
'Plural',
|
|
69
|
-
];
|
|
70
61
|
const importAliases = {};
|
|
71
62
|
// First pass: collect imports and process translation functions
|
|
72
63
|
const translationPaths = [];
|
|
73
64
|
traverse(ast, {
|
|
74
65
|
ImportDeclaration(path) {
|
|
75
66
|
if (path.node.source.value.startsWith(pkg)) {
|
|
76
|
-
const importName = extractImportName(path.node, pkg,
|
|
67
|
+
const importName = extractImportName(path.node, pkg, GT_TRANSLATION_FUNCS);
|
|
77
68
|
for (const name of importName) {
|
|
78
69
|
if (name.original === 'useGT' || name.original === 'getGT') {
|
|
79
|
-
translationPaths.push({
|
|
70
|
+
translationPaths.push({
|
|
71
|
+
localName: name.local,
|
|
72
|
+
path,
|
|
73
|
+
originalName: name.original,
|
|
74
|
+
});
|
|
80
75
|
}
|
|
81
76
|
else {
|
|
82
77
|
importAliases[name.local] = name.original;
|
|
@@ -96,10 +91,14 @@ export default async function createInlineUpdates(options, pkg) {
|
|
|
96
91
|
args[0].value.startsWith(pkg)) {
|
|
97
92
|
const parentPath = path.parentPath;
|
|
98
93
|
if (parentPath.isVariableDeclaration()) {
|
|
99
|
-
const importName = extractImportName(parentPath.node, pkg,
|
|
94
|
+
const importName = extractImportName(parentPath.node, pkg, GT_TRANSLATION_FUNCS);
|
|
100
95
|
for (const name of importName) {
|
|
101
96
|
if (name.original === 'useGT' || name.original === 'getGT') {
|
|
102
|
-
translationPaths.push({
|
|
97
|
+
translationPaths.push({
|
|
98
|
+
localName: name.local,
|
|
99
|
+
path: parentPath,
|
|
100
|
+
originalName: name.original,
|
|
101
|
+
});
|
|
103
102
|
}
|
|
104
103
|
else {
|
|
105
104
|
importAliases[name.local] = name.original;
|
|
@@ -111,7 +110,7 @@ export default async function createInlineUpdates(options, pkg) {
|
|
|
111
110
|
},
|
|
112
111
|
});
|
|
113
112
|
// Process translation functions asynchronously
|
|
114
|
-
for (const { name, path } of translationPaths) {
|
|
113
|
+
for (const { localName: name, path } of translationPaths) {
|
|
115
114
|
parseStrings(name, path, updates, errors, file);
|
|
116
115
|
}
|
|
117
116
|
// Parse <T> components
|
|
@@ -120,6 +119,12 @@ export default async function createInlineUpdates(options, pkg) {
|
|
|
120
119
|
parseJSXElement(importAliases, path.node, updates, errors, file);
|
|
121
120
|
},
|
|
122
121
|
});
|
|
122
|
+
// Extra validation (for Locadex)
|
|
123
|
+
if (validate) {
|
|
124
|
+
for (const { localName: name, path, originalName } of translationPaths) {
|
|
125
|
+
validateStringFunction(name, path, updates, errors, file, originalName);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
123
128
|
}
|
|
124
129
|
// Post-process to add a hash to each update
|
|
125
130
|
await Promise.all(updates.map(async (update) => {
|
|
@@ -8,7 +8,7 @@ import { Options, GenerateSourceOptions, Updates } from '../types/index.js';
|
|
|
8
8
|
* @param pkg - The package name
|
|
9
9
|
* @returns An object containing the updates and errors
|
|
10
10
|
*/
|
|
11
|
-
export declare function createUpdates(options: Options | GenerateSourceOptions, sourceDictionary: string | undefined, pkg: 'gt-react' | 'gt-next'): Promise<{
|
|
11
|
+
export declare function createUpdates(options: Options | GenerateSourceOptions, sourceDictionary: string | undefined, pkg: 'gt-react' | 'gt-next', validate: boolean): Promise<{
|
|
12
12
|
updates: Updates;
|
|
13
13
|
errors: string[];
|
|
14
14
|
}>;
|
|
@@ -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, sourceDictionary, pkg) {
|
|
17
|
+
export async function createUpdates(options, sourceDictionary, pkg, validate) {
|
|
18
18
|
let updates = [];
|
|
19
19
|
let errors = [];
|
|
20
20
|
// Parse dictionary with esbuildConfig
|
|
@@ -48,7 +48,7 @@ export async function createUpdates(options, sourceDictionary, pkg) {
|
|
|
48
48
|
}
|
|
49
49
|
// Scan through project for <T> tags
|
|
50
50
|
if (options.inline) {
|
|
51
|
-
const { updates: newUpdates, errors: newErrors } = await createInlineUpdates(options, pkg);
|
|
51
|
+
const { updates: newUpdates, errors: newErrors } = await createInlineUpdates(options, pkg, validate);
|
|
52
52
|
errors = [...errors, ...newErrors];
|
|
53
53
|
updates = [...updates, ...newUpdates];
|
|
54
54
|
}
|
|
@@ -26,7 +26,7 @@ export async function stageProject(settings, pkg) {
|
|
|
26
26
|
}
|
|
27
27
|
settings.timeout = timeout.toString();
|
|
28
28
|
// ---- CREATING UPDATES ---- //
|
|
29
|
-
const { updates, errors } = await createUpdates(settings, settings.dictionary, pkg);
|
|
29
|
+
const { updates, errors } = await createUpdates(settings, settings.dictionary, pkg, false);
|
|
30
30
|
if (errors.length > 0) {
|
|
31
31
|
if (settings.ignoreErrors) {
|
|
32
32
|
logWarning(chalk.yellow(`Warning: CLI tool encountered ${errors.length} syntax errors while scanning for translatable content. These components will not be translated.\n` +
|
|
@@ -14,7 +14,7 @@ export async function validateProject(settings, pkg) {
|
|
|
14
14
|
'./src/dictionary.ts',
|
|
15
15
|
]);
|
|
16
16
|
}
|
|
17
|
-
const { updates, errors } = await createUpdates(settings, settings.dictionary, pkg);
|
|
17
|
+
const { updates, errors } = await createUpdates(settings, settings.dictionary, pkg, true);
|
|
18
18
|
if (errors.length > 0) {
|
|
19
19
|
logErrorAndExit(chalk.red(`Error: CLI tool encountered ${errors.length} syntax errors while scanning for translatable content.\n` +
|
|
20
20
|
errors
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gtx-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.30-alpha.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": "dist/main.js",
|
|
6
6
|
"files": [
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"tsconfig-paths": "^4.2.0"
|
|
94
94
|
},
|
|
95
95
|
"devDependencies": {
|
|
96
|
-
"@biomejs/biome": "1.9.4",
|
|
96
|
+
"@biomejs/biome": "^1.9.4",
|
|
97
97
|
"@types/babel__generator": "^7.27.0",
|
|
98
98
|
"@types/babel__traverse": "^7.20.6",
|
|
99
99
|
"@types/figlet": "^1.7.0",
|