@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.
- package/README.md +768 -0
- package/dist/compiler/attribute-processor.d.ts +128 -0
- package/dist/compiler/attribute-processor.d.ts.map +1 -0
- package/dist/compiler/attribute-processor.js +380 -0
- package/dist/compiler/attribute-processor.js.map +1 -0
- package/dist/compiler/babel-preset.d.ts +6 -0
- package/dist/compiler/babel-preset.d.ts.map +1 -0
- package/dist/compiler/babel-preset.js +27 -0
- package/dist/compiler/babel-preset.js.map +1 -0
- package/dist/compiler/builder.d.ts +22 -0
- package/dist/compiler/builder.d.ts.map +1 -0
- package/dist/compiler/builder.js +62 -0
- package/dist/compiler/builder.js.map +1 -0
- package/dist/compiler/compiler-utils.d.ts +81 -0
- package/dist/compiler/compiler-utils.d.ts.map +1 -0
- package/dist/compiler/compiler-utils.js +410 -0
- package/dist/compiler/compiler-utils.js.map +1 -0
- package/dist/compiler/config.d.ts +77 -0
- package/dist/compiler/config.d.ts.map +1 -0
- package/dist/compiler/config.js +78 -0
- package/dist/compiler/config.js.map +1 -0
- package/dist/compiler/postprocess.d.ts +5 -0
- package/dist/compiler/postprocess.d.ts.map +1 -0
- package/dist/compiler/postprocess.js +3 -0
- package/dist/compiler/postprocess.js.map +1 -0
- package/dist/compiler/preprocess.d.ts +5 -0
- package/dist/compiler/preprocess.d.ts.map +1 -0
- package/dist/compiler/preprocess.js +28 -0
- package/dist/compiler/preprocess.js.map +1 -0
- package/dist/compiler/transform-jsx.d.ts +5 -0
- package/dist/compiler/transform-jsx.d.ts.map +1 -0
- package/dist/compiler/transform-jsx.js +25 -0
- package/dist/compiler/transform-jsx.js.map +1 -0
- package/dist/compiler/transpiler.d.ts +48 -0
- package/dist/compiler/transpiler.d.ts.map +1 -0
- package/dist/compiler/transpiler.js +463 -0
- package/dist/compiler/transpiler.js.map +1 -0
- package/dist/compiler/vite-plugin.d.ts +38 -0
- package/dist/compiler/vite-plugin.d.ts.map +1 -0
- package/dist/compiler/vite-plugin.js +96 -0
- package/dist/compiler/vite-plugin.js.map +1 -0
- package/dist/runtime/choose-component.d.ts +39 -0
- package/dist/runtime/choose-component.d.ts.map +1 -0
- package/dist/runtime/choose-component.js +40 -0
- package/dist/runtime/choose-component.js.map +1 -0
- package/dist/runtime/compiler-ctors.d.ts +21 -0
- package/dist/runtime/compiler-ctors.d.ts.map +1 -0
- package/dist/runtime/compiler-ctors.js +21 -0
- package/dist/runtime/compiler-ctors.js.map +1 -0
- package/dist/runtime/for-component.d.ts +25 -0
- package/dist/runtime/for-component.d.ts.map +1 -0
- package/dist/runtime/for-component.js +35 -0
- package/dist/runtime/for-component.js.map +1 -0
- package/dist/runtime/literal-map.d.ts +22 -0
- package/dist/runtime/literal-map.d.ts.map +1 -0
- package/dist/runtime/literal-map.js +29 -0
- package/dist/runtime/literal-map.js.map +1 -0
- package/dist/runtime/rest-directive.d.ts +28 -0
- package/dist/runtime/rest-directive.d.ts.map +1 -0
- package/dist/runtime/rest-directive.js +49 -0
- package/dist/runtime/rest-directive.js.map +1 -0
- package/dist/runtime/show-component.d.ts +33 -0
- package/dist/runtime/show-component.d.ts.map +1 -0
- package/dist/runtime/show-component.js +30 -0
- package/dist/runtime/show-component.js.map +1 -0
- package/dist/runtime/tagged-template.d.ts +12 -0
- package/dist/runtime/tagged-template.d.ts.map +1 -0
- package/dist/runtime/tagged-template.js +12 -0
- package/dist/runtime/tagged-template.js.map +1 -0
- package/dist/runtime/type-helpers.d.ts +80 -0
- package/dist/runtime/type-helpers.d.ts.map +1 -0
- package/dist/runtime/type-helpers.js +85 -0
- package/dist/runtime/type-helpers.js.map +1 -0
- package/dist/shared/jsx-types.d.ts +2139 -0
- package/dist/shared/jsx-types.d.ts.map +1 -0
- package/dist/shared/jsx-types.js +2 -0
- package/dist/shared/jsx-types.js.map +1 -0
- package/dist/shared/jsx-utils.d.ts +30 -0
- package/dist/shared/jsx-utils.d.ts.map +1 -0
- package/dist/shared/jsx-utils.js +58 -0
- package/dist/shared/jsx-utils.js.map +1 -0
- package/dist/shared/mathml-tags.d.ts +12 -0
- package/dist/shared/mathml-tags.d.ts.map +1 -0
- package/dist/shared/mathml-tags.js +215 -0
- package/dist/shared/mathml-tags.js.map +1 -0
- package/dist/shared/svg-tags.d.ts +13 -0
- package/dist/shared/svg-tags.d.ts.map +1 -0
- package/dist/shared/svg-tags.js +95 -0
- package/dist/shared/svg-tags.js.map +1 -0
- package/dist/utils.d.ts +30 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +30 -0
- package/dist/utils.js.map +1 -0
- package/package.json +52 -0
- package/src/compiler/attribute-processor.ts +579 -0
- package/src/compiler/babel-preset.ts +34 -0
- package/src/compiler/builder.ts +86 -0
- package/src/compiler/compiler-utils.ts +789 -0
- package/src/compiler/config.ts +77 -0
- package/src/compiler/postprocess.ts +7 -0
- package/src/compiler/preprocess.ts +40 -0
- package/src/compiler/transform-jsx.ts +36 -0
- package/src/compiler/transpiler.ts +644 -0
- package/src/compiler/vite-plugin.ts +114 -0
- package/src/external.d.ts +9 -0
- package/src/runtime/choose-component.ts +53 -0
- package/src/runtime/compiler-ctors.ts +28 -0
- package/src/runtime/for-component.ts +54 -0
- package/src/runtime/literal-map.ts +37 -0
- package/src/runtime/rest-directive.ts +66 -0
- package/src/runtime/show-component.ts +48 -0
- package/src/runtime/tagged-template.ts +11 -0
- package/src/runtime/type-helpers.ts +91 -0
- package/src/shared/jsx-types.ts +2556 -0
- package/src/shared/jsx-utils.ts +85 -0
- package/src/shared/mathml-tags.ts +235 -0
- package/src/shared/svg-tags.ts +103 -0
- package/src/tsconfig.json +4 -0
- 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
|
+
};
|