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