gtx-cli 2.5.0-alpha.0 → 2.5.0-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 (93) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/api/collectUserEditDiffs.d.ts +2 -7
  3. package/dist/api/collectUserEditDiffs.js +33 -78
  4. package/dist/api/downloadFileBatch.d.ts +11 -10
  5. package/dist/api/downloadFileBatch.js +120 -127
  6. package/dist/api/saveLocalEdits.js +18 -15
  7. package/dist/cli/base.js +1 -1
  8. package/dist/cli/commands/stage.d.ts +8 -2
  9. package/dist/cli/commands/stage.js +25 -7
  10. package/dist/cli/commands/translate.d.ts +4 -2
  11. package/dist/cli/commands/translate.js +5 -6
  12. package/dist/cli/flags.js +4 -1
  13. package/dist/config/generateSettings.js +10 -0
  14. package/dist/console/colors.d.ts +0 -1
  15. package/dist/console/colors.js +0 -3
  16. package/dist/console/index.d.ts +0 -6
  17. package/dist/console/index.js +2 -13
  18. package/dist/console/logging.d.ts +1 -1
  19. package/dist/console/logging.js +3 -4
  20. package/dist/formats/files/translate.d.ts +2 -2
  21. package/dist/formats/files/translate.js +31 -5
  22. package/dist/fs/config/downloadedVersions.d.ts +10 -3
  23. package/dist/fs/config/downloadedVersions.js +8 -0
  24. package/dist/fs/config/updateVersions.d.ts +2 -1
  25. package/dist/git/branches.d.ts +7 -0
  26. package/dist/git/branches.js +88 -0
  27. package/dist/react/{jsx/utils/jsxParsing → data-_gt}/addGTIdentifierToSyntaxTree.d.ts +1 -2
  28. package/dist/react/{jsx/utils/jsxParsing → data-_gt}/addGTIdentifierToSyntaxTree.js +6 -30
  29. package/dist/react/jsx/evaluateJsx.d.ts +6 -5
  30. package/dist/react/jsx/evaluateJsx.js +4 -32
  31. package/dist/react/jsx/trimJsxStringChildren.d.ts +7 -0
  32. package/dist/react/jsx/trimJsxStringChildren.js +122 -0
  33. package/dist/react/jsx/utils/constants.d.ts +0 -2
  34. package/dist/react/jsx/utils/constants.js +2 -11
  35. package/dist/react/jsx/utils/parseJsx.d.ts +21 -0
  36. package/dist/react/jsx/utils/parseJsx.js +259 -0
  37. package/dist/react/jsx/utils/parseStringFunction.js +141 -4
  38. package/dist/react/parse/createInlineUpdates.js +70 -19
  39. package/dist/types/branch.d.ts +14 -0
  40. package/dist/types/branch.js +1 -0
  41. package/dist/types/data.d.ts +1 -1
  42. package/dist/types/files.d.ts +7 -0
  43. package/dist/types/index.d.ts +7 -0
  44. package/dist/utils/SpinnerManager.d.ts +30 -0
  45. package/dist/utils/SpinnerManager.js +73 -0
  46. package/dist/utils/gitDiff.js +18 -16
  47. package/dist/workflow/BranchStep.d.ts +13 -0
  48. package/dist/workflow/BranchStep.js +131 -0
  49. package/dist/workflow/DownloadStep.d.ts +19 -0
  50. package/dist/workflow/DownloadStep.js +127 -0
  51. package/dist/workflow/EnqueueStep.d.ts +15 -0
  52. package/dist/workflow/EnqueueStep.js +33 -0
  53. package/dist/workflow/PollJobsStep.d.ts +31 -0
  54. package/dist/workflow/PollJobsStep.js +286 -0
  55. package/dist/workflow/SetupStep.d.ts +16 -0
  56. package/dist/workflow/SetupStep.js +72 -0
  57. package/dist/workflow/UploadStep.d.ts +21 -0
  58. package/dist/workflow/UploadStep.js +72 -0
  59. package/dist/workflow/UserEditDiffsStep.d.ts +11 -0
  60. package/dist/workflow/UserEditDiffsStep.js +30 -0
  61. package/dist/workflow/Workflow.d.ts +4 -0
  62. package/dist/workflow/Workflow.js +2 -0
  63. package/dist/workflow/download.d.ts +22 -0
  64. package/dist/workflow/download.js +104 -0
  65. package/dist/workflow/stage.d.ts +14 -0
  66. package/dist/workflow/stage.js +76 -0
  67. package/package.json +3 -4
  68. package/dist/api/checkFileTranslations.d.ts +0 -23
  69. package/dist/api/checkFileTranslations.js +0 -281
  70. package/dist/api/sendFiles.d.ts +0 -17
  71. package/dist/api/sendFiles.js +0 -127
  72. package/dist/api/sendUserEdits.d.ts +0 -19
  73. package/dist/api/sendUserEdits.js +0 -15
  74. package/dist/cli/commands/edits.d.ts +0 -8
  75. package/dist/cli/commands/edits.js +0 -32
  76. package/dist/react/jsx/utils/buildImportMap.d.ts +0 -9
  77. package/dist/react/jsx/utils/buildImportMap.js +0 -30
  78. package/dist/react/jsx/utils/getPathsAndAliases.d.ts +0 -17
  79. package/dist/react/jsx/utils/getPathsAndAliases.js +0 -89
  80. package/dist/react/jsx/utils/jsxParsing/handleChildrenWhitespace.d.ts +0 -6
  81. package/dist/react/jsx/utils/jsxParsing/handleChildrenWhitespace.js +0 -199
  82. package/dist/react/jsx/utils/jsxParsing/multiplication/findMultiplicationNode.d.ts +0 -13
  83. package/dist/react/jsx/utils/jsxParsing/multiplication/findMultiplicationNode.js +0 -42
  84. package/dist/react/jsx/utils/jsxParsing/multiplication/multiplyJsxTree.d.ts +0 -5
  85. package/dist/react/jsx/utils/jsxParsing/multiplication/multiplyJsxTree.js +0 -69
  86. package/dist/react/jsx/utils/jsxParsing/parseJsx.d.ts +0 -60
  87. package/dist/react/jsx/utils/jsxParsing/parseJsx.js +0 -949
  88. package/dist/react/jsx/utils/jsxParsing/parseTProps.d.ts +0 -8
  89. package/dist/react/jsx/utils/jsxParsing/parseTProps.js +0 -47
  90. package/dist/react/jsx/utils/jsxParsing/types.d.ts +0 -48
  91. package/dist/react/jsx/utils/jsxParsing/types.js +0 -34
  92. package/dist/react/jsx/utils/resolveImportPath.d.ts +0 -11
  93. package/dist/react/jsx/utils/resolveImportPath.js +0 -111
@@ -33,10 +33,9 @@ export function isMeaningful(node) {
33
33
  /**
34
34
  * Checks if an expression is static (does not contain any variables which could change at runtime).
35
35
  * @param expr - The expression to check
36
- * @param ignoreStaticFunction - Whether to ignore static functions
37
36
  * @returns An object containing the result of the static check
38
37
  */
39
- export function isStaticExpression(expr, jsxStatic = false) {
38
+ export function isStaticExpression(expr) {
40
39
  // Handle empty expressions
41
40
  if (t.isJSXEmptyExpression(expr)) {
42
41
  return { isStatic: true, value: '' };
@@ -47,10 +46,7 @@ export function isStaticExpression(expr, jsxStatic = false) {
47
46
  }
48
47
  // Handle template literals without expressions
49
48
  if (t.isTemplateLiteral(expr) && expr.expressions.length === 0) {
50
- return {
51
- isStatic: true,
52
- value: jsxStatic ? expr.quasis[0].value.cooked : expr.quasis[0].value.raw,
53
- };
49
+ return { isStatic: true, value: expr.quasis[0].value.raw };
54
50
  }
55
51
  // Binary expressions are not static
56
52
  if (t.isBinaryExpression(expr)) {
@@ -65,37 +61,13 @@ export function isStaticExpression(expr, jsxStatic = false) {
65
61
  if (t.isNumericLiteral(expr)) {
66
62
  return { isStatic: true, value: String(expr.value) };
67
63
  }
68
- // Handle unary expressions by converting them to strings
69
- if (t.isUnaryExpression(expr)) {
70
- let value;
71
- let operator = '';
72
- if (expr.operator === '-') {
73
- operator = expr.operator;
74
- }
75
- if (t.isNumericLiteral(expr.argument)) {
76
- if (expr.argument.value === 0) {
77
- value = '0';
78
- }
79
- else {
80
- value = operator + expr.argument.value.toString();
81
- }
82
- }
83
- else {
84
- // invalid
85
- return { isStatic: false };
86
- }
87
- return { isStatic: true, value };
88
- }
89
64
  // Handle boolean literals by converting them to strings
90
65
  if (t.isBooleanLiteral(expr)) {
91
- return {
92
- isStatic: true,
93
- value: jsxStatic ? expr.value : String(expr.value),
94
- };
66
+ return { isStatic: true, value: String(expr.value) };
95
67
  }
96
68
  // Handle null literal
97
69
  if (t.isNullLiteral(expr)) {
98
- return { isStatic: true, value: jsxStatic ? null : 'null' };
70
+ return { isStatic: true, value: 'null' };
99
71
  }
100
72
  // Not a static expression
101
73
  return { isStatic: false };
@@ -0,0 +1,7 @@
1
+ export declare function trimJsxStringChild(child: string, index: number, childrenTypes: ('expression' | 'text' | 'element')[]): string;
2
+ /**
3
+ * Handles whitespace in children of a JSX element.
4
+ * @param currentTree - The current tree to handle
5
+ * @returns The processed tree with whitespace handled
6
+ */
7
+ export declare const handleChildrenWhitespace: (currentTree: any) => any;
@@ -0,0 +1,122 @@
1
+ import { isAcceptedPluralForm } from 'generaltranslation/internal';
2
+ // JSX whitespace characters (space, tab, newline, carriage return)
3
+ // Does NOT include non-breaking space (U+00A0) which should be preserved
4
+ const isJsxWhitespace = (char) => {
5
+ return char === ' ' || char === '\t' || char === '\n' || char === '\r';
6
+ };
7
+ const trimJsxWhitespace = (str, side = 'both') => {
8
+ let start = 0;
9
+ let end = str.length;
10
+ if (side === 'start' || side === 'both') {
11
+ while (start < end && isJsxWhitespace(str[start])) {
12
+ start++;
13
+ }
14
+ }
15
+ if (side === 'end' || side === 'both') {
16
+ while (end > start && isJsxWhitespace(str[end - 1])) {
17
+ end--;
18
+ }
19
+ }
20
+ return str.slice(start, end);
21
+ };
22
+ const hasNonJsxWhitespace = (str) => {
23
+ for (const char of str) {
24
+ if (!isJsxWhitespace(char))
25
+ return true;
26
+ }
27
+ return false;
28
+ };
29
+ export function trimJsxStringChild(child, index, childrenTypes) {
30
+ // Normalize line endings to \n for consistency across platforms
31
+ let result = child.replace(/\r\n|\r/g, '\n');
32
+ // Collapse multiple spaces/tabs into a single space (but not nbsp)
33
+ result = result.replace(/[\t ]+/g, ' ');
34
+ let newResult = '';
35
+ let newline = false;
36
+ for (const char of result) {
37
+ if (char === '\n') {
38
+ if (hasNonJsxWhitespace(newResult))
39
+ newResult += ' ';
40
+ else
41
+ newResult = '';
42
+ newline = true;
43
+ continue;
44
+ }
45
+ if (!newline) {
46
+ newResult += char;
47
+ continue;
48
+ }
49
+ if (isJsxWhitespace(char))
50
+ continue;
51
+ newResult += char;
52
+ newline = false;
53
+ }
54
+ if (newline)
55
+ newResult = trimJsxWhitespace(newResult, 'end');
56
+ result = newResult;
57
+ // Collapse multiple spaces/tabs into a single space (but not nbsp)
58
+ result = result.replace(/[\t ]+/g, ' ');
59
+ return result;
60
+ }
61
+ /**
62
+ * Handles whitespace in children of a JSX element.
63
+ * @param currentTree - The current tree to handle
64
+ * @returns The processed tree with whitespace handled
65
+ */
66
+ export const handleChildrenWhitespace = (currentTree) => {
67
+ if (Array.isArray(currentTree)) {
68
+ const childrenTypes = currentTree.map((child) => {
69
+ if (typeof child === 'string')
70
+ return 'text';
71
+ if (typeof child === 'object' && 'expression' in child)
72
+ return 'expression';
73
+ return 'element';
74
+ });
75
+ const newChildren = [];
76
+ currentTree.forEach((child, index) => {
77
+ if (childrenTypes[index] === 'text') {
78
+ const string = trimJsxStringChild(child, index, childrenTypes);
79
+ if (string)
80
+ newChildren.push(string);
81
+ }
82
+ else if (childrenTypes[index] === 'expression') {
83
+ newChildren.push(child.result);
84
+ }
85
+ else {
86
+ newChildren.push(handleChildrenWhitespace(child));
87
+ }
88
+ });
89
+ return newChildren.length === 1 ? newChildren[0] : newChildren;
90
+ }
91
+ else if (currentTree?.props) {
92
+ // Process all props recursively
93
+ const elementIsPlural = currentTree.type === 'Plural';
94
+ const elementIsBranch = currentTree.type === 'Branch';
95
+ const processedProps = Object.fromEntries(Object.entries(currentTree.props).map(([key, value]) => {
96
+ let shouldProcess = false;
97
+ if (key === 'children')
98
+ shouldProcess = true;
99
+ if (elementIsPlural && isAcceptedPluralForm(key))
100
+ shouldProcess = true;
101
+ if (elementIsBranch && key !== 'branch')
102
+ shouldProcess = true;
103
+ // Add your validation logic here
104
+ if (shouldProcess) {
105
+ return [key, handleChildrenWhitespace(value)];
106
+ }
107
+ return [key, value];
108
+ }));
109
+ return {
110
+ ...currentTree,
111
+ props: processedProps,
112
+ };
113
+ }
114
+ else if (typeof currentTree === 'object' &&
115
+ 'expression' in currentTree === true) {
116
+ return currentTree.result;
117
+ }
118
+ else if (typeof currentTree === 'string') {
119
+ return trimJsxStringChild(currentTree, 0, ['text']);
120
+ }
121
+ return currentTree;
122
+ };
@@ -3,8 +3,6 @@ export declare const INLINE_TRANSLATION_HOOK = "useGT";
3
3
  export declare const INLINE_TRANSLATION_HOOK_ASYNC = "getGT";
4
4
  export declare const INLINE_MESSAGE_HOOK = "useMessages";
5
5
  export declare const INLINE_MESSAGE_HOOK_ASYNC = "getMessages";
6
- export declare const TRANSLATION_COMPONENT = "T";
7
- export declare const STATIC_COMPONENT = "Static";
8
6
  export declare const GT_TRANSLATION_FUNCS: string[];
9
7
  export declare const VARIABLE_COMPONENTS: string[];
10
8
  export declare const GT_ATTRIBUTES_WITH_SUGAR: string[];
@@ -3,8 +3,6 @@ export const INLINE_TRANSLATION_HOOK = 'useGT';
3
3
  export const INLINE_TRANSLATION_HOOK_ASYNC = 'getGT';
4
4
  export const INLINE_MESSAGE_HOOK = 'useMessages';
5
5
  export const INLINE_MESSAGE_HOOK_ASYNC = 'getMessages';
6
- export const TRANSLATION_COMPONENT = 'T';
7
- export const STATIC_COMPONENT = 'Static';
8
6
  // GT translation functions
9
7
  export const GT_TRANSLATION_FUNCS = [
10
8
  INLINE_TRANSLATION_HOOK,
@@ -12,8 +10,7 @@ export const GT_TRANSLATION_FUNCS = [
12
10
  INLINE_MESSAGE_HOOK,
13
11
  INLINE_MESSAGE_HOOK_ASYNC,
14
12
  MSG_TRANSLATION_HOOK,
15
- TRANSLATION_COMPONENT,
16
- STATIC_COMPONENT,
13
+ 'T',
17
14
  'Var',
18
15
  'DateTime',
19
16
  'Currency',
@@ -22,13 +19,7 @@ export const GT_TRANSLATION_FUNCS = [
22
19
  'Plural',
23
20
  ];
24
21
  // Valid variable components
25
- export const VARIABLE_COMPONENTS = [
26
- 'Var',
27
- 'DateTime',
28
- 'Currency',
29
- 'Num',
30
- STATIC_COMPONENT,
31
- ];
22
+ export const VARIABLE_COMPONENTS = ['Var', 'DateTime', 'Currency', 'Num'];
32
23
  export const GT_ATTRIBUTES_WITH_SUGAR = ['$id', '$context'];
33
24
  export const GT_ATTRIBUTES = ['id', 'context', ...GT_ATTRIBUTES_WITH_SUGAR];
34
25
  export function mapAttributeName(attrName) {
@@ -0,0 +1,21 @@
1
+ import { Updates } from '../../../types/index.js';
2
+ import * as t from '@babel/types';
3
+ /**
4
+ * Builds a JSX tree from a given node, recursively handling children.
5
+ * @param node - The node to build the tree from
6
+ * @param unwrappedExpressions - An array to store unwrapped expressions
7
+ * @param updates - The updates array
8
+ * @param errors - The errors array
9
+ * @param file - The file name
10
+ * @param insideT - Whether the current node is inside a <T> component
11
+ * @returns The built JSX tree
12
+ */
13
+ export declare function buildJSXTree(importAliases: Record<string, string>, node: any, unwrappedExpressions: string[], updates: Updates, errors: string[], warnings: Set<string>, file: string, insideT: boolean): {
14
+ expression?: boolean;
15
+ result?: string;
16
+ type?: string;
17
+ props?: {
18
+ children?: any;
19
+ };
20
+ } | string | null;
21
+ export declare function parseJSXElement(importAliases: Record<string, string>, node: t.JSXElement, updates: Updates, errors: string[], warnings: Set<string>, file: string): void;
@@ -0,0 +1,259 @@
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
+ import { HTML_CONTENT_PROPS } from 'generaltranslation/types';
12
+ /**
13
+ * Builds a JSX tree from a given node, recursively handling children.
14
+ * @param node - The node to build the tree from
15
+ * @param unwrappedExpressions - An array to store unwrapped expressions
16
+ * @param updates - The updates array
17
+ * @param errors - The errors array
18
+ * @param file - The file name
19
+ * @param insideT - Whether the current node is inside a <T> component
20
+ * @returns The built JSX tree
21
+ */
22
+ export function buildJSXTree(importAliases, node, unwrappedExpressions, updates, errors, warnings, file, insideT) {
23
+ if (t.isJSXExpressionContainer(node)) {
24
+ // Skip JSX comments
25
+ if (t.isJSXEmptyExpression(node.expression)) {
26
+ return null;
27
+ }
28
+ const expr = node.expression;
29
+ if (t.isJSXElement(expr)) {
30
+ return buildJSXTree(importAliases, expr, unwrappedExpressions, updates, errors, warnings, file, insideT);
31
+ }
32
+ const staticAnalysis = isStaticExpression(expr);
33
+ if (staticAnalysis.isStatic && staticAnalysis.value !== undefined) {
34
+ // Preserve the exact whitespace for static string expressions
35
+ return {
36
+ expression: true,
37
+ result: staticAnalysis.value,
38
+ };
39
+ }
40
+ // Keep existing behavior for non-static expressions
41
+ const code = generate(node).code;
42
+ unwrappedExpressions.push(code); // Keep track of unwrapped expressions for error reporting
43
+ return code;
44
+ }
45
+ else if (t.isJSXText(node)) {
46
+ // Updated JSX Text handling
47
+ // JSX Text handling following React's rules
48
+ const text = node.value;
49
+ return text;
50
+ }
51
+ else if (t.isJSXElement(node)) {
52
+ const element = node;
53
+ const elementName = element.openingElement.name;
54
+ let typeName;
55
+ if (t.isJSXIdentifier(elementName)) {
56
+ typeName = elementName.name;
57
+ }
58
+ else if (t.isJSXMemberExpression(elementName)) {
59
+ typeName = generate(elementName).code;
60
+ }
61
+ else {
62
+ typeName = null;
63
+ }
64
+ // Convert from alias to original name
65
+ const componentType = importAliases[typeName ?? ''];
66
+ if (componentType === 'T' && insideT) {
67
+ // Add warning: Nested <T> components are allowed, but they are advised against
68
+ warnings.add(warnNestedTComponent(file, `${element.loc?.start?.line}:${element.loc?.start?.column}`));
69
+ }
70
+ // If this JSXElement is one of the recognized variable components,
71
+ const elementIsVariable = VARIABLE_COMPONENTS.includes(componentType);
72
+ const props = {};
73
+ const elementIsPlural = componentType === 'Plural';
74
+ const elementIsBranch = componentType === 'Branch';
75
+ element.openingElement.attributes.forEach((attr) => {
76
+ if (t.isJSXAttribute(attr)) {
77
+ const attrName = attr.name.name;
78
+ let attrValue = null;
79
+ if (attr.value) {
80
+ if (t.isStringLiteral(attr.value)) {
81
+ attrValue = attr.value.value;
82
+ }
83
+ else if (t.isJSXExpressionContainer(attr.value)) {
84
+ // Check if this is an HTML content prop (title, placeholder, alt, etc.)
85
+ const isHtmlContentProp = Object.values(HTML_CONTENT_PROPS).includes(attrName);
86
+ if (isHtmlContentProp) {
87
+ // For HTML content props, only accept static string expressions
88
+ const staticAnalysis = isStaticExpression(attr.value.expression);
89
+ if (staticAnalysis.isStatic &&
90
+ staticAnalysis.value !== undefined) {
91
+ attrValue = staticAnalysis.value;
92
+ }
93
+ // Otherwise attrValue stays null and won't be included
94
+ }
95
+ else {
96
+ // For non-HTML-content props, validate plural/branch then build tree
97
+ if ((elementIsPlural && isAcceptedPluralForm(attrName)) ||
98
+ (elementIsBranch && attrName !== 'branch')) {
99
+ // Make sure that variable strings like {`I have ${count} book`} are invalid!
100
+ if (t.isTemplateLiteral(attr.value.expression) &&
101
+ !isStaticExpression(attr.value.expression).isStatic) {
102
+ unwrappedExpressions.push(generate(attr.value).code);
103
+ }
104
+ }
105
+ attrValue = buildJSXTree(importAliases, attr.value.expression, unwrappedExpressions, updates, errors, warnings, file, true);
106
+ }
107
+ }
108
+ }
109
+ props[attrName] = attrValue;
110
+ }
111
+ });
112
+ if (elementIsVariable) {
113
+ parseJSXElement(importAliases, element, updates, errors, warnings, file);
114
+ return {
115
+ // if componentType is undefined, use typeName
116
+ // Basically, if componentType is not a GT component, use typeName such as <div>
117
+ type: componentType ?? typeName,
118
+ props,
119
+ };
120
+ }
121
+ const children = element.children
122
+ .map((child) => buildJSXTree(importAliases, child, unwrappedExpressions, updates, errors, warnings, file, true))
123
+ .filter((child) => child !== null && child !== '');
124
+ if (children.length === 1) {
125
+ props.children = children[0];
126
+ }
127
+ else if (children.length > 1) {
128
+ props.children = children;
129
+ }
130
+ return {
131
+ // if componentType is undefined, use typeName
132
+ // Basically, if componentType is not a GT component, use typeName such as <div>
133
+ type: componentType ?? typeName,
134
+ props,
135
+ };
136
+ }
137
+ // If it's a JSX fragment
138
+ else if (t.isJSXFragment(node)) {
139
+ const children = node.children
140
+ .map((child) => buildJSXTree(importAliases, child, unwrappedExpressions, updates, errors, warnings, file, true))
141
+ .filter((child) => child !== null && child !== '');
142
+ const props = {};
143
+ if (children.length === 1) {
144
+ props.children = children[0];
145
+ }
146
+ else if (children.length > 1) {
147
+ props.children = children;
148
+ }
149
+ return {
150
+ type: '',
151
+ props,
152
+ };
153
+ }
154
+ // If it's a string literal (standalone)
155
+ else if (t.isStringLiteral(node)) {
156
+ return node.value;
157
+ }
158
+ // If it's some other JS expression
159
+ else if (t.isIdentifier(node) ||
160
+ t.isMemberExpression(node) ||
161
+ t.isCallExpression(node) ||
162
+ t.isBinaryExpression(node) ||
163
+ t.isLogicalExpression(node) ||
164
+ t.isConditionalExpression(node)) {
165
+ return generate(node).code;
166
+ }
167
+ else {
168
+ return generate(node).code;
169
+ }
170
+ }
171
+ // end buildJSXTree
172
+ // Parses a JSX element and adds it to the updates array
173
+ export function parseJSXElement(importAliases, node, updates, errors, warnings, file) {
174
+ const openingElement = node.openingElement;
175
+ const name = openingElement.name;
176
+ // Only proceed if it's <T> ...
177
+ if (!(name.type === 'JSXIdentifier' && importAliases[name.name] === 'T')) {
178
+ return;
179
+ }
180
+ const componentErrors = [];
181
+ const componentWarnings = new Set();
182
+ const metadata = {};
183
+ // We'll track this flag to know if any unwrapped {variable} is found in children
184
+ const unwrappedExpressions = [];
185
+ // Gather <T>'s props
186
+ openingElement.attributes.forEach((attr) => {
187
+ if (!t.isJSXAttribute(attr))
188
+ return;
189
+ const attrName = attr.name.name;
190
+ if (typeof attrName !== 'string')
191
+ return;
192
+ if (attr.value) {
193
+ // If it's a plain string literal like id="hello"
194
+ if (t.isStringLiteral(attr.value)) {
195
+ metadata[attrName] = attr.value.value;
196
+ }
197
+ // If it's an expression container like id={"hello"}, id={someVar}, etc.
198
+ else if (t.isJSXExpressionContainer(attr.value)) {
199
+ const expr = attr.value.expression;
200
+ const code = generate(expr).code;
201
+ // Only check for static expressions on id and context props
202
+ if (GT_ATTRIBUTES.includes(attrName)) {
203
+ const staticAnalysis = isStaticExpression(expr);
204
+ if (!staticAnalysis.isStatic) {
205
+ componentErrors.push(warnVariablePropSync(file, attrName, code, `${expr.loc?.start?.line}:${expr.loc?.start?.column}`));
206
+ }
207
+ // Use the static value if available
208
+ if (staticAnalysis.isStatic && staticAnalysis.value !== undefined) {
209
+ metadata[mapAttributeName(attrName)] = staticAnalysis.value;
210
+ }
211
+ else {
212
+ // Only store the code if we couldn't extract a static value
213
+ metadata[attrName] = code;
214
+ }
215
+ }
216
+ else {
217
+ // For other attributes that aren't id or context
218
+ metadata[attrName] = code;
219
+ }
220
+ }
221
+ }
222
+ });
223
+ // Build the JSX tree for this component
224
+ const treeResult = buildJSXTree(importAliases, node, unwrappedExpressions, updates, componentErrors, componentWarnings, file, false);
225
+ let jsxTree = undefined;
226
+ if (treeResult && typeof treeResult === 'object') {
227
+ jsxTree = treeResult.props?.children;
228
+ }
229
+ else {
230
+ jsxTree = treeResult;
231
+ }
232
+ if (componentWarnings.size > 0) {
233
+ componentWarnings.forEach((warning) => warnings.add(warning));
234
+ }
235
+ if (componentErrors.length > 0) {
236
+ errors.push(...componentErrors);
237
+ return;
238
+ }
239
+ // Handle whitespace in children
240
+ const whitespaceHandledTree = handleChildrenWhitespace(jsxTree);
241
+ // Add GT identifiers to the tree
242
+ let minifiedTree = addGTIdentifierToSyntaxTree(whitespaceHandledTree);
243
+ minifiedTree =
244
+ Array.isArray(minifiedTree) && minifiedTree.length === 1
245
+ ? minifiedTree[0]
246
+ : minifiedTree;
247
+ const id = metadata.id;
248
+ // If we found an unwrapped expression, skip
249
+ if (unwrappedExpressions.length > 0) {
250
+ errors.push(warnHasUnwrappedExpressionSync(file, unwrappedExpressions, id, `${node.loc?.start?.line}:${node.loc?.start?.column}`));
251
+ return;
252
+ }
253
+ // <T> is valid here
254
+ updates.push({
255
+ dataFormat: 'JSX',
256
+ source: minifiedTree,
257
+ metadata,
258
+ });
259
+ }