@esportsplus/template 0.32.0 → 0.32.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.
- package/build/attributes.js +1 -2
- package/build/constants.d.ts +18 -3
- package/build/constants.js +31 -4
- package/build/html.d.ts +3 -3
- package/build/index.d.ts +3 -3
- package/build/index.js +3 -3
- package/build/slot/array.d.ts +2 -2
- package/build/slot/array.js +5 -4
- package/build/slot/render.js +3 -2
- package/build/transformer/codegen.d.ts +3 -9
- package/build/transformer/codegen.js +90 -147
- package/build/transformer/index.d.ts +1 -5
- package/build/transformer/index.js +30 -46
- package/build/transformer/parser.d.ts +3 -2
- package/build/transformer/parser.js +4 -4
- package/build/transformer/plugins/tsc.d.ts +2 -2
- package/build/transformer/plugins/tsc.js +3 -4
- package/build/transformer/plugins/vite.d.ts +11 -3
- package/build/transformer/plugins/vite.js +7 -37
- package/build/transformer/ts-parser.d.ts +1 -2
- package/build/transformer/ts-parser.js +28 -41
- package/build/transformer/type-analyzer.d.ts +4 -5
- package/build/transformer/type-analyzer.js +73 -118
- package/build/types.d.ts +1 -1
- package/package.json +7 -7
- package/src/attributes.ts +1 -4
- package/src/constants.ts +42 -6
- package/src/html.ts +3 -3
- package/src/index.ts +5 -3
- package/src/slot/array.ts +9 -6
- package/src/slot/render.ts +5 -2
- package/src/transformer/codegen.ts +119 -189
- package/src/transformer/index.ts +34 -54
- package/src/transformer/parser.ts +10 -7
- package/src/transformer/plugins/tsc.ts +3 -5
- package/src/transformer/plugins/vite.ts +7 -47
- package/src/transformer/ts-parser.ts +34 -54
- package/src/transformer/type-analyzer.ts +90 -158
- package/src/types.ts +1 -1
- package/test/vite.config.ts +1 -1
- package/build/event/constants.d.ts +0 -3
- package/build/event/constants.js +0 -13
- package/src/event/constants.ts +0 -16
- package/storage/rewrite-analysis-2026-01-04.md +0 -439
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type
|
|
1
|
+
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { code as c, uid, type Replacement } from '@esportsplus/typescript/transformer';
|
|
3
3
|
import type { ReactiveCallInfo, TemplateInfo } from './ts-parser';
|
|
4
4
|
import { analyzeExpression, generateAttributeBinding, generateSpreadBindings } from './type-analyzer';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
COMPILER_ENTRYPOINT, COMPILER_NAMESPACE, COMPILER_TYPES,
|
|
7
|
+
PACKAGE
|
|
8
|
+
} from '~/constants';
|
|
6
9
|
import parser from './parser';
|
|
7
10
|
|
|
8
11
|
|
|
@@ -12,7 +15,15 @@ type AttributeSlot = {
|
|
|
12
15
|
statics: Record<string, string>;
|
|
13
16
|
};
|
|
14
17
|
path: string[];
|
|
15
|
-
type:
|
|
18
|
+
type: COMPILER_TYPES.AttributeSlot;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type CodegenContext = {
|
|
22
|
+
checker?: ts.TypeChecker;
|
|
23
|
+
hoistedFactories: Map<string, string>;
|
|
24
|
+
htmlToTemplateId: Map<string, string>;
|
|
25
|
+
printer: ts.Printer;
|
|
26
|
+
sourceFile: ts.SourceFile;
|
|
16
27
|
};
|
|
17
28
|
|
|
18
29
|
type CodegenResult = {
|
|
@@ -22,7 +33,7 @@ type CodegenResult = {
|
|
|
22
33
|
|
|
23
34
|
type NodeSlot = {
|
|
24
35
|
path: string[];
|
|
25
|
-
type:
|
|
36
|
+
type: COMPILER_TYPES.NodeSlot;
|
|
26
37
|
};
|
|
27
38
|
|
|
28
39
|
type ParseResult = {
|
|
@@ -34,69 +45,28 @@ type ParseResult = {
|
|
|
34
45
|
const ARROW_EMPTY_PARAMS = /\(\s*\)\s*=>\s*$/;
|
|
35
46
|
|
|
36
47
|
|
|
37
|
-
let
|
|
38
|
-
hoistedFactories = new Map<string, string>(),
|
|
39
|
-
htmlToTemplateId = new Map<string, string>(),
|
|
40
|
-
nameArraySlot = '',
|
|
41
|
-
nameAttr = '',
|
|
42
|
-
nameEffectSlot = '',
|
|
43
|
-
nameEvent = '',
|
|
44
|
-
nameSlot = '',
|
|
45
|
-
nameTemplate = '',
|
|
46
|
-
needsArraySlot = false,
|
|
47
|
-
needsAttr = false,
|
|
48
|
-
needsEffectSlot = false,
|
|
49
|
-
needsEvent = false,
|
|
50
|
-
needsSlot = false;
|
|
48
|
+
let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
51
49
|
|
|
52
50
|
|
|
53
51
|
function collectNestedTemplateReplacements(
|
|
52
|
+
ctx: CodegenContext,
|
|
54
53
|
node: ts.Node,
|
|
55
54
|
exprStart: number,
|
|
56
|
-
sourceFile: ts.SourceFile,
|
|
57
55
|
replacements: Replacement[]
|
|
58
56
|
): void {
|
|
59
57
|
if (isNestedHtmlTemplate(node as ts.Expression)) {
|
|
60
58
|
replacements.push({
|
|
61
59
|
end: node.end - exprStart,
|
|
62
|
-
newText: generateNestedTemplateCode(node as ts.TaggedTemplateExpression
|
|
60
|
+
newText: generateNestedTemplateCode(ctx, node as ts.TaggedTemplateExpression),
|
|
63
61
|
start: node.getStart() - exprStart
|
|
64
62
|
});
|
|
65
63
|
}
|
|
66
64
|
else {
|
|
67
|
-
ts.forEachChild(node, child => collectNestedTemplateReplacements(child, exprStart,
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function generateImports(): string {
|
|
72
|
-
let specifiers: string[] = [];
|
|
73
|
-
|
|
74
|
-
if (needsArraySlot) {
|
|
75
|
-
specifiers.push(`ArraySlot as ${nameArraySlot}`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (needsEffectSlot) {
|
|
79
|
-
specifiers.push(`EffectSlot as ${nameEffectSlot}`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (needsAttr) {
|
|
83
|
-
specifiers.push(`attributes as ${nameAttr}`);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (needsEvent) {
|
|
87
|
-
specifiers.push(`event as ${nameEvent}`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (needsSlot) {
|
|
91
|
-
specifiers.push(`slot as ${nameSlot}`);
|
|
65
|
+
ts.forEachChild(node, child => collectNestedTemplateReplacements(ctx, child, exprStart, replacements));
|
|
92
66
|
}
|
|
93
|
-
|
|
94
|
-
specifiers.push(`template as ${nameTemplate}`);
|
|
95
|
-
|
|
96
|
-
return `import { ${specifiers.join(', ')} } from '@esportsplus/template';`;
|
|
97
67
|
}
|
|
98
68
|
|
|
99
|
-
function generateNestedTemplateCode(
|
|
69
|
+
function generateNestedTemplateCode(ctx: CodegenContext, node: ts.TaggedTemplateExpression): string {
|
|
100
70
|
let expressions: ts.Expression[] = [],
|
|
101
71
|
exprTexts: string[] = [],
|
|
102
72
|
literals: string[] = [],
|
|
@@ -113,61 +83,57 @@ function generateNestedTemplateCode(node: ts.TaggedTemplateExpression, sourceFil
|
|
|
113
83
|
|
|
114
84
|
expressions.push(expr);
|
|
115
85
|
literals.push(template.templateSpans[i].literal.text);
|
|
116
|
-
exprTexts.push(rewriteExpression(
|
|
86
|
+
exprTexts.push(rewriteExpression(ctx, expr));
|
|
117
87
|
}
|
|
118
88
|
}
|
|
119
89
|
|
|
120
90
|
return generateTemplateCode(
|
|
91
|
+
ctx,
|
|
121
92
|
parser.parse(literals) as ParseResult,
|
|
122
93
|
exprTexts,
|
|
123
94
|
expressions,
|
|
124
|
-
sourceFile,
|
|
125
95
|
false
|
|
126
96
|
);
|
|
127
97
|
}
|
|
128
98
|
|
|
129
|
-
function generateNodeBinding(anchor: string, exprText: string, exprNode: ts.Expression | undefined
|
|
99
|
+
function generateNodeBinding(ctx: CodegenContext, anchor: string, exprText: string, exprNode: ts.Expression | undefined): string {
|
|
130
100
|
if (!exprNode) {
|
|
131
|
-
|
|
132
|
-
return `${nameSlot}(${anchor}, ${exprText});`;
|
|
101
|
+
return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
133
102
|
}
|
|
134
103
|
|
|
135
104
|
if (isNestedHtmlTemplate(exprNode)) {
|
|
136
|
-
return `${anchor}.parentNode.insertBefore(${generateNestedTemplateCode(
|
|
105
|
+
return `${anchor}.parentNode.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
|
|
137
106
|
}
|
|
138
107
|
|
|
139
|
-
let slotType = analyzeExpression(exprNode,
|
|
108
|
+
let slotType = analyzeExpression(exprNode, ctx.checker);
|
|
140
109
|
|
|
141
110
|
switch (slotType) {
|
|
142
|
-
case
|
|
143
|
-
|
|
144
|
-
return `new ${nameEffectSlot}(${anchor}, ${exprText});`;
|
|
111
|
+
case COMPILER_TYPES.Effect:
|
|
112
|
+
return `new ${COMPILER_NAMESPACE}.EffectSlot(${anchor}, ${exprText});`;
|
|
145
113
|
|
|
146
|
-
case
|
|
147
|
-
|
|
148
|
-
return `new ${nameArraySlot}(${anchor}, ${exprText});`;
|
|
114
|
+
case COMPILER_TYPES.ArraySlot:
|
|
115
|
+
return `new ${COMPILER_NAMESPACE}.ArraySlot(${anchor}, ${exprText});`;
|
|
149
116
|
|
|
150
|
-
case
|
|
117
|
+
case COMPILER_TYPES.Static:
|
|
151
118
|
return `${anchor}.textContent = ${exprText};`;
|
|
152
119
|
|
|
153
|
-
case
|
|
120
|
+
case COMPILER_TYPES.DocumentFragment:
|
|
154
121
|
return `${anchor}.parentNode.insertBefore(${exprText}, ${anchor});`;
|
|
155
122
|
|
|
156
123
|
default:
|
|
157
|
-
|
|
158
|
-
return `${nameSlot}(${anchor}, ${exprText});`;
|
|
124
|
+
return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
159
125
|
}
|
|
160
126
|
}
|
|
161
127
|
|
|
162
128
|
function generateTemplateCode(
|
|
129
|
+
ctx: CodegenContext,
|
|
163
130
|
{ html, slots }: ParseResult,
|
|
164
131
|
exprTexts: string[],
|
|
165
132
|
exprNodes: ts.Expression[],
|
|
166
|
-
sourceFile: ts.SourceFile,
|
|
167
133
|
isArrowBody: boolean
|
|
168
134
|
): string {
|
|
169
135
|
if (!slots || slots.length === 0) {
|
|
170
|
-
return `${getOrCreateTemplateId(html)}()`;
|
|
136
|
+
return `${getOrCreateTemplateId(ctx, html)}()`;
|
|
171
137
|
}
|
|
172
138
|
|
|
173
139
|
let code: string[] = [],
|
|
@@ -176,7 +142,7 @@ function generateTemplateCode(
|
|
|
176
142
|
nodes = new Map<string, string>(),
|
|
177
143
|
root = uid('root');
|
|
178
144
|
|
|
179
|
-
declarations.push(`${root} = ${getOrCreateTemplateId(html)}()`);
|
|
145
|
+
declarations.push(`${root} = ${getOrCreateTemplateId(ctx, html)}()`);
|
|
180
146
|
nodes.set('', root);
|
|
181
147
|
|
|
182
148
|
for (let i = 0, n = slots.length; i < n; i++) {
|
|
@@ -223,41 +189,44 @@ function generateTemplateCode(
|
|
|
223
189
|
: (nodes.get(slots[i].path.join('.')) || root),
|
|
224
190
|
slot = slots[i];
|
|
225
191
|
|
|
226
|
-
if (slot.type ===
|
|
227
|
-
|
|
228
|
-
|
|
192
|
+
if (slot.type === COMPILER_TYPES.AttributeSlot) {
|
|
193
|
+
let names = slot.attributes.names;
|
|
194
|
+
|
|
195
|
+
for (let j = 0, m = names.length; j < m; j++) {
|
|
196
|
+
let name = names[j];
|
|
229
197
|
|
|
230
198
|
if (name === 'spread') {
|
|
231
199
|
let bindings = generateSpreadBindings(
|
|
232
200
|
exprNodes[index],
|
|
233
201
|
exprTexts[index] || 'undefined',
|
|
234
202
|
elementVar,
|
|
235
|
-
|
|
236
|
-
|
|
203
|
+
ctx.checker,
|
|
204
|
+
COMPILER_NAMESPACE
|
|
237
205
|
);
|
|
238
206
|
|
|
239
207
|
for (let k = 0, o = bindings.length; k < o; k++) {
|
|
240
|
-
trackBindingUsage(bindings[k]);
|
|
241
208
|
code.push(bindings[k]);
|
|
242
209
|
}
|
|
243
210
|
|
|
244
211
|
index++;
|
|
245
212
|
}
|
|
246
213
|
else {
|
|
247
|
-
|
|
214
|
+
code.push(
|
|
215
|
+
generateAttributeBinding(
|
|
248
216
|
elementVar,
|
|
249
217
|
name,
|
|
250
218
|
exprTexts[index++] || 'undefined',
|
|
251
|
-
slot.attributes.statics[name] || ''
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
code.push(binding);
|
|
219
|
+
slot.attributes.statics[name] || '',
|
|
220
|
+
COMPILER_NAMESPACE
|
|
221
|
+
)
|
|
222
|
+
);
|
|
256
223
|
}
|
|
257
224
|
}
|
|
258
225
|
}
|
|
259
226
|
else {
|
|
260
|
-
code.push(
|
|
227
|
+
code.push(
|
|
228
|
+
generateNodeBinding(ctx, elementVar, exprTexts[index] || 'undefined', exprNodes[index])
|
|
229
|
+
);
|
|
261
230
|
index++;
|
|
262
231
|
}
|
|
263
232
|
}
|
|
@@ -268,13 +237,13 @@ function generateTemplateCode(
|
|
|
268
237
|
return code.join('\n');
|
|
269
238
|
}
|
|
270
239
|
|
|
271
|
-
function getOrCreateTemplateId(html: string): string {
|
|
272
|
-
let id = htmlToTemplateId.get(html);
|
|
240
|
+
function getOrCreateTemplateId(ctx: CodegenContext, html: string): string {
|
|
241
|
+
let id = ctx.htmlToTemplateId.get(html);
|
|
273
242
|
|
|
274
243
|
if (!id) {
|
|
275
244
|
id = uid('tmpl');
|
|
276
|
-
hoistedFactories.set(id, html);
|
|
277
|
-
htmlToTemplateId.set(html, id);
|
|
245
|
+
ctx.hoistedFactories.set(id, html);
|
|
246
|
+
ctx.htmlToTemplateId.set(html, id);
|
|
278
247
|
}
|
|
279
248
|
|
|
280
249
|
return id;
|
|
@@ -304,36 +273,16 @@ function hasArraySlotImport(sourceFile: ts.SourceFile): boolean {
|
|
|
304
273
|
return false;
|
|
305
274
|
}
|
|
306
275
|
|
|
307
|
-
function
|
|
308
|
-
if (
|
|
309
|
-
ts.isNewExpression(node) &&
|
|
310
|
-
ts.isIdentifier(node.expression) &&
|
|
311
|
-
node.expression.text === 'ArraySlot'
|
|
312
|
-
) {
|
|
276
|
+
function hasMatch(node: ts.Node, predicate: (n: ts.Node) => boolean): boolean {
|
|
277
|
+
if (predicate(node)) {
|
|
313
278
|
return true;
|
|
314
279
|
}
|
|
315
280
|
|
|
316
281
|
let found = false;
|
|
317
282
|
|
|
318
283
|
ts.forEachChild(node, child => {
|
|
319
|
-
if (!found
|
|
320
|
-
found =
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
return found;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function hasNestedTemplates(node: ts.Node): boolean {
|
|
328
|
-
if (isNestedHtmlTemplate(node as ts.Expression)) {
|
|
329
|
-
return true;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
let found = false;
|
|
333
|
-
|
|
334
|
-
ts.forEachChild(node, child => {
|
|
335
|
-
if (!found && hasNestedTemplates(child)) {
|
|
336
|
-
found = true;
|
|
284
|
+
if (!found) {
|
|
285
|
+
found = hasMatch(child, predicate);
|
|
337
286
|
}
|
|
338
287
|
});
|
|
339
288
|
|
|
@@ -341,93 +290,79 @@ function hasNestedTemplates(node: ts.Node): boolean {
|
|
|
341
290
|
}
|
|
342
291
|
|
|
343
292
|
function isNestedHtmlTemplate(expr: ts.Expression): expr is ts.TaggedTemplateExpression {
|
|
344
|
-
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text ===
|
|
293
|
+
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
|
|
345
294
|
}
|
|
346
295
|
|
|
347
|
-
function isNestedTemplate(template: TemplateInfo,
|
|
348
|
-
for (let i = 0, n =
|
|
349
|
-
let
|
|
296
|
+
function isNestedTemplate(template: TemplateInfo, exprRanges: { end: number; start: number }[]): boolean {
|
|
297
|
+
for (let i = 0, n = exprRanges.length; i < n; i++) {
|
|
298
|
+
let range = exprRanges[i];
|
|
350
299
|
|
|
351
|
-
if (
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
for (let j = 0, m = other.expressions.length; j < m; j++) {
|
|
356
|
-
let expr = other.expressions[j];
|
|
357
|
-
|
|
358
|
-
if (template.start >= expr.getStart() && template.end <= expr.end) {
|
|
359
|
-
return true;
|
|
360
|
-
}
|
|
300
|
+
if (template.start >= range.start && template.end <= range.end) {
|
|
301
|
+
return true;
|
|
361
302
|
}
|
|
362
303
|
}
|
|
363
304
|
|
|
364
305
|
return false;
|
|
365
306
|
}
|
|
366
307
|
|
|
367
|
-
function rewriteExpression(
|
|
308
|
+
function rewriteExpression(ctx: CodegenContext, expr: ts.Expression): string {
|
|
368
309
|
if (isNestedHtmlTemplate(expr)) {
|
|
369
|
-
return generateNestedTemplateCode(
|
|
310
|
+
return generateNestedTemplateCode(ctx, expr);
|
|
370
311
|
}
|
|
371
312
|
|
|
372
|
-
if (!
|
|
373
|
-
return
|
|
313
|
+
if (!hasMatch(expr, n => isNestedHtmlTemplate(n as ts.Expression))) {
|
|
314
|
+
return ctx.printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
|
|
374
315
|
}
|
|
375
316
|
|
|
376
|
-
let
|
|
377
|
-
replacements: Replacement[] = [];
|
|
317
|
+
let replacements: Replacement[] = [];
|
|
378
318
|
|
|
379
|
-
collectNestedTemplateReplacements(
|
|
319
|
+
collectNestedTemplateReplacements(ctx, expr, expr.getStart(), replacements);
|
|
380
320
|
|
|
381
|
-
return
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
function trackBindingUsage(binding: string): void {
|
|
385
|
-
if (binding.startsWith(nameEvent + '.')) {
|
|
386
|
-
needsEvent = true;
|
|
387
|
-
}
|
|
388
|
-
else if (binding.startsWith(nameAttr + '.')) {
|
|
389
|
-
needsAttr = true;
|
|
390
|
-
}
|
|
321
|
+
return c.replaceReverse(expr.getText(ctx.sourceFile), replacements);
|
|
391
322
|
}
|
|
392
323
|
|
|
393
324
|
|
|
394
325
|
const addArraySlotImport = (code: string): string => {
|
|
395
|
-
return
|
|
326
|
+
return `import * as ${COMPILER_NAMESPACE} from '${PACKAGE}';\n` + code;
|
|
396
327
|
};
|
|
397
328
|
|
|
398
|
-
const generateCode = (templates: TemplateInfo[], originalCode: string, sourceFile: ts.SourceFile): CodegenResult => {
|
|
329
|
+
const generateCode = (templates: TemplateInfo[], originalCode: string, sourceFile: ts.SourceFile, checker?: ts.TypeChecker): CodegenResult => {
|
|
399
330
|
if (templates.length === 0) {
|
|
400
331
|
return { changed: false, code: originalCode };
|
|
401
332
|
}
|
|
402
333
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
needsSlot = false;
|
|
416
|
-
|
|
417
|
-
let rootTemplates = templates.filter(t => !isNestedTemplate(t, templates));
|
|
334
|
+
// Precompute expression ranges for nested template detection
|
|
335
|
+
let ranges: { end: number; start: number }[] = [];
|
|
336
|
+
|
|
337
|
+
for (let i = 0, n = templates.length; i < n; i++) {
|
|
338
|
+
let exprs = templates[i].expressions;
|
|
339
|
+
|
|
340
|
+
for (let j = 0, m = exprs.length; j < m; j++) {
|
|
341
|
+
ranges.push({ end: exprs[j].end, start: exprs[j].getStart() });
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
let rootTemplates = templates.filter(t => !isNestedTemplate(t, ranges));
|
|
418
346
|
|
|
419
347
|
if (rootTemplates.length === 0) {
|
|
420
348
|
return { changed: false, code: originalCode };
|
|
421
349
|
}
|
|
422
350
|
|
|
423
|
-
let
|
|
351
|
+
let ctx: CodegenContext = {
|
|
352
|
+
checker,
|
|
353
|
+
hoistedFactories: new Map(),
|
|
354
|
+
htmlToTemplateId: new Map(),
|
|
355
|
+
printer,
|
|
356
|
+
sourceFile
|
|
357
|
+
},
|
|
358
|
+
replacements: Replacement[] = [];
|
|
424
359
|
|
|
425
360
|
for (let i = 0, n = rootTemplates.length; i < n; i++) {
|
|
426
361
|
let exprTexts: string[] = [],
|
|
427
362
|
template = rootTemplates[i];
|
|
428
363
|
|
|
429
364
|
for (let j = 0, m = template.expressions.length; j < m; j++) {
|
|
430
|
-
exprTexts.push(rewriteExpression(template.expressions[j]
|
|
365
|
+
exprTexts.push(rewriteExpression(ctx, template.expressions[j]));
|
|
431
366
|
}
|
|
432
367
|
|
|
433
368
|
let codeBefore = originalCode.slice(0, template.start),
|
|
@@ -441,7 +376,7 @@ const generateCode = (templates: TemplateInfo[], originalCode: string, sourceFil
|
|
|
441
376
|
if (arrowMatch) {
|
|
442
377
|
replacements.push({
|
|
443
378
|
end: template.end,
|
|
444
|
-
newText: getOrCreateTemplateId(parsed.html),
|
|
379
|
+
newText: getOrCreateTemplateId(ctx, parsed.html),
|
|
445
380
|
start: template.start - arrowMatch[0].length
|
|
446
381
|
});
|
|
447
382
|
continue;
|
|
@@ -451,10 +386,10 @@ const generateCode = (templates: TemplateInfo[], originalCode: string, sourceFil
|
|
|
451
386
|
replacements.push({
|
|
452
387
|
end: template.end,
|
|
453
388
|
newText: generateTemplateCode(
|
|
389
|
+
ctx,
|
|
454
390
|
parsed,
|
|
455
391
|
exprTexts,
|
|
456
392
|
template.expressions,
|
|
457
|
-
sourceFile,
|
|
458
393
|
isArrowBody
|
|
459
394
|
),
|
|
460
395
|
start: template.start
|
|
@@ -462,16 +397,16 @@ const generateCode = (templates: TemplateInfo[], originalCode: string, sourceFil
|
|
|
462
397
|
}
|
|
463
398
|
|
|
464
399
|
let changed = replacements.length > 0,
|
|
465
|
-
code =
|
|
400
|
+
code = c.replaceReverse(originalCode, replacements);
|
|
466
401
|
|
|
467
|
-
if (changed && hoistedFactories.size > 0) {
|
|
402
|
+
if (changed && ctx.hoistedFactories.size > 0) {
|
|
468
403
|
let factories: string[] = [];
|
|
469
404
|
|
|
470
|
-
for (let [id, html] of hoistedFactories) {
|
|
471
|
-
factories.push(`const ${id} = ${
|
|
405
|
+
for (let [id, html] of ctx.hoistedFactories) {
|
|
406
|
+
factories.push(`const ${id} = ${COMPILER_NAMESPACE}.template(\`${html}\`);`);
|
|
472
407
|
}
|
|
473
408
|
|
|
474
|
-
code =
|
|
409
|
+
code = `import * as ${COMPILER_NAMESPACE} from '${PACKAGE}';\n\n` + factories.join('\n') + '\n\n' + code;
|
|
475
410
|
}
|
|
476
411
|
|
|
477
412
|
return { changed, code };
|
|
@@ -482,37 +417,32 @@ const generateReactiveInlining = (calls: ReactiveCallInfo[], code: string, sourc
|
|
|
482
417
|
return code;
|
|
483
418
|
}
|
|
484
419
|
|
|
485
|
-
let
|
|
486
|
-
result = code;
|
|
420
|
+
let replacements: Replacement[] = [];
|
|
487
421
|
|
|
488
|
-
for (let i = calls.length
|
|
422
|
+
for (let i = 0, n = calls.length; i < n; i++) {
|
|
489
423
|
let call = calls[i];
|
|
490
424
|
|
|
491
|
-
|
|
492
|
-
|
|
425
|
+
replacements.push({
|
|
426
|
+
end: call.end,
|
|
427
|
+
newText: `new ${COMPILER_NAMESPACE}.ArraySlot(
|
|
493
428
|
${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)},
|
|
494
429
|
${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)}
|
|
495
|
-
)
|
|
496
|
-
|
|
430
|
+
)`,
|
|
431
|
+
start: call.start
|
|
432
|
+
});
|
|
497
433
|
}
|
|
498
434
|
|
|
499
|
-
return
|
|
435
|
+
return c.replaceReverse(code, replacements);
|
|
500
436
|
};
|
|
501
437
|
|
|
502
|
-
const getNames = () => ({
|
|
503
|
-
attr: nameAttr,
|
|
504
|
-
event: nameEvent,
|
|
505
|
-
slot: nameSlot
|
|
506
|
-
});
|
|
507
|
-
|
|
508
438
|
const needsArraySlotImport = (sourceFile: ts.SourceFile): boolean => {
|
|
509
|
-
return
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
439
|
+
return hasMatch(sourceFile, n =>
|
|
440
|
+
ts.isNewExpression(n) &&
|
|
441
|
+
ts.isPropertyAccessExpression(n.expression) &&
|
|
442
|
+
n.expression.name.text === 'ArraySlot'
|
|
443
|
+
) && !hasArraySlotImport(sourceFile);
|
|
514
444
|
};
|
|
515
445
|
|
|
516
446
|
|
|
517
|
-
export { addArraySlotImport, generateCode, generateReactiveInlining,
|
|
447
|
+
export { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport };
|
|
518
448
|
export type { CodegenResult };
|
package/src/transformer/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport
|
|
1
|
+
import { code as c } from '@esportsplus/typescript/transformer';
|
|
2
|
+
import { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport } from './codegen';
|
|
3
|
+
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY } from '../constants';
|
|
3
4
|
import { findHtmlTemplates, findReactiveCalls } from './ts-parser';
|
|
4
5
|
import { ts } from '@esportsplus/typescript';
|
|
5
6
|
|
|
@@ -11,88 +12,67 @@ type TransformResult = {
|
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
const PATTERNS = [
|
|
15
|
+
const PATTERNS = [`${COMPILER_ENTRYPOINT}\``, `${COMPILER_ENTRYPOINT}.${COMPILER_ENTRYPOINT_REACTIVITY}`];
|
|
15
16
|
|
|
17
|
+
const REGEX_BACKSLASH = /\\/g;
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
let typeChecker = program.getTypeChecker();
|
|
19
|
+
const REGEX_FORWARD_SLASH = /\//g;
|
|
19
20
|
|
|
20
|
-
return (_context: ts.TransformationContext) => {
|
|
21
|
-
return (sourceFile: ts.SourceFile): ts.SourceFile => {
|
|
22
|
-
let code = sourceFile.getFullText();
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
setTypeChecker(typeChecker);
|
|
22
|
+
const transform = (sourceFile: ts.SourceFile, program: ts.Program): TransformResult => {
|
|
23
|
+
let code = sourceFile.getFullText();
|
|
29
24
|
|
|
30
|
-
|
|
25
|
+
if (!c.contains(code, { patterns: PATTERNS })) {
|
|
26
|
+
return { changed: false, code, sourceFile };
|
|
27
|
+
}
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
let checker: ts.TypeChecker | undefined,
|
|
30
|
+
fileName = sourceFile.fileName,
|
|
31
|
+
programSourceFile = program.getSourceFile(fileName)
|
|
32
|
+
|| program.getSourceFile(fileName.replace(REGEX_BACKSLASH, '/'))
|
|
33
|
+
|| program.getSourceFile(fileName.replace(REGEX_FORWARD_SLASH, '\\'));
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
35
|
+
if (programSourceFile) {
|
|
36
|
+
checker = program.getTypeChecker();
|
|
37
|
+
sourceFile = programSourceFile;
|
|
38
|
+
}
|
|
40
39
|
|
|
41
|
-
function transformCode(code: string, sourceFile: ts.SourceFile): TransformResult {
|
|
42
40
|
let changed = false,
|
|
41
|
+
codegenChanged = false,
|
|
42
|
+
needsImport = false,
|
|
43
|
+
reactiveCalls = findReactiveCalls(sourceFile),
|
|
43
44
|
result = code;
|
|
44
45
|
|
|
45
|
-
let reactiveCalls = findReactiveCalls(sourceFile);
|
|
46
|
-
|
|
47
46
|
if (reactiveCalls.length > 0) {
|
|
48
|
-
result = generateReactiveInlining(reactiveCalls, result, sourceFile);
|
|
49
47
|
changed = true;
|
|
48
|
+
checker = undefined;
|
|
49
|
+
result = generateReactiveInlining(reactiveCalls, result, sourceFile);
|
|
50
50
|
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
51
|
-
|
|
52
|
-
if (needsArraySlotImport(sourceFile)) {
|
|
53
|
-
result = addArraySlotImport(result);
|
|
54
|
-
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
setTypeChecker(undefined);
|
|
51
|
+
needsImport = needsArraySlotImport(sourceFile);
|
|
58
52
|
}
|
|
59
53
|
|
|
60
54
|
let templates = findHtmlTemplates(sourceFile);
|
|
61
55
|
|
|
62
56
|
if (templates.length > 0) {
|
|
63
|
-
let codegenResult = generateCode(templates, result, sourceFile);
|
|
57
|
+
let codegenResult = generateCode(templates, result, sourceFile, checker);
|
|
64
58
|
|
|
65
59
|
if (codegenResult.changed) {
|
|
66
60
|
changed = true;
|
|
61
|
+
codegenChanged = true;
|
|
67
62
|
result = codegenResult.code;
|
|
68
|
-
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
69
63
|
}
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const transform = (sourceFile: ts.SourceFile, program: ts.Program): TransformResult => {
|
|
77
|
-
let code = sourceFile.getFullText();
|
|
78
|
-
|
|
79
|
-
if (!mightNeedTransform(code, { patterns: PATTERNS })) {
|
|
80
|
-
return { changed: false, code, sourceFile };
|
|
66
|
+
if (needsImport && !codegenChanged) {
|
|
67
|
+
result = addArraySlotImport(result);
|
|
81
68
|
}
|
|
82
69
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (programSourceFile) {
|
|
86
|
-
setTypeChecker(program.getTypeChecker());
|
|
87
|
-
sourceFile = programSourceFile;
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
setTypeChecker(undefined);
|
|
70
|
+
if (changed) {
|
|
71
|
+
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
91
72
|
}
|
|
92
73
|
|
|
93
|
-
return
|
|
74
|
+
return { changed, code: result, sourceFile };
|
|
94
75
|
};
|
|
95
76
|
|
|
96
77
|
|
|
97
|
-
export
|
|
98
|
-
export { createTransformer, mightNeedTransform, PATTERNS, transform };
|
|
78
|
+
export { transform };
|