@arcmantle/lit-jsx 1.0.0

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 (119) hide show
  1. package/README.md +768 -0
  2. package/dist/compiler/attribute-processor.d.ts +128 -0
  3. package/dist/compiler/attribute-processor.d.ts.map +1 -0
  4. package/dist/compiler/attribute-processor.js +380 -0
  5. package/dist/compiler/attribute-processor.js.map +1 -0
  6. package/dist/compiler/babel-preset.d.ts +6 -0
  7. package/dist/compiler/babel-preset.d.ts.map +1 -0
  8. package/dist/compiler/babel-preset.js +27 -0
  9. package/dist/compiler/babel-preset.js.map +1 -0
  10. package/dist/compiler/builder.d.ts +22 -0
  11. package/dist/compiler/builder.d.ts.map +1 -0
  12. package/dist/compiler/builder.js +62 -0
  13. package/dist/compiler/builder.js.map +1 -0
  14. package/dist/compiler/compiler-utils.d.ts +81 -0
  15. package/dist/compiler/compiler-utils.d.ts.map +1 -0
  16. package/dist/compiler/compiler-utils.js +410 -0
  17. package/dist/compiler/compiler-utils.js.map +1 -0
  18. package/dist/compiler/config.d.ts +77 -0
  19. package/dist/compiler/config.d.ts.map +1 -0
  20. package/dist/compiler/config.js +78 -0
  21. package/dist/compiler/config.js.map +1 -0
  22. package/dist/compiler/postprocess.d.ts +5 -0
  23. package/dist/compiler/postprocess.d.ts.map +1 -0
  24. package/dist/compiler/postprocess.js +3 -0
  25. package/dist/compiler/postprocess.js.map +1 -0
  26. package/dist/compiler/preprocess.d.ts +5 -0
  27. package/dist/compiler/preprocess.d.ts.map +1 -0
  28. package/dist/compiler/preprocess.js +28 -0
  29. package/dist/compiler/preprocess.js.map +1 -0
  30. package/dist/compiler/transform-jsx.d.ts +5 -0
  31. package/dist/compiler/transform-jsx.d.ts.map +1 -0
  32. package/dist/compiler/transform-jsx.js +25 -0
  33. package/dist/compiler/transform-jsx.js.map +1 -0
  34. package/dist/compiler/transpiler.d.ts +48 -0
  35. package/dist/compiler/transpiler.d.ts.map +1 -0
  36. package/dist/compiler/transpiler.js +463 -0
  37. package/dist/compiler/transpiler.js.map +1 -0
  38. package/dist/compiler/vite-plugin.d.ts +38 -0
  39. package/dist/compiler/vite-plugin.d.ts.map +1 -0
  40. package/dist/compiler/vite-plugin.js +96 -0
  41. package/dist/compiler/vite-plugin.js.map +1 -0
  42. package/dist/runtime/choose-component.d.ts +39 -0
  43. package/dist/runtime/choose-component.d.ts.map +1 -0
  44. package/dist/runtime/choose-component.js +40 -0
  45. package/dist/runtime/choose-component.js.map +1 -0
  46. package/dist/runtime/compiler-ctors.d.ts +21 -0
  47. package/dist/runtime/compiler-ctors.d.ts.map +1 -0
  48. package/dist/runtime/compiler-ctors.js +21 -0
  49. package/dist/runtime/compiler-ctors.js.map +1 -0
  50. package/dist/runtime/for-component.d.ts +25 -0
  51. package/dist/runtime/for-component.d.ts.map +1 -0
  52. package/dist/runtime/for-component.js +35 -0
  53. package/dist/runtime/for-component.js.map +1 -0
  54. package/dist/runtime/literal-map.d.ts +22 -0
  55. package/dist/runtime/literal-map.d.ts.map +1 -0
  56. package/dist/runtime/literal-map.js +29 -0
  57. package/dist/runtime/literal-map.js.map +1 -0
  58. package/dist/runtime/rest-directive.d.ts +28 -0
  59. package/dist/runtime/rest-directive.d.ts.map +1 -0
  60. package/dist/runtime/rest-directive.js +49 -0
  61. package/dist/runtime/rest-directive.js.map +1 -0
  62. package/dist/runtime/show-component.d.ts +33 -0
  63. package/dist/runtime/show-component.d.ts.map +1 -0
  64. package/dist/runtime/show-component.js +30 -0
  65. package/dist/runtime/show-component.js.map +1 -0
  66. package/dist/runtime/tagged-template.d.ts +12 -0
  67. package/dist/runtime/tagged-template.d.ts.map +1 -0
  68. package/dist/runtime/tagged-template.js +12 -0
  69. package/dist/runtime/tagged-template.js.map +1 -0
  70. package/dist/runtime/type-helpers.d.ts +80 -0
  71. package/dist/runtime/type-helpers.d.ts.map +1 -0
  72. package/dist/runtime/type-helpers.js +85 -0
  73. package/dist/runtime/type-helpers.js.map +1 -0
  74. package/dist/shared/jsx-types.d.ts +2139 -0
  75. package/dist/shared/jsx-types.d.ts.map +1 -0
  76. package/dist/shared/jsx-types.js +2 -0
  77. package/dist/shared/jsx-types.js.map +1 -0
  78. package/dist/shared/jsx-utils.d.ts +30 -0
  79. package/dist/shared/jsx-utils.d.ts.map +1 -0
  80. package/dist/shared/jsx-utils.js +58 -0
  81. package/dist/shared/jsx-utils.js.map +1 -0
  82. package/dist/shared/mathml-tags.d.ts +12 -0
  83. package/dist/shared/mathml-tags.d.ts.map +1 -0
  84. package/dist/shared/mathml-tags.js +215 -0
  85. package/dist/shared/mathml-tags.js.map +1 -0
  86. package/dist/shared/svg-tags.d.ts +13 -0
  87. package/dist/shared/svg-tags.d.ts.map +1 -0
  88. package/dist/shared/svg-tags.js +95 -0
  89. package/dist/shared/svg-tags.js.map +1 -0
  90. package/dist/utils.d.ts +30 -0
  91. package/dist/utils.d.ts.map +1 -0
  92. package/dist/utils.js +30 -0
  93. package/dist/utils.js.map +1 -0
  94. package/package.json +52 -0
  95. package/src/compiler/attribute-processor.ts +579 -0
  96. package/src/compiler/babel-preset.ts +34 -0
  97. package/src/compiler/builder.ts +86 -0
  98. package/src/compiler/compiler-utils.ts +789 -0
  99. package/src/compiler/config.ts +77 -0
  100. package/src/compiler/postprocess.ts +7 -0
  101. package/src/compiler/preprocess.ts +40 -0
  102. package/src/compiler/transform-jsx.ts +36 -0
  103. package/src/compiler/transpiler.ts +644 -0
  104. package/src/compiler/vite-plugin.ts +114 -0
  105. package/src/external.d.ts +9 -0
  106. package/src/runtime/choose-component.ts +53 -0
  107. package/src/runtime/compiler-ctors.ts +28 -0
  108. package/src/runtime/for-component.ts +54 -0
  109. package/src/runtime/literal-map.ts +37 -0
  110. package/src/runtime/rest-directive.ts +66 -0
  111. package/src/runtime/show-component.ts +48 -0
  112. package/src/runtime/tagged-template.ts +11 -0
  113. package/src/runtime/type-helpers.ts +91 -0
  114. package/src/shared/jsx-types.ts +2556 -0
  115. package/src/shared/jsx-utils.ts +85 -0
  116. package/src/shared/mathml-tags.ts +235 -0
  117. package/src/shared/svg-tags.ts +103 -0
  118. package/src/tsconfig.json +4 -0
  119. package/src/utils.ts +30 -0
@@ -0,0 +1,789 @@
1
+ import type { Binding, NodePath } from '@babel/traverse';
2
+ import * as t from '@babel/types';
3
+
4
+ import { isMathmlTag } from '../shared/mathml-tags.js';
5
+ import { isSvgTag } from '../shared/svg-tags.js';
6
+ import type { ProcessorContext } from './attribute-processor.js';
7
+ import { COMPONENT_POSTFIX, ERROR_MESSAGES, SOURCES, VARIABLES } from './config.js';
8
+
9
+
10
+ export type Values<T> = T[keyof T];
11
+
12
+
13
+ export const isComponent = (tagName: string): boolean => {
14
+ return (tagName[0] && tagName[0].toLowerCase() !== tagName[0])
15
+ || tagName.includes('.')
16
+ || /[^a-zA-Z]/.test(tagName[0] ?? '');
17
+ };
18
+
19
+ export const getProgramFromPath = (path: NodePath): t.Program => {
20
+ const program = path.findParent(p => t.isProgram(p.node))?.node as t.Program | undefined;
21
+ if (!program)
22
+ throw new Error(ERROR_MESSAGES.NO_PROGRAM_FOUND);
23
+
24
+ return program;
25
+ };
26
+
27
+
28
+ export class Ensure {
29
+
30
+ static findProgram(path: NodePath): NodePath<t.Program> {
31
+ const programPath = path.findParent(p => t.isProgram(p.node)) as NodePath<t.Program>;
32
+ if (!programPath)
33
+ throw new Error('Could not find program path');
34
+
35
+ return programPath;
36
+ }
37
+
38
+ static import(
39
+ importSource: (value: string) => boolean,
40
+ importName: (value: string) => boolean,
41
+ createImport: () => t.ImportDeclaration,
42
+ program: t.Program,
43
+ path: NodePath,
44
+ ): void {
45
+ // Check if the import already exists
46
+ const hasImport = program.body.some(node => {
47
+ if (!t.isImportDeclaration(node))
48
+ return false;
49
+
50
+ // Check if the import source matches
51
+ const isCorrectImport = importSource(node.source.value);
52
+ if (!isCorrectImport)
53
+ return false;
54
+
55
+ // Check if the import name matches
56
+ return node.specifiers.some(spec => {
57
+ return t.isImportSpecifier(spec)
58
+ ? t.isIdentifier(spec.imported)
59
+ ? importName(spec.imported.name)
60
+ : importName(spec.imported.value)
61
+ : false;
62
+ });
63
+ });
64
+
65
+ // If not found, add the import
66
+ if (!hasImport) {
67
+ const importDeclaration = createImport();
68
+ const programPath = path.findParent(p => t.isProgram(p.node)) as NodePath<t.Program>;
69
+
70
+ // Insert at the top of the file
71
+ const [ insertedPath ] = programPath.unshiftContainer('body', importDeclaration);
72
+ programPath.scope.registerDeclaration(insertedPath);
73
+ }
74
+ }
75
+
76
+ static getNodePath<T extends t.Node>(
77
+ node: T,
78
+ path: NodePath,
79
+ ): NodePath<T> | undefined {
80
+ // First, traverse upwards to find the root (Program) path
81
+ let rootPath = path;
82
+ while (rootPath.parentPath)
83
+ rootPath = rootPath.parentPath;
84
+
85
+ // Now traverse down from the root to find the target node
86
+ let foundPath: NodePath<T> | undefined;
87
+
88
+ rootPath.traverse({
89
+ enter(path) {
90
+ if (path.node === node) {
91
+ foundPath = path as NodePath<T>;
92
+ path.stop();
93
+ }
94
+ },
95
+ });
96
+
97
+ return foundPath;
98
+ }
99
+
100
+ static getClosestStatementPath(path: NodePath): NodePath<t.Statement> {
101
+ let statementPath: NodePath<t.Node> | null = path;
102
+
103
+ while (statementPath && !statementPath.isStatement())
104
+ statementPath = statementPath.parentPath;
105
+
106
+ if (!statementPath)
107
+ throw new Error(`Could not find statement path for node insertion`);
108
+
109
+ return statementPath;
110
+ }
111
+
112
+ static getClosestBinding(path: NodePath, name: string): Binding | undefined {
113
+ let currentScope = path.scope;
114
+ while (currentScope) {
115
+ const existingBinding = currentScope.getBinding(name);
116
+ if (existingBinding)
117
+ return existingBinding;
118
+
119
+ currentScope = currentScope.parent;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Finds the closest arrow function expression with an expression body
125
+ * starting from the given path.
126
+ * Returns the NodePath of the arrow function expression if found, otherwise undefined.
127
+ */
128
+ static getArrowExpressionPath(
129
+ path: NodePath,
130
+ ): NodePath<t.ArrowFunctionExpression> | undefined {
131
+ // Check if we're inside an arrow function with an expression body
132
+ let currentPath: NodePath | null = path;
133
+ let arrowFunctionPath: NodePath<t.ArrowFunctionExpression> | undefined;
134
+
135
+ while (currentPath && currentPath.parentPath) {
136
+ if (t.isArrowFunctionExpression(currentPath.node) && t.isExpression(currentPath.node.body)) {
137
+ arrowFunctionPath = currentPath as NodePath<t.ArrowFunctionExpression>;
138
+ break;
139
+ }
140
+
141
+ currentPath = currentPath.parentPath;
142
+ }
143
+
144
+ return arrowFunctionPath;
145
+ }
146
+
147
+ /**
148
+ * Hoists the expression to a variable declaration in the closest scope.
149
+ *
150
+ * If the path is inside an arrow function with an expression body, it converts
151
+ * the arrow function body to a block statement and inserts the variable declaration
152
+ * before the return statement.
153
+ *
154
+ * If the path is not inside such an arrow function, it inserts the variable declaration
155
+ * before the closest statement and replaces the target node with the new variable identifier.
156
+ */
157
+ static replaceAndHoistAsVariable(
158
+ path: NodePath,
159
+ variableName: string,
160
+ expression: t.Expression,
161
+ expandArrow = true,
162
+ ): t.Identifier {
163
+ if (this.getClosestBinding(path, variableName))
164
+ return t.identifier(variableName);
165
+
166
+ const nodeToReplace = path.node;
167
+
168
+ // Create the new variable declaration
169
+ const identifier = t.identifier(variableName);
170
+ const declarator = t.variableDeclarator(identifier, expression);
171
+ const variableDeclaration = t.variableDeclaration('const', [ declarator ]);
172
+
173
+ // Check if we're inside an arrow function with an expression body
174
+ // If expandArrow is false, we skip this check and always insert
175
+ // the variable declaration as a regular statement.
176
+ const arrowFunctionPath = expandArrow ? this.getArrowExpressionPath(path) : undefined;
177
+
178
+ if (arrowFunctionPath) {
179
+ // Convert arrow function expression body to block statement
180
+ const returnStatement = t.returnStatement(identifier);
181
+ const blockStatement = t.blockStatement([ variableDeclaration, returnStatement ]);
182
+
183
+ // Replace the arrow function body
184
+ arrowFunctionPath.get('body').replaceWith(blockStatement);
185
+
186
+ // Replace the target node with an identifier pointing to the new variable
187
+ const nodePath = this.getNodePath(nodeToReplace, path);
188
+ nodePath?.replaceWith(identifier);
189
+ }
190
+ else {
191
+ // Fall back to the original behavior
192
+ const statementPath = this.getClosestStatementPath(path);
193
+
194
+ // Insert the new declaration before the current statement
195
+ const [ insertedPath ] = statementPath.insertBefore(variableDeclaration);
196
+
197
+ // Register the new declaration with the appropriate scope
198
+ statementPath.scope.registerDeclaration(insertedPath);
199
+
200
+ // Replace the target node with an identifier pointing to the new variable
201
+ const nodePath = this.getNodePath(nodeToReplace, path);
202
+ nodePath?.replaceWith(identifier);
203
+ }
204
+
205
+ return identifier;
206
+ }
207
+
208
+ static hoistAsTopLevelVariable(
209
+ path: NodePath,
210
+ variableName: string,
211
+ expression: t.Expression,
212
+ ): t.Identifier {
213
+ // Find the program path
214
+ const programPath = this.findProgram(path);
215
+
216
+ // Check if variable with this name already exists at the top level
217
+ const existingBinding = programPath.scope.getBinding(variableName);
218
+ if (existingBinding)
219
+ return t.identifier(variableName);
220
+
221
+ // Create the variable declaration
222
+ const identifier = t.identifier(variableName);
223
+ const declarator = t.variableDeclarator(identifier, expression);
224
+ const variableDeclaration = t.variableDeclaration('const', [ declarator ]);
225
+
226
+ // Find the last import declaration index
227
+ const programBody = programPath.node.body;
228
+ const lastImportIndex = programBody.reduceRight((lastIndex, node, index) => {
229
+ return lastIndex === -1 && t.isImportDeclaration(node) ? index : lastIndex;
230
+ }, -1);
231
+
232
+ // Insert after the last import, or at the beginning if no imports
233
+ const insertionIndex = lastImportIndex + 1;
234
+
235
+ if (insertionIndex === 0 || insertionIndex >= programBody.length) {
236
+ // No imports found or at the end - add to the beginning/end
237
+ const [ insertedPath ] = insertionIndex === 0
238
+ ? programPath.unshiftContainer('body', variableDeclaration)
239
+ : programPath.pushContainer('body', variableDeclaration);
240
+
241
+ programPath.scope.registerDeclaration(insertedPath);
242
+ }
243
+ else {
244
+ // Insert after the last import
245
+ const bodyPaths = programPath.get('body') as NodePath<t.Statement>[];
246
+ const targetPath = bodyPaths[insertionIndex];
247
+ if (targetPath) {
248
+ const [ insertedPath ] = targetPath.insertBefore(variableDeclaration);
249
+ programPath.scope.registerDeclaration(insertedPath);
250
+ }
251
+ }
252
+
253
+ return identifier;
254
+ }
255
+
256
+ static componentTagDeclaration(
257
+ path: NodePath,
258
+ tagName: string,
259
+ variableName: string,
260
+ createDeclaration: () => t.VariableDeclarator,
261
+ ): t.Identifier {
262
+ // Start from the current scope and work upward
263
+ let currentScope = path.scope;
264
+
265
+ while (currentScope) {
266
+ // First check if the prefixed variable already exists
267
+ const prefixedBinding = currentScope.getBinding(variableName);
268
+ if (prefixedBinding)
269
+ return t.identifier(variableName);
270
+
271
+ // Then check if the tagName exists
272
+ const tagNameBinding = currentScope.getBinding(tagName);
273
+ if (tagNameBinding) {
274
+ // Found the tagName binding, now insert the prefixed declaration just below it
275
+ const declarator = createDeclaration();
276
+ const variableDeclaration = t.variableDeclaration('const', [ declarator ]);
277
+
278
+ // Find the statement-level path to insert after
279
+ // This handles cases where the binding might be an import specifier or variable declarator
280
+ let statementPath: NodePath<t.Node> | null = tagNameBinding.path;
281
+ while (statementPath && !statementPath.isStatement())
282
+ statementPath = statementPath.parentPath;
283
+
284
+ if (!statementPath)
285
+ throw new Error(ERROR_MESSAGES.NO_STATEMENT_PATH(tagName));
286
+
287
+ // Insert the new declaration after the tagName declaration
288
+ const [ insertedPath ] = statementPath.insertAfter(variableDeclaration);
289
+
290
+ // Register the new declaration with the appropriate scope
291
+ statementPath.scope.registerDeclaration(insertedPath);
292
+
293
+ return t.identifier(variableName);
294
+ }
295
+
296
+ // Move up to the parent scope
297
+ currentScope = currentScope.parent;
298
+ }
299
+
300
+ // If tagName is not found in any scope, throw an error
301
+ throw new Error(ERROR_MESSAGES.TAG_NAME_NOT_FOUND(tagName));
302
+ }
303
+
304
+ static componentLiteral(
305
+ tagName: string,
306
+ variableName: string,
307
+ path: NodePath,
308
+ program: t.Program,
309
+ ): t.Identifier {
310
+ EnsureImport.literalMap(program, path);
311
+
312
+ variableName = variableName.replace(COMPONENT_POSTFIX, '');
313
+ tagName = tagName.replace(COMPONENT_POSTFIX, '');
314
+
315
+ return this.componentTagDeclaration(
316
+ path,
317
+ tagName,
318
+ variableName,
319
+ () => t.variableDeclarator(
320
+ t.identifier(variableName),
321
+ t.callExpression(
322
+ t.memberExpression(
323
+ t.identifier(VARIABLES.LITERAL_MAP),
324
+ t.identifier('get'),
325
+ ),
326
+ [ t.identifier(tagName + COMPONENT_POSTFIX) ],
327
+ ),
328
+ ),
329
+ );
330
+ }
331
+
332
+ }
333
+
334
+
335
+ export class EnsureImport {
336
+
337
+ static html(program: t.Program, path: NodePath): void {
338
+ Ensure.import(
339
+ (source) => source === SOURCES.HTML || source === SOURCES.HTML_ALT,
340
+ (name) => name === VARIABLES.HTML,
341
+ () => t.importDeclaration(
342
+ [ t.importSpecifier(t.identifier(VARIABLES.HTML), t.identifier(VARIABLES.HTML)) ],
343
+ t.stringLiteral(SOURCES.HTML),
344
+ ),
345
+ program,
346
+ path,
347
+ );
348
+ }
349
+
350
+ static htmlStatic(program: t.Program, path: NodePath): void {
351
+ Ensure.import(
352
+ (source) => source === SOURCES.HTML_STATIC || source === SOURCES.HTML_STATIC_ALT,
353
+ (name) => name === VARIABLES.HTML,
354
+ () => t.importDeclaration(
355
+ [
356
+ t.importSpecifier(
357
+ t.identifier(VARIABLES.HTML_STATIC),
358
+ t.identifier(VARIABLES.HTML),
359
+ ),
360
+ ],
361
+ t.stringLiteral(SOURCES.HTML_STATIC),
362
+ ),
363
+ program,
364
+ path,
365
+ );
366
+ }
367
+
368
+ static svg(program: t.Program, path: NodePath): void {
369
+ Ensure.import(
370
+ (source) => source === SOURCES.SVG || source === SOURCES.SVG_ALT,
371
+ (name) => name === VARIABLES.SVG,
372
+ () => t.importDeclaration(
373
+ [ t.importSpecifier(t.identifier(VARIABLES.SVG), t.identifier(VARIABLES.SVG)) ],
374
+ t.stringLiteral(SOURCES.SVG),
375
+ ),
376
+ program,
377
+ path,
378
+ );
379
+ }
380
+
381
+ static svgStatic(program: t.Program, path: NodePath): void {
382
+ Ensure.import(
383
+ (source) => source === SOURCES.SVG_STATIC || source === SOURCES.SVG_STATIC_ALT,
384
+ (name) => name === VARIABLES.SVG,
385
+ () => t.importDeclaration(
386
+ [
387
+ t.importSpecifier(
388
+ t.identifier(VARIABLES.SVG_STATIC),
389
+ t.identifier(VARIABLES.SVG),
390
+ ),
391
+ ],
392
+ t.stringLiteral(SOURCES.SVG_STATIC),
393
+ ),
394
+ program,
395
+ path,
396
+ );
397
+ }
398
+
399
+ static mathml(program: t.Program, path: NodePath): void {
400
+ Ensure.import(
401
+ (source) => source === SOURCES.MATHML || source === SOURCES.MATHML_ALT,
402
+ (name) => name === VARIABLES.MATHML,
403
+ () => t.importDeclaration(
404
+ [ t.importSpecifier(t.identifier(VARIABLES.MATHML), t.identifier(VARIABLES.MATHML)) ],
405
+ t.stringLiteral(SOURCES.MATHML),
406
+ ),
407
+ program,
408
+ path,
409
+ );
410
+ }
411
+
412
+ static mathmlStatic(program: t.Program, path: NodePath): void {
413
+ Ensure.import(
414
+ (source) => source === SOURCES.MATHML_STATIC || source === SOURCES.MATHML_STATIC_ALT,
415
+ (name) => name === VARIABLES.MATHML,
416
+ () => t.importDeclaration(
417
+ [
418
+ t.importSpecifier(
419
+ t.identifier(VARIABLES.MATHML_STATIC),
420
+ t.identifier(VARIABLES.MATHML),
421
+ ),
422
+ ],
423
+ t.stringLiteral(SOURCES.MATHML_STATIC),
424
+ ),
425
+ program,
426
+ path,
427
+ );
428
+ }
429
+
430
+ static unsafeStatic(program: t.Program, path: NodePath): void {
431
+ Ensure.import(
432
+ (source) => source === SOURCES.UNSAFE_STATIC || source === SOURCES.UNSAFE_STATIC_ALT,
433
+ (name) => name === VARIABLES.UNSAFE_STATIC,
434
+ () => t.importDeclaration(
435
+ [
436
+ t.importSpecifier(
437
+ t.identifier(VARIABLES.UNSAFE_STATIC),
438
+ t.identifier(VARIABLES.UNSAFE_STATIC),
439
+ ),
440
+ ],
441
+ t.stringLiteral(SOURCES.UNSAFE_STATIC),
442
+ ),
443
+ program,
444
+ path,
445
+ );
446
+ }
447
+
448
+ static createRef(program: t.Program, path: NodePath): void {
449
+ Ensure.import(
450
+ (source) => source === SOURCES.REF_ALT || source === SOURCES.REF,
451
+ (name) => name === VARIABLES.REF,
452
+ () => t.importDeclaration(
453
+ [
454
+ t.importSpecifier(
455
+ t.identifier(VARIABLES.REF),
456
+ t.identifier(VARIABLES.REF),
457
+ ),
458
+ ],
459
+ t.stringLiteral(SOURCES.REF),
460
+ ),
461
+ program,
462
+ path,
463
+ );
464
+ }
465
+
466
+ static styleMap(program: t.Program, path: NodePath): void {
467
+ Ensure.import(
468
+ (source) => source === SOURCES.STYLE_MAP_ALT || source === SOURCES.STYLE_MAP,
469
+ (name) => name === VARIABLES.STYLE_MAP,
470
+ () => t.importDeclaration(
471
+ [
472
+ t.importSpecifier(
473
+ t.identifier(VARIABLES.STYLE_MAP),
474
+ t.identifier(VARIABLES.STYLE_MAP),
475
+ ),
476
+ ],
477
+ t.stringLiteral(SOURCES.STYLE_MAP),
478
+ ),
479
+ program,
480
+ path,
481
+ );
482
+ }
483
+
484
+ static classMap(program: t.Program, path: NodePath): void {
485
+ Ensure.import(
486
+ (source) => source === SOURCES.CLASS_MAP_ALT || source === SOURCES.CLASS_MAP,
487
+ (name) => name === VARIABLES.CLASS_MAP,
488
+ () => t.importDeclaration(
489
+ [
490
+ t.importSpecifier(
491
+ t.identifier(VARIABLES.CLASS_MAP),
492
+ t.identifier(VARIABLES.CLASS_MAP),
493
+ ),
494
+ ],
495
+ t.stringLiteral(SOURCES.CLASS_MAP),
496
+ ),
497
+ program,
498
+ path,
499
+ );
500
+ }
501
+
502
+ static rest(program: t.Program, path: NodePath): void {
503
+ Ensure.import(
504
+ (source) => source === SOURCES.REST,
505
+ (name) => name === VARIABLES.REST,
506
+ () => t.importDeclaration(
507
+ [
508
+ t.importSpecifier(
509
+ t.identifier(VARIABLES.REST),
510
+ t.identifier(VARIABLES.REST),
511
+ ),
512
+ ],
513
+ t.stringLiteral(SOURCES.REST),
514
+ ),
515
+ program,
516
+ path,
517
+ );
518
+ }
519
+
520
+ static literalMap(program: t.Program, path: NodePath): void {
521
+ Ensure.import(
522
+ (source) => source === SOURCES.LITERAL_MAP,
523
+ (name) => name === VARIABLES.LITERAL_MAP,
524
+ () => t.importDeclaration(
525
+ [
526
+ t.importSpecifier(
527
+ t.identifier(VARIABLES.LITERAL_MAP),
528
+ t.identifier(VARIABLES.LITERAL_MAP),
529
+ ),
530
+ ],
531
+ t.stringLiteral(SOURCES.LITERAL_MAP),
532
+ ),
533
+ program,
534
+ path,
535
+ );
536
+ }
537
+
538
+ static taggedTemplateUtil(program: t.Program, path: NodePath): void {
539
+ Ensure.import(
540
+ (source) => source === SOURCES.JSX_LIT,
541
+ (name) => name === VARIABLES.TAGGED_TEMPLATE_UTIL,
542
+ () => t.importDeclaration([
543
+ t.importSpecifier(
544
+ t.identifier(VARIABLES.TAGGED_TEMPLATE_UTIL),
545
+ t.identifier(VARIABLES.TAGGED_TEMPLATE_UTIL),
546
+ ),
547
+ ], t.stringLiteral(SOURCES.JSX_LIT)),
548
+ program,
549
+ path,
550
+ );
551
+ }
552
+
553
+ static booleanPart(program: t.Program, path: NodePath): void {
554
+ Ensure.import(
555
+ (source) => source === SOURCES.JSX_LIT,
556
+ (name) => name === VARIABLES.BOOLEAN_PART,
557
+ () => t.importDeclaration([
558
+ t.importSpecifier(
559
+ t.identifier(VARIABLES.BOOLEAN_PART),
560
+ t.identifier(VARIABLES.BOOLEAN_PART),
561
+ ),
562
+ ], t.stringLiteral(SOURCES.JSX_LIT)),
563
+ program,
564
+ path,
565
+ );
566
+ }
567
+
568
+ static attributePart(program: t.Program, path: NodePath): void {
569
+ Ensure.import(
570
+ (source) => source === SOURCES.JSX_LIT,
571
+ (name) => name === VARIABLES.ATTRIBUTE_PART,
572
+ () => t.importDeclaration([
573
+ t.importSpecifier(
574
+ t.identifier(VARIABLES.ATTRIBUTE_PART),
575
+ t.identifier(VARIABLES.ATTRIBUTE_PART),
576
+ ),
577
+ ], t.stringLiteral(SOURCES.JSX_LIT)),
578
+ program,
579
+ path,
580
+ );
581
+ }
582
+
583
+ static propertyPart(program: t.Program, path: NodePath): void {
584
+ Ensure.import(
585
+ (source) => source === SOURCES.JSX_LIT,
586
+ (name) => name === VARIABLES.PROPERTY_PART,
587
+ () => t.importDeclaration([
588
+ t.importSpecifier(
589
+ t.identifier(VARIABLES.PROPERTY_PART),
590
+ t.identifier(VARIABLES.PROPERTY_PART),
591
+ ),
592
+ ], t.stringLiteral(SOURCES.JSX_LIT)),
593
+ program,
594
+ path,
595
+ );
596
+ }
597
+
598
+ static elementPart(program: t.Program, path: NodePath): void {
599
+ Ensure.import(
600
+ (source) => source === SOURCES.JSX_LIT,
601
+ (name) => name === VARIABLES.ELEMENT_PART,
602
+ () => t.importDeclaration([
603
+ t.importSpecifier(
604
+ t.identifier(VARIABLES.ELEMENT_PART),
605
+ t.identifier(VARIABLES.ELEMENT_PART),
606
+ ),
607
+ ], t.stringLiteral(SOURCES.JSX_LIT)),
608
+ program,
609
+ path,
610
+ );
611
+ }
612
+
613
+ static eventPart(program: t.Program, path: NodePath): void {
614
+ Ensure.import(
615
+ (source) => source === SOURCES.JSX_LIT,
616
+ (name) => name === VARIABLES.EVENT_PART,
617
+ () => t.importDeclaration([
618
+ t.importSpecifier(
619
+ t.identifier(VARIABLES.EVENT_PART),
620
+ t.identifier(VARIABLES.EVENT_PART),
621
+ ),
622
+ ], t.stringLiteral(SOURCES.JSX_LIT)),
623
+ program,
624
+ path,
625
+ );
626
+ }
627
+
628
+ static childPart(program: t.Program, path: NodePath): void {
629
+ Ensure.import(
630
+ (source) => source === SOURCES.JSX_LIT,
631
+ (name) => name === VARIABLES.CHILD_PART,
632
+ () => t.importDeclaration([
633
+ t.importSpecifier(
634
+ t.identifier(VARIABLES.CHILD_PART),
635
+ t.identifier(VARIABLES.CHILD_PART),
636
+ ),
637
+ ], t.stringLiteral(SOURCES.JSX_LIT)),
638
+ program,
639
+ path,
640
+ );
641
+ }
642
+
643
+ }
644
+
645
+
646
+ export type ValidJSXElement = t.JSXElement & {
647
+ openingElement: t.JSXOpeningElement & {
648
+ name: t.JSXIdentifier | t.JSXMemberExpression;
649
+ };
650
+ };
651
+
652
+ export const isValidJSXElement = (path: NodePath): path is NodePath<ValidJSXElement> => {
653
+ const node = path.node;
654
+
655
+ return t.isJSXElement(node)
656
+ && t.isJSXOpeningElement(node.openingElement)
657
+ && (t.isJSXIdentifier(node.openingElement.name)
658
+ || t.isJSXMemberExpression(node.openingElement.name));
659
+ };
660
+
661
+
662
+ export const isValidOpeningElement = (path: NodePath): path is NodePath<t.JSXElement | t.JSXFragment> => {
663
+ return t.isJSXElement(path.node) || t.isJSXFragment(path.node);
664
+ };
665
+
666
+
667
+ export const getJSXElementName = (node: t.JSXElement): string => {
668
+ const openingElement = node.openingElement;
669
+
670
+ const name = t.isJSXIdentifier(openingElement.name)
671
+ ? openingElement.name.name
672
+ : t.isJSXMemberExpression(openingElement.name)
673
+ ? t.isJSXIdentifier(openingElement.name.object)
674
+ ? openingElement.name.object.name + '.' + openingElement.name.property.name
675
+ : ''
676
+ : '';
677
+
678
+ return name;
679
+ };
680
+
681
+
682
+ export const isJSXCustomElementComponent = (nodeOrName: t.JSXElement | string): boolean => {
683
+ const tagName = typeof nodeOrName !== 'string'
684
+ ? getJSXElementName(nodeOrName)
685
+ : nodeOrName;
686
+
687
+ if (tagName.endsWith(COMPONENT_POSTFIX))
688
+ return true;
689
+
690
+ return false;
691
+ };
692
+
693
+
694
+ export const isJSXFunctionElementComponent = (
695
+ pathOrName: NodePath<t.JSXElement | t.JSXFragment> | string,
696
+ ): boolean => {
697
+ let tagName: string;
698
+ if (typeof pathOrName === 'string') {
699
+ tagName = pathOrName;
700
+ }
701
+ else {
702
+ const node = pathOrName.node;
703
+ // If it's a fragment, we cannot determine the tag name.
704
+ if (t.isJSXFragment(node))
705
+ return false;
706
+
707
+ tagName = getJSXElementName(node);
708
+ }
709
+
710
+ if (!isComponent(tagName))
711
+ return false;
712
+
713
+ if (tagName.endsWith(COMPONENT_POSTFIX))
714
+ return false;
715
+
716
+ return true;
717
+ };
718
+
719
+
720
+ export const isJSXElementPath = (path: NodePath): path is NodePath<t.JSXElement> => t.isJSXElement(path.node);
721
+ export const isJSXFragmentPath = (path: NodePath): path is NodePath<t.JSXFragment> => t.isJSXFragment(path.node);
722
+
723
+
724
+ /**
725
+ * Determines if a JSX element will result in a static template.
726
+ * This function traverses the JSX tree to check if any custom element components
727
+ * are present, which would make the template static.
728
+ *
729
+ * @param path - The NodePath of the JSX element to analyze
730
+ * @returns true if the template will be static, false otherwise
731
+ */
732
+ export const isJSXElementStatic = (path: NodePath<t.JSXElement | t.JSXFragment>): boolean => {
733
+ if (t.isJSXElement(path.node) && isJSXCustomElementComponent(path.node))
734
+ return true;
735
+
736
+ for (const childPath of path.get('children')) {
737
+ if (!isJSXElementPath(childPath) && !isJSXFragmentPath(childPath))
738
+ continue;
739
+
740
+ if (isJSXElementStatic(childPath))
741
+ return true;
742
+ }
743
+
744
+ return false;
745
+ };
746
+
747
+
748
+ export const ensureImports = (context: ProcessorContext): void => {
749
+ type Imports = Omit<typeof EnsureImport, 'prototype'>;
750
+ const record = EnsureImport as Imports;
751
+
752
+ // Ensure all imports used in the JSX element are imported.
753
+ context.importsUsed.forEach(importName => {
754
+ const key = importName as keyof Imports;
755
+ if (key in record)
756
+ record[key](context.program, context.path);
757
+ });
758
+ };
759
+
760
+ export type TemplateType = Values<Pick<typeof VARIABLES, 'HTML' | 'SVG' | 'MATHML'>>;
761
+ export const getTemplateType = (path: NodePath<t.JSXElement | t.JSXFragment>): TemplateType => {
762
+ if (t.isJSXElement(path.node)) {
763
+ const name = getJSXElementName(path.node);
764
+
765
+ return getTemplateTag(name);
766
+ }
767
+
768
+ for (const childPath of path.get('children')) {
769
+ if (!isJSXElementPath(childPath) && !isJSXFragmentPath(childPath))
770
+ continue;
771
+
772
+ return getTemplateType(childPath);
773
+ }
774
+
775
+ return VARIABLES.HTML;
776
+ };
777
+
778
+
779
+ export const getTemplateTag = (
780
+ tagName: string,
781
+ ): Values<Pick<typeof VARIABLES, 'HTML' | 'SVG' | 'MATHML'>> => {
782
+ if (isSvgTag(tagName))
783
+ return VARIABLES.SVG;
784
+
785
+ if (isMathmlTag(tagName))
786
+ return VARIABLES.MATHML;
787
+
788
+ return VARIABLES.HTML;
789
+ };