gtx-cli 2.3.5 → 2.3.6-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/package.json +23 -18
  2. package/dist/api/checkFileTranslations.d.ts +0 -23
  3. package/dist/api/checkFileTranslations.js +0 -236
  4. package/dist/api/downloadFileBatch.d.ts +0 -20
  5. package/dist/api/downloadFileBatch.js +0 -113
  6. package/dist/api/sendFiles.d.ts +0 -17
  7. package/dist/api/sendFiles.js +0 -115
  8. package/dist/api/uploadFiles.d.ts +0 -27
  9. package/dist/api/uploadFiles.js +0 -40
  10. package/dist/cli/base.d.ts +0 -32
  11. package/dist/cli/base.js +0 -335
  12. package/dist/cli/commands/stage.d.ts +0 -5
  13. package/dist/cli/commands/stage.js +0 -100
  14. package/dist/cli/commands/translate.d.ts +0 -6
  15. package/dist/cli/commands/translate.js +0 -63
  16. package/dist/cli/flags.d.ts +0 -3
  17. package/dist/cli/flags.js +0 -38
  18. package/dist/cli/next.d.ts +0 -11
  19. package/dist/cli/next.js +0 -20
  20. package/dist/cli/react.d.ts +0 -18
  21. package/dist/cli/react.js +0 -175
  22. package/dist/config/generateSettings.d.ts +0 -9
  23. package/dist/config/generateSettings.js +0 -176
  24. package/dist/config/optionPresets.d.ts +0 -2
  25. package/dist/config/optionPresets.js +0 -56
  26. package/dist/config/resolveConfig.d.ts +0 -4
  27. package/dist/config/resolveConfig.js +0 -19
  28. package/dist/config/utils.d.ts +0 -2
  29. package/dist/config/utils.js +0 -4
  30. package/dist/config/validateSettings.d.ts +0 -3
  31. package/dist/config/validateSettings.js +0 -32
  32. package/dist/console/colors.d.ts +0 -5
  33. package/dist/console/colors.js +0 -16
  34. package/dist/console/index.d.ts +0 -21
  35. package/dist/console/index.js +0 -24
  36. package/dist/console/logging.d.ts +0 -53
  37. package/dist/console/logging.js +0 -185
  38. package/dist/formats/files/fileMapping.d.ts +0 -11
  39. package/dist/formats/files/fileMapping.js +0 -82
  40. package/dist/formats/files/save.d.ts +0 -5
  41. package/dist/formats/files/save.js +0 -17
  42. package/dist/formats/files/supportedFiles.d.ts +0 -10
  43. package/dist/formats/files/supportedFiles.js +0 -18
  44. package/dist/formats/files/translate.d.ts +0 -4
  45. package/dist/formats/files/translate.js +0 -119
  46. package/dist/formats/files/upload.d.ts +0 -13
  47. package/dist/formats/files/upload.js +0 -136
  48. package/dist/formats/gt/save.d.ts +0 -9
  49. package/dist/formats/gt/save.js +0 -26
  50. package/dist/formats/json/flattenJson.d.ts +0 -14
  51. package/dist/formats/json/flattenJson.js +0 -64
  52. package/dist/formats/json/mergeJson.d.ts +0 -13
  53. package/dist/formats/json/mergeJson.js +0 -257
  54. package/dist/formats/json/parseJson.d.ts +0 -2
  55. package/dist/formats/json/parseJson.js +0 -108
  56. package/dist/formats/json/utils.d.ts +0 -47
  57. package/dist/formats/json/utils.js +0 -149
  58. package/dist/formats/utils.d.ts +0 -2
  59. package/dist/formats/utils.js +0 -24
  60. package/dist/formats/yaml/mergeYaml.d.ts +0 -5
  61. package/dist/formats/yaml/mergeYaml.js +0 -55
  62. package/dist/formats/yaml/parseYaml.d.ts +0 -5
  63. package/dist/formats/yaml/parseYaml.js +0 -23
  64. package/dist/formats/yaml/utils.d.ts +0 -2
  65. package/dist/formats/yaml/utils.js +0 -22
  66. package/dist/fs/config/loadConfig.d.ts +0 -1
  67. package/dist/fs/config/loadConfig.js +0 -9
  68. package/dist/fs/config/parseFilesConfig.d.ts +0 -27
  69. package/dist/fs/config/parseFilesConfig.js +0 -129
  70. package/dist/fs/config/setupConfig.d.ts +0 -17
  71. package/dist/fs/config/setupConfig.js +0 -50
  72. package/dist/fs/config/updateConfig.d.ts +0 -10
  73. package/dist/fs/config/updateConfig.js +0 -36
  74. package/dist/fs/config/updateVersions.d.ts +0 -10
  75. package/dist/fs/config/updateVersions.js +0 -30
  76. package/dist/fs/copyFile.d.ts +0 -7
  77. package/dist/fs/copyFile.js +0 -39
  78. package/dist/fs/createLoadTranslationsFile.d.ts +0 -1
  79. package/dist/fs/createLoadTranslationsFile.js +0 -36
  80. package/dist/fs/determineFramework.d.ts +0 -5
  81. package/dist/fs/determineFramework.js +0 -46
  82. package/dist/fs/findFilepath.d.ts +0 -36
  83. package/dist/fs/findFilepath.js +0 -89
  84. package/dist/fs/getPackageResource.d.ts +0 -1
  85. package/dist/fs/getPackageResource.js +0 -6
  86. package/dist/fs/index.d.ts +0 -1
  87. package/dist/fs/index.js +0 -1
  88. package/dist/fs/loadJSON.d.ts +0 -6
  89. package/dist/fs/loadJSON.js +0 -17
  90. package/dist/fs/matchFiles.d.ts +0 -1
  91. package/dist/fs/matchFiles.js +0 -8
  92. package/dist/fs/saveJSON.d.ts +0 -1
  93. package/dist/fs/saveJSON.js +0 -7
  94. package/dist/fs/utils.d.ts +0 -1
  95. package/dist/fs/utils.js +0 -16
  96. package/dist/hooks/postProcess.d.ts +0 -4
  97. package/dist/hooks/postProcess.js +0 -110
  98. package/dist/index.d.ts +0 -4
  99. package/dist/index.js +0 -20
  100. package/dist/main.d.ts +0 -2
  101. package/dist/main.js +0 -9
  102. package/dist/next/config/parseNextConfig.d.ts +0 -10
  103. package/dist/next/config/parseNextConfig.js +0 -53
  104. package/dist/next/jsx/utils.d.ts +0 -7
  105. package/dist/next/jsx/utils.js +0 -42
  106. package/dist/next/parse/handleInitGT.d.ts +0 -7
  107. package/dist/next/parse/handleInitGT.js +0 -208
  108. package/dist/next/parse/wrapContent.d.ts +0 -11
  109. package/dist/next/parse/wrapContent.js +0 -163
  110. package/dist/react/config/createESBuildConfig.d.ts +0 -2
  111. package/dist/react/config/createESBuildConfig.js +0 -119
  112. package/dist/react/data-_gt/addGTIdentifierToSyntaxTree.d.ts +0 -8
  113. package/dist/react/data-_gt/addGTIdentifierToSyntaxTree.js +0 -111
  114. package/dist/react/jsx/evaluateJsx.d.ts +0 -17
  115. package/dist/react/jsx/evaluateJsx.js +0 -85
  116. package/dist/react/jsx/trimJsxStringChildren.d.ts +0 -7
  117. package/dist/react/jsx/trimJsxStringChildren.js +0 -95
  118. package/dist/react/jsx/utils/constants.d.ts +0 -10
  119. package/dist/react/jsx/utils/constants.js +0 -31
  120. package/dist/react/jsx/utils/parseAst.d.ts +0 -30
  121. package/dist/react/jsx/utils/parseAst.js +0 -277
  122. package/dist/react/jsx/utils/parseJsx.d.ts +0 -21
  123. package/dist/react/jsx/utils/parseJsx.js +0 -244
  124. package/dist/react/jsx/utils/parseStringFunction.d.ts +0 -16
  125. package/dist/react/jsx/utils/parseStringFunction.js +0 -411
  126. package/dist/react/jsx/utils/validateStringFunction.d.ts +0 -7
  127. package/dist/react/jsx/utils/validateStringFunction.js +0 -31
  128. package/dist/react/jsx/wrapJsx.d.ts +0 -51
  129. package/dist/react/jsx/wrapJsx.js +0 -387
  130. package/dist/react/parse/createDictionaryUpdates.d.ts +0 -3
  131. package/dist/react/parse/createDictionaryUpdates.js +0 -169
  132. package/dist/react/parse/createInlineUpdates.d.ts +0 -6
  133. package/dist/react/parse/createInlineUpdates.js +0 -122
  134. package/dist/react/parse/wrapContent.d.ts +0 -11
  135. package/dist/react/parse/wrapContent.js +0 -162
  136. package/dist/react/utils/flattenDictionary.d.ts +0 -20
  137. package/dist/react/utils/flattenDictionary.js +0 -75
  138. package/dist/react/utils/getEntryAndMetadata.d.ts +0 -5
  139. package/dist/react/utils/getEntryAndMetadata.js +0 -11
  140. package/dist/react/utils/getVariableName.d.ts +0 -25
  141. package/dist/react/utils/getVariableName.js +0 -37
  142. package/dist/setup/userInput.d.ts +0 -4
  143. package/dist/setup/userInput.js +0 -29
  144. package/dist/setup/wizard.d.ts +0 -2
  145. package/dist/setup/wizard.js +0 -127
  146. package/dist/translation/parse.d.ts +0 -15
  147. package/dist/translation/parse.js +0 -76
  148. package/dist/translation/stage.d.ts +0 -2
  149. package/dist/translation/stage.js +0 -44
  150. package/dist/translation/validate.d.ts +0 -2
  151. package/dist/translation/validate.js +0 -50
  152. package/dist/types/data/json.d.ts +0 -6
  153. package/dist/types/data/json.js +0 -1
  154. package/dist/types/data.d.ts +0 -30
  155. package/dist/types/data.js +0 -1
  156. package/dist/types/files.d.ts +0 -1
  157. package/dist/types/files.js +0 -1
  158. package/dist/types/index.d.ts +0 -173
  159. package/dist/types/index.js +0 -1
  160. package/dist/utils/addExplicitAnchorIds.d.ts +0 -24
  161. package/dist/utils/addExplicitAnchorIds.js +0 -260
  162. package/dist/utils/constants.d.ts +0 -2
  163. package/dist/utils/constants.js +0 -2
  164. package/dist/utils/credentials.d.ts +0 -12
  165. package/dist/utils/credentials.js +0 -119
  166. package/dist/utils/flattenJsonFiles.d.ts +0 -2
  167. package/dist/utils/flattenJsonFiles.js +0 -36
  168. package/dist/utils/gt.d.ts +0 -2
  169. package/dist/utils/gt.js +0 -2
  170. package/dist/utils/hash.d.ts +0 -6
  171. package/dist/utils/hash.js +0 -11
  172. package/dist/utils/headers.d.ts +0 -1
  173. package/dist/utils/headers.js +0 -14
  174. package/dist/utils/installPackage.d.ts +0 -3
  175. package/dist/utils/installPackage.js +0 -77
  176. package/dist/utils/localizeStaticImports.d.ts +0 -15
  177. package/dist/utils/localizeStaticImports.js +0 -341
  178. package/dist/utils/localizeStaticUrls.d.ts +0 -19
  179. package/dist/utils/localizeStaticUrls.js +0 -432
  180. package/dist/utils/packageInfo.d.ts +0 -3
  181. package/dist/utils/packageInfo.js +0 -17
  182. package/dist/utils/packageJson.d.ts +0 -6
  183. package/dist/utils/packageJson.js +0 -76
  184. package/dist/utils/packageManager.d.ts +0 -28
  185. package/dist/utils/packageManager.js +0 -269
  186. package/dist/utils/processAnchorIds.d.ts +0 -6
  187. package/dist/utils/processAnchorIds.js +0 -47
  188. package/dist/utils/sanitizeFileContent.d.ts +0 -6
  189. package/dist/utils/sanitizeFileContent.js +0 -29
  190. package/dist/utils/validateMdx.d.ts +0 -10
  191. package/dist/utils/validateMdx.js +0 -25
@@ -1,244 +0,0 @@
1
- import generateModule from '@babel/generator';
2
- // Handle CommonJS/ESM interop
3
- const generate = generateModule.default || generateModule;
4
- import * as t from '@babel/types';
5
- import addGTIdentifierToSyntaxTree from '../../data-_gt/addGTIdentifierToSyntaxTree.js';
6
- import { warnHasUnwrappedExpressionSync, warnVariablePropSync, warnNestedTComponent, } from '../../../console/index.js';
7
- import { isAcceptedPluralForm } from 'generaltranslation/internal';
8
- import { handleChildrenWhitespace } from '../trimJsxStringChildren.js';
9
- import { isStaticExpression } from '../evaluateJsx.js';
10
- import { GT_ATTRIBUTES, mapAttributeName, VARIABLE_COMPONENTS, } from './constants.js';
11
- /**
12
- * Builds a JSX tree from a given node, recursively handling children.
13
- * @param node - The node to build the tree from
14
- * @param unwrappedExpressions - An array to store unwrapped expressions
15
- * @param updates - The updates array
16
- * @param errors - The errors array
17
- * @param file - The file name
18
- * @param insideT - Whether the current node is inside a <T> component
19
- * @returns The built JSX tree
20
- */
21
- export function buildJSXTree(importAliases, node, unwrappedExpressions, updates, errors, warnings, file, insideT) {
22
- if (t.isJSXExpressionContainer(node)) {
23
- // Skip JSX comments
24
- if (t.isJSXEmptyExpression(node.expression)) {
25
- return null;
26
- }
27
- const expr = node.expression;
28
- if (t.isJSXElement(expr)) {
29
- return buildJSXTree(importAliases, expr, unwrappedExpressions, updates, errors, warnings, file, insideT);
30
- }
31
- const staticAnalysis = isStaticExpression(expr);
32
- if (staticAnalysis.isStatic && staticAnalysis.value !== undefined) {
33
- // Preserve the exact whitespace for static string expressions
34
- return {
35
- expression: true,
36
- result: staticAnalysis.value,
37
- };
38
- }
39
- // Keep existing behavior for non-static expressions
40
- const code = generate(node).code;
41
- unwrappedExpressions.push(code); // Keep track of unwrapped expressions for error reporting
42
- return code;
43
- }
44
- else if (t.isJSXText(node)) {
45
- // Updated JSX Text handling
46
- // JSX Text handling following React's rules
47
- const text = node.value;
48
- return text;
49
- }
50
- else if (t.isJSXElement(node)) {
51
- const element = node;
52
- const elementName = element.openingElement.name;
53
- let typeName;
54
- if (t.isJSXIdentifier(elementName)) {
55
- typeName = elementName.name;
56
- }
57
- else if (t.isJSXMemberExpression(elementName)) {
58
- typeName = generate(elementName).code;
59
- }
60
- else {
61
- typeName = null;
62
- }
63
- // Convert from alias to original name
64
- const componentType = importAliases[typeName ?? ''];
65
- if (componentType === 'T' && insideT) {
66
- // Add warning: Nested <T> components are allowed, but they are advised against
67
- warnings.add(warnNestedTComponent(file, `${element.loc?.start?.line}:${element.loc?.start?.column}`));
68
- }
69
- // If this JSXElement is one of the recognized variable components,
70
- const elementIsVariable = VARIABLE_COMPONENTS.includes(componentType);
71
- const props = {};
72
- const elementIsPlural = componentType === 'Plural';
73
- const elementIsBranch = componentType === 'Branch';
74
- element.openingElement.attributes.forEach((attr) => {
75
- if (t.isJSXAttribute(attr)) {
76
- const attrName = attr.name.name;
77
- let attrValue = null;
78
- if (attr.value) {
79
- if (t.isStringLiteral(attr.value)) {
80
- attrValue = attr.value.value;
81
- }
82
- else if (t.isJSXExpressionContainer(attr.value)) {
83
- if ((elementIsPlural && isAcceptedPluralForm(attrName)) ||
84
- (elementIsBranch && attrName !== 'branch')) {
85
- // Make sure that variable strings like {`I have ${count} book`} are invalid!
86
- if (t.isTemplateLiteral(attr.value.expression) &&
87
- !isStaticExpression(attr.value.expression).isStatic) {
88
- unwrappedExpressions.push(generate(attr.value).code);
89
- }
90
- }
91
- attrValue = buildJSXTree(importAliases, attr.value.expression, unwrappedExpressions, updates, errors, warnings, file, true);
92
- }
93
- }
94
- props[attrName] = attrValue;
95
- }
96
- });
97
- if (elementIsVariable) {
98
- parseJSXElement(importAliases, element, updates, errors, warnings, file);
99
- return {
100
- // if componentType is undefined, use typeName
101
- // Basically, if componentType is not a GT component, use typeName such as <div>
102
- type: componentType ?? typeName,
103
- props,
104
- };
105
- }
106
- const children = element.children
107
- .map((child) => buildJSXTree(importAliases, child, unwrappedExpressions, updates, errors, warnings, file, true))
108
- .filter((child) => child !== null && child !== '');
109
- if (children.length === 1) {
110
- props.children = children[0];
111
- }
112
- else if (children.length > 1) {
113
- props.children = children;
114
- }
115
- return {
116
- // if componentType is undefined, use typeName
117
- // Basically, if componentType is not a GT component, use typeName such as <div>
118
- type: componentType ?? typeName,
119
- props,
120
- };
121
- }
122
- // If it's a JSX fragment
123
- else if (t.isJSXFragment(node)) {
124
- const children = node.children
125
- .map((child) => buildJSXTree(importAliases, child, unwrappedExpressions, updates, errors, warnings, file, true))
126
- .filter((child) => child !== null && child !== '');
127
- const props = {};
128
- if (children.length === 1) {
129
- props.children = children[0];
130
- }
131
- else if (children.length > 1) {
132
- props.children = children;
133
- }
134
- return {
135
- type: '',
136
- props,
137
- };
138
- }
139
- // If it's a string literal (standalone)
140
- else if (t.isStringLiteral(node)) {
141
- return node.value;
142
- }
143
- // If it's some other JS expression
144
- else if (t.isIdentifier(node) ||
145
- t.isMemberExpression(node) ||
146
- t.isCallExpression(node) ||
147
- t.isBinaryExpression(node) ||
148
- t.isLogicalExpression(node) ||
149
- t.isConditionalExpression(node)) {
150
- return generate(node).code;
151
- }
152
- else {
153
- return generate(node).code;
154
- }
155
- }
156
- // end buildJSXTree
157
- // Parses a JSX element and adds it to the updates array
158
- export function parseJSXElement(importAliases, node, updates, errors, warnings, file) {
159
- const openingElement = node.openingElement;
160
- const name = openingElement.name;
161
- // Only proceed if it's <T> ...
162
- if (!(name.type === 'JSXIdentifier' && importAliases[name.name] === 'T')) {
163
- return;
164
- }
165
- const componentErrors = [];
166
- const componentWarnings = new Set();
167
- const metadata = {};
168
- // We'll track this flag to know if any unwrapped {variable} is found in children
169
- const unwrappedExpressions = [];
170
- // Gather <T>'s props
171
- openingElement.attributes.forEach((attr) => {
172
- if (!t.isJSXAttribute(attr))
173
- return;
174
- const attrName = attr.name.name;
175
- if (typeof attrName !== 'string')
176
- return;
177
- if (attr.value) {
178
- // If it's a plain string literal like id="hello"
179
- if (t.isStringLiteral(attr.value)) {
180
- metadata[attrName] = attr.value.value;
181
- }
182
- // If it's an expression container like id={"hello"}, id={someVar}, etc.
183
- else if (t.isJSXExpressionContainer(attr.value)) {
184
- const expr = attr.value.expression;
185
- const code = generate(expr).code;
186
- // Only check for static expressions on id and context props
187
- if (GT_ATTRIBUTES.includes(attrName)) {
188
- const staticAnalysis = isStaticExpression(expr);
189
- if (!staticAnalysis.isStatic) {
190
- componentErrors.push(warnVariablePropSync(file, attrName, code, `${expr.loc?.start?.line}:${expr.loc?.start?.column}`));
191
- }
192
- // Use the static value if available
193
- if (staticAnalysis.isStatic && staticAnalysis.value !== undefined) {
194
- metadata[mapAttributeName(attrName)] = staticAnalysis.value;
195
- }
196
- else {
197
- // Only store the code if we couldn't extract a static value
198
- metadata[attrName] = code;
199
- }
200
- }
201
- else {
202
- // For other attributes that aren't id or context
203
- metadata[attrName] = code;
204
- }
205
- }
206
- }
207
- });
208
- // Build the JSX tree for this component
209
- const treeResult = buildJSXTree(importAliases, node, unwrappedExpressions, updates, componentErrors, componentWarnings, file, false);
210
- let jsxTree = undefined;
211
- if (treeResult && typeof treeResult === 'object') {
212
- jsxTree = treeResult.props?.children;
213
- }
214
- else {
215
- jsxTree = treeResult;
216
- }
217
- if (componentWarnings.size > 0) {
218
- componentWarnings.forEach((warning) => warnings.add(warning));
219
- }
220
- if (componentErrors.length > 0) {
221
- errors.push(...componentErrors);
222
- return;
223
- }
224
- // Handle whitespace in children
225
- const whitespaceHandledTree = handleChildrenWhitespace(jsxTree);
226
- // Add GT identifiers to the tree
227
- let minifiedTree = addGTIdentifierToSyntaxTree(whitespaceHandledTree);
228
- minifiedTree =
229
- Array.isArray(minifiedTree) && minifiedTree.length === 1
230
- ? minifiedTree[0]
231
- : minifiedTree;
232
- const id = metadata.id;
233
- // If we found an unwrapped expression, skip
234
- if (unwrappedExpressions.length > 0) {
235
- errors.push(warnHasUnwrappedExpressionSync(file, unwrappedExpressions, id, `${node.loc?.start?.line}:${node.loc?.start?.column}`));
236
- return;
237
- }
238
- // <T> is valid here
239
- updates.push({
240
- dataFormat: 'JSX',
241
- source: minifiedTree,
242
- metadata,
243
- });
244
- }
@@ -1,16 +0,0 @@
1
- import { NodePath } from '@babel/traverse';
2
- import { Updates } from '../../../types/index.js';
3
- /**
4
- * Main entry point for parsing translation strings from useGT() and getGT() calls.
5
- *
6
- * Supports complex patterns including:
7
- * 1. Direct calls: const t = useGT(); t('hello');
8
- * 2. Translation callback prop drilling: const t = useGT(); getInfo(t); where getInfo uses t() internally
9
- * 3. Cross-file function calls: imported functions that receive the translation callback as a parameter
10
- *
11
- * Example flow:
12
- * - const t = useGT();
13
- * - const { home } = getInfo(t); // getInfo is imported from './constants'
14
- * - This will parse constants.ts to find translation calls within getInfo function
15
- */
16
- export declare function parseStrings(importName: string, originalName: string, path: NodePath, updates: Updates, errors: string[], file: string): void;
@@ -1,411 +0,0 @@
1
- import * as t from '@babel/types';
2
- import { isStaticExpression } from '../evaluateJsx.js';
3
- import { GT_ATTRIBUTES_WITH_SUGAR, MSG_TRANSLATION_HOOK, INLINE_TRANSLATION_HOOK, INLINE_TRANSLATION_HOOK_ASYNC, mapAttributeName, INLINE_MESSAGE_HOOK, INLINE_MESSAGE_HOOK_ASYNC, } from './constants.js';
4
- import { warnNonStaticExpressionSync, warnNonStringSync, warnTemplateLiteralSync, warnAsyncUseGT, warnSyncGetGT, } from '../../../console/index.js';
5
- import generateModule from '@babel/generator';
6
- import traverseModule from '@babel/traverse';
7
- // Handle CommonJS/ESM interop
8
- const generate = generateModule.default || generateModule;
9
- const traverse = traverseModule.default || traverseModule;
10
- import fs from 'node:fs';
11
- import path from 'node:path';
12
- import { parse } from '@babel/parser';
13
- import { createMatchPath, loadConfig } from 'tsconfig-paths';
14
- import resolve from 'resolve';
15
- /**
16
- * Processes a single translation function call (e.g., t('hello world', { id: 'greeting' })).
17
- * Extracts the translatable string content and metadata, then adds it to the updates array.
18
- *
19
- * Handles:
20
- * - String literals: t('hello')
21
- * - Template literals without expressions: t(`hello`)
22
- * - Metadata extraction from options object
23
- * - Error reporting for non-static expressions and template literals with expressions
24
- */
25
- function processTranslationCall(tPath, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent) {
26
- if (tPath.parent.type === 'CallExpression' &&
27
- tPath.parent.arguments.length > 0) {
28
- const arg = tPath.parent.arguments[0];
29
- if (arg.type === 'StringLiteral' ||
30
- (t.isTemplateLiteral(arg) && arg.expressions.length === 0)) {
31
- const source = arg.type === 'StringLiteral' ? arg.value : arg.quasis[0].value.raw;
32
- // get metadata and id from options
33
- const options = tPath.parent.arguments[1];
34
- const metadata = {};
35
- if (options && options.type === 'ObjectExpression') {
36
- options.properties.forEach((prop) => {
37
- if (prop.type === 'ObjectProperty' &&
38
- prop.key.type === 'Identifier') {
39
- const attribute = prop.key.name;
40
- if (GT_ATTRIBUTES_WITH_SUGAR.includes(attribute) &&
41
- t.isExpression(prop.value)) {
42
- const result = isStaticExpression(prop.value);
43
- if (!result.isStatic) {
44
- errors.push(warnNonStaticExpressionSync(file, attribute, generate(prop.value).code, `${prop.loc?.start?.line}:${prop.loc?.start?.column}`));
45
- }
46
- if (result.isStatic && result.value && !ignoreAdditionalData) {
47
- // Map $id and $context to id and context
48
- metadata[mapAttributeName(attribute)] = result.value;
49
- }
50
- }
51
- }
52
- });
53
- }
54
- updates.push({
55
- dataFormat: 'ICU',
56
- source,
57
- metadata,
58
- });
59
- }
60
- else if (t.isTemplateLiteral(arg)) {
61
- // warn if template literal
62
- if (!ignoreDynamicContent) {
63
- errors.push(warnTemplateLiteralSync(file, generate(arg).code, `${arg.loc?.start?.line}:${arg.loc?.start?.column}`));
64
- }
65
- }
66
- else {
67
- if (!ignoreDynamicContent) {
68
- errors.push(warnNonStringSync(file, generate(arg).code, `${arg.loc?.start?.line}:${arg.loc?.start?.column}`));
69
- }
70
- }
71
- }
72
- }
73
- /**
74
- * Extracts the parameter name from a function parameter node, handling TypeScript annotations.
75
- */
76
- function extractParameterName(param) {
77
- if (t.isIdentifier(param)) {
78
- return param.name;
79
- }
80
- return null;
81
- }
82
- /**
83
- * Builds a map of imported function names to their import paths from a given program path.
84
- * Handles both named imports and default imports.
85
- *
86
- * Example: import { getInfo } from './constants' -> Map { 'getInfo' => './constants' }
87
- * Example: import utils from './utils' -> Map { 'utils' => './utils' }
88
- */
89
- function buildImportMap(programPath) {
90
- const importMap = new Map();
91
- programPath.traverse({
92
- ImportDeclaration(importPath) {
93
- if (t.isStringLiteral(importPath.node.source)) {
94
- const importSource = importPath.node.source.value;
95
- importPath.node.specifiers.forEach((spec) => {
96
- if (t.isImportSpecifier(spec) &&
97
- t.isIdentifier(spec.imported) &&
98
- t.isIdentifier(spec.local)) {
99
- importMap.set(spec.local.name, importSource);
100
- }
101
- else if (t.isImportDefaultSpecifier(spec) &&
102
- t.isIdentifier(spec.local)) {
103
- importMap.set(spec.local.name, importSource);
104
- }
105
- });
106
- }
107
- },
108
- });
109
- return importMap;
110
- }
111
- /**
112
- * Recursively resolves variable assignments to find all aliases of a translation callback parameter.
113
- * Handles cases like: const t = translate; const a = translate; const b = a; const c = b;
114
- *
115
- * @param scope The scope to search within
116
- * @param variableName The variable name to resolve
117
- * @param visited Set to track already visited variables to prevent infinite loops
118
- * @returns Array of all variable names that reference the original translation callback
119
- */
120
- function resolveVariableAliases(scope, variableName, visited = new Set()) {
121
- if (visited.has(variableName)) {
122
- return []; // Prevent infinite loops
123
- }
124
- visited.add(variableName);
125
- const aliases = [variableName];
126
- const binding = scope.bindings[variableName];
127
- if (binding) {
128
- // Look for variable declarations that assign this variable to another name
129
- // Example: const t = translate; or const a = t;
130
- for (const [otherVarName, otherBinding] of Object.entries(scope.bindings)) {
131
- if (otherVarName === variableName || visited.has(otherVarName))
132
- continue;
133
- const otherBindingTyped = otherBinding;
134
- if (otherBindingTyped.path &&
135
- otherBindingTyped.path.isVariableDeclarator() &&
136
- otherBindingTyped.path.node.init &&
137
- t.isIdentifier(otherBindingTyped.path.node.init) &&
138
- otherBindingTyped.path.node.init.name === variableName) {
139
- // Found an alias: const otherVarName = variableName;
140
- const nestedAliases = resolveVariableAliases(scope, otherVarName, visited);
141
- aliases.push(...nestedAliases);
142
- }
143
- }
144
- }
145
- return aliases;
146
- }
147
- /**
148
- * Handles how translation callbacks are used within code.
149
- * This covers both direct translation calls (t('hello')) and prop drilling
150
- * where the translation callback is passed to other functions (getData(t)).
151
- */
152
- function handleFunctionCall(tPath, updates, errors, file, importMap, ignoreAdditionalData, ignoreDynamicContent) {
153
- if (tPath.parent.type === 'CallExpression' &&
154
- tPath.parent.callee === tPath.node) {
155
- // Direct translation call: t('hello')
156
- processTranslationCall(tPath, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent);
157
- }
158
- else if (tPath.parent.type === 'CallExpression' &&
159
- t.isExpression(tPath.node) &&
160
- tPath.parent.arguments.includes(tPath.node)) {
161
- // Parameter passed to another function: getData(t)
162
- const argIndex = tPath.parent.arguments.indexOf(tPath.node);
163
- const callee = tPath.parent.callee;
164
- if (t.isIdentifier(callee)) {
165
- const calleeBinding = tPath.scope.getBinding(callee.name);
166
- if (calleeBinding && calleeBinding.path.isFunction()) {
167
- const functionPath = calleeBinding.path;
168
- processFunctionIfMatches(callee.name, argIndex, functionPath.node, functionPath, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent);
169
- }
170
- // Handle arrow functions assigned to variables: const getData = (t) => {...}
171
- else if (calleeBinding &&
172
- calleeBinding.path.isVariableDeclarator() &&
173
- calleeBinding.path.node.init &&
174
- (t.isArrowFunctionExpression(calleeBinding.path.node.init) ||
175
- t.isFunctionExpression(calleeBinding.path.node.init))) {
176
- const initPath = calleeBinding.path.get('init');
177
- processFunctionIfMatches(callee.name, argIndex, calleeBinding.path.node.init, initPath, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent);
178
- }
179
- // If not found locally, check if it's an imported function
180
- else if (importMap.has(callee.name)) {
181
- const importPath = importMap.get(callee.name);
182
- const resolvedPath = resolveImportPath(file, importPath);
183
- if (resolvedPath) {
184
- findFunctionInFile(resolvedPath, callee.name, argIndex, updates, errors, ignoreAdditionalData, ignoreDynamicContent);
185
- }
186
- }
187
- }
188
- }
189
- }
190
- /**
191
- * Processes a user-defined function that receives a translation callback as a parameter.
192
- * Validates the function has enough parameters and traces how the translation callback
193
- * is used within that function's body.
194
- */
195
- function processFunctionIfMatches(_functionName, argIndex, functionNode, functionPath, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent) {
196
- if (functionNode.params.length > argIndex) {
197
- const param = functionNode.params[argIndex];
198
- const paramName = extractParameterName(param);
199
- if (paramName) {
200
- findFunctionParameterUsage(functionPath, paramName, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent);
201
- }
202
- }
203
- }
204
- /**
205
- * Finds all usages of a translation callback parameter within a user-defined function's scope.
206
- * Processes both direct translation calls and cases where the translation callback is passed
207
- * to other functions (prop drilling).
208
- *
209
- * Example: In function getInfo(t) { return t('hello'); }, this finds the t('hello') call.
210
- * Example: In function getData(t) { return getFooter(t); }, this finds and traces into getFooter.
211
- */
212
- function findFunctionParameterUsage(functionPath, parameterName, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent) {
213
- // Look for the function body and find all usages of the parameter
214
- if (functionPath.isFunction()) {
215
- const functionScope = functionPath.scope;
216
- // Resolve all aliases of the translation callback parameter
217
- // Example: translate -> [translate, t, a, b] for const t = translate; const a = t; const b = a;
218
- const allParameterNames = resolveVariableAliases(functionScope, parameterName);
219
- // Build import map for this function's scope to handle cross-file calls
220
- const programPath = functionPath.scope.getProgramParent().path;
221
- const importMap = buildImportMap(programPath);
222
- // Process references for all parameter names and their aliases
223
- allParameterNames.forEach((name) => {
224
- const binding = functionScope.bindings[name];
225
- if (binding) {
226
- binding.referencePaths.forEach((refPath) => {
227
- handleFunctionCall(refPath, updates, errors, file, importMap, ignoreAdditionalData, ignoreDynamicContent);
228
- });
229
- }
230
- });
231
- }
232
- }
233
- /**
234
- * Resolves import paths to absolute file paths using battle-tested libraries.
235
- * Handles relative paths, TypeScript paths, and node module resolution.
236
- *
237
- * Examples:
238
- * - './constants' -> '/full/path/to/constants.ts'
239
- * - '@/components/ui/button' -> '/full/path/to/src/components/ui/button.tsx'
240
- * - '@shared/utils' -> '/full/path/to/packages/utils/index.ts'
241
- */
242
- function resolveImportPath(currentFile, importPath) {
243
- const basedir = path.dirname(currentFile);
244
- const extensions = ['.tsx', '.ts', '.jsx', '.js'];
245
- // 1. Try tsconfig-paths resolution first (handles TypeScript path mapping)
246
- const tsConfigResult = loadConfig(basedir);
247
- if (tsConfigResult.resultType === 'success') {
248
- const matchPath = createMatchPath(tsConfigResult.absoluteBaseUrl, tsConfigResult.paths, ['main', 'module', 'browser']);
249
- // First try without any extension
250
- let tsResolved = matchPath(importPath);
251
- if (tsResolved && fs.existsSync(tsResolved)) {
252
- return tsResolved;
253
- }
254
- // Then try with each extension
255
- for (const ext of extensions) {
256
- tsResolved = matchPath(importPath + ext);
257
- if (tsResolved && fs.existsSync(tsResolved)) {
258
- return tsResolved;
259
- }
260
- // Also try the resolved path with extension
261
- tsResolved = matchPath(importPath);
262
- if (tsResolved) {
263
- const resolvedWithExt = tsResolved + ext;
264
- if (fs.existsSync(resolvedWithExt)) {
265
- return resolvedWithExt;
266
- }
267
- }
268
- }
269
- }
270
- // 2. Fallback to Node.js resolution (handles relative paths and node_modules)
271
- try {
272
- return resolve.sync(importPath, { basedir, extensions });
273
- }
274
- catch {
275
- // If resolution fails, try to manually replace .js/.jsx with .ts/.tsx for source files
276
- if (importPath.endsWith('.js')) {
277
- const tsPath = importPath.replace(/\.js$/, '.ts');
278
- try {
279
- return resolve.sync(tsPath, { basedir, extensions });
280
- }
281
- catch {
282
- // Continue to return null
283
- }
284
- }
285
- else if (importPath.endsWith('.jsx')) {
286
- const tsxPath = importPath.replace(/\.jsx$/, '.tsx');
287
- try {
288
- return resolve.sync(tsxPath, { basedir, extensions });
289
- }
290
- catch {
291
- // Continue to return null
292
- }
293
- }
294
- return null;
295
- }
296
- }
297
- /**
298
- * Searches for a specific user-defined function in a file and analyzes how a translation callback
299
- * parameter (at argIndex position) is used within that function.
300
- *
301
- * Handles multiple function declaration patterns:
302
- * - function getInfo(t) { ... }
303
- * - export function getInfo(t) { ... }
304
- * - const getInfo = (t) => { ... }
305
- */
306
- function findFunctionInFile(filePath, functionName, argIndex, updates, errors, ignoreAdditionalData, ignoreDynamicContent) {
307
- try {
308
- const code = fs.readFileSync(filePath, 'utf8');
309
- const ast = parse(code, {
310
- sourceType: 'module',
311
- plugins: ['jsx', 'typescript'],
312
- });
313
- traverse(ast, {
314
- // Handle function declarations: function getInfo(t) { ... }
315
- FunctionDeclaration(path) {
316
- if (path.node.id?.name === functionName) {
317
- processFunctionIfMatches(functionName, argIndex, path.node, path, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent);
318
- }
319
- },
320
- // Handle variable declarations: const getInfo = (t) => { ... }
321
- VariableDeclarator(path) {
322
- if (t.isIdentifier(path.node.id) &&
323
- path.node.id.name === functionName &&
324
- path.node.init &&
325
- (t.isArrowFunctionExpression(path.node.init) ||
326
- t.isFunctionExpression(path.node.init))) {
327
- const initPath = path.get('init');
328
- processFunctionIfMatches(functionName, argIndex, path.node.init, initPath, updates, errors, filePath, ignoreAdditionalData, ignoreDynamicContent);
329
- }
330
- },
331
- });
332
- }
333
- catch {
334
- // Silently skip files that can't be parsed or accessed
335
- }
336
- }
337
- /**
338
- * Main entry point for parsing translation strings from useGT() and getGT() calls.
339
- *
340
- * Supports complex patterns including:
341
- * 1. Direct calls: const t = useGT(); t('hello');
342
- * 2. Translation callback prop drilling: const t = useGT(); getInfo(t); where getInfo uses t() internally
343
- * 3. Cross-file function calls: imported functions that receive the translation callback as a parameter
344
- *
345
- * Example flow:
346
- * - const t = useGT();
347
- * - const { home } = getInfo(t); // getInfo is imported from './constants'
348
- * - This will parse constants.ts to find translation calls within getInfo function
349
- */
350
- export function parseStrings(importName, originalName, path, updates, errors, file) {
351
- // First, collect all imports in this file to track cross-file function calls
352
- const importMap = buildImportMap(path.scope.getProgramParent().path);
353
- const referencePaths = path.scope.bindings[importName]?.referencePaths || [];
354
- for (const refPath of referencePaths) {
355
- // Handle msg() calls directly without variable assignment
356
- if (originalName === MSG_TRANSLATION_HOOK) {
357
- const ignoreAdditionalData = false;
358
- const ignoreDynamicContent = false;
359
- // Check if this is a direct call to msg('string')
360
- if (refPath.parent.type === 'CallExpression' &&
361
- refPath.parent.callee === refPath.node) {
362
- processTranslationCall(refPath, updates, errors, file, ignoreAdditionalData, ignoreDynamicContent);
363
- }
364
- continue;
365
- }
366
- // Handle useGT(), getGT(), useMessages(), and getMessages() calls that need variable assignment
367
- const callExpr = refPath.findParent((p) => p.isCallExpression());
368
- if (callExpr) {
369
- // Get the parent, handling both await and non-await cases
370
- const parentPath = callExpr.parentPath;
371
- const parentFunction = refPath.getFunctionParent();
372
- const asyncScope = parentFunction?.node.async;
373
- if (asyncScope &&
374
- (originalName === INLINE_TRANSLATION_HOOK ||
375
- originalName === INLINE_MESSAGE_HOOK)) {
376
- errors.push(warnAsyncUseGT(file, `${refPath.node.loc?.start?.line}:${refPath.node.loc?.start?.column}`));
377
- return;
378
- }
379
- else if (!asyncScope &&
380
- (originalName === INLINE_TRANSLATION_HOOK_ASYNC ||
381
- originalName === INLINE_MESSAGE_HOOK_ASYNC)) {
382
- errors.push(warnSyncGetGT(file, `${refPath.node.loc?.start?.line}:${refPath.node.loc?.start?.column}`));
383
- return;
384
- }
385
- const isMessageHook = originalName === INLINE_MESSAGE_HOOK ||
386
- originalName === INLINE_MESSAGE_HOOK_ASYNC;
387
- const ignoreAdditionalData = isMessageHook;
388
- const ignoreDynamicContent = isMessageHook;
389
- const effectiveParent = parentPath?.node.type === 'AwaitExpression'
390
- ? parentPath.parentPath
391
- : parentPath;
392
- if (effectiveParent &&
393
- effectiveParent.node.type === 'VariableDeclarator' &&
394
- effectiveParent.node.id.type === 'Identifier') {
395
- const tFuncName = effectiveParent.node.id.name;
396
- // Get the scope from the variable declaration
397
- const variableScope = effectiveParent.scope;
398
- // Resolve all aliases of the translation function
399
- // Example: translate -> [translate, t, a, b] for const t = translate; const a = t; const b = a;
400
- const allTranslationNames = resolveVariableAliases(variableScope, tFuncName);
401
- // Process references for all translation function names and their aliases
402
- allTranslationNames.forEach((name) => {
403
- const tReferencePaths = variableScope.bindings[name]?.referencePaths || [];
404
- for (const tPath of tReferencePaths) {
405
- handleFunctionCall(tPath, updates, errors, file, importMap, ignoreAdditionalData, ignoreDynamicContent);
406
- }
407
- });
408
- }
409
- }
410
- }
411
- }
@@ -1,7 +0,0 @@
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;