@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.
Files changed (44) hide show
  1. package/build/attributes.js +1 -2
  2. package/build/constants.d.ts +18 -3
  3. package/build/constants.js +31 -4
  4. package/build/html.d.ts +3 -3
  5. package/build/index.d.ts +3 -3
  6. package/build/index.js +3 -3
  7. package/build/slot/array.d.ts +2 -2
  8. package/build/slot/array.js +5 -4
  9. package/build/slot/render.js +3 -2
  10. package/build/transformer/codegen.d.ts +3 -9
  11. package/build/transformer/codegen.js +90 -147
  12. package/build/transformer/index.d.ts +1 -5
  13. package/build/transformer/index.js +30 -46
  14. package/build/transformer/parser.d.ts +3 -2
  15. package/build/transformer/parser.js +4 -4
  16. package/build/transformer/plugins/tsc.d.ts +2 -2
  17. package/build/transformer/plugins/tsc.js +3 -4
  18. package/build/transformer/plugins/vite.d.ts +11 -3
  19. package/build/transformer/plugins/vite.js +7 -37
  20. package/build/transformer/ts-parser.d.ts +1 -2
  21. package/build/transformer/ts-parser.js +28 -41
  22. package/build/transformer/type-analyzer.d.ts +4 -5
  23. package/build/transformer/type-analyzer.js +73 -118
  24. package/build/types.d.ts +1 -1
  25. package/package.json +7 -7
  26. package/src/attributes.ts +1 -4
  27. package/src/constants.ts +42 -6
  28. package/src/html.ts +3 -3
  29. package/src/index.ts +5 -3
  30. package/src/slot/array.ts +9 -6
  31. package/src/slot/render.ts +5 -2
  32. package/src/transformer/codegen.ts +119 -189
  33. package/src/transformer/index.ts +34 -54
  34. package/src/transformer/parser.ts +10 -7
  35. package/src/transformer/plugins/tsc.ts +3 -5
  36. package/src/transformer/plugins/vite.ts +7 -47
  37. package/src/transformer/ts-parser.ts +34 -54
  38. package/src/transformer/type-analyzer.ts +90 -158
  39. package/src/types.ts +1 -1
  40. package/test/vite.config.ts +1 -1
  41. package/build/event/constants.d.ts +0 -3
  42. package/build/event/constants.js +0 -13
  43. package/src/event/constants.ts +0 -16
  44. package/storage/rewrite-analysis-2026-01-04.md +0 -439
@@ -1,8 +1,11 @@
1
- import { addImport, applyReplacementsReverse, uid } from '@esportsplus/typescript/transformer';
2
- import type { Replacement } from '@esportsplus/typescript/transformer';
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 { ts } from '@esportsplus/typescript';
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: 'attributes';
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: 'slot';
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 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;
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, sourceFile),
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, sourceFile, replacements));
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(node: ts.TaggedTemplateExpression, sourceFile: ts.SourceFile): string {
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(expr, sourceFile));
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, sourceFile: ts.SourceFile): string {
99
+ function generateNodeBinding(ctx: CodegenContext, anchor: string, exprText: string, exprNode: ts.Expression | undefined): string {
130
100
  if (!exprNode) {
131
- needsSlot = true;
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(exprNode, sourceFile)}, ${anchor});`;
105
+ return `${anchor}.parentNode.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
137
106
  }
138
107
 
139
- let slotType = analyzeExpression(exprNode, currentChecker);
108
+ let slotType = analyzeExpression(exprNode, ctx.checker);
140
109
 
141
110
  switch (slotType) {
142
- case 'effect':
143
- needsEffectSlot = true;
144
- return `new ${nameEffectSlot}(${anchor}, ${exprText});`;
111
+ case COMPILER_TYPES.Effect:
112
+ return `new ${COMPILER_NAMESPACE}.EffectSlot(${anchor}, ${exprText});`;
145
113
 
146
- case 'array-slot':
147
- needsArraySlot = true;
148
- return `new ${nameArraySlot}(${anchor}, ${exprText});`;
114
+ case COMPILER_TYPES.ArraySlot:
115
+ return `new ${COMPILER_NAMESPACE}.ArraySlot(${anchor}, ${exprText});`;
149
116
 
150
- case 'static':
117
+ case COMPILER_TYPES.Static:
151
118
  return `${anchor}.textContent = ${exprText};`;
152
119
 
153
- case 'document-fragment':
120
+ case COMPILER_TYPES.DocumentFragment:
154
121
  return `${anchor}.parentNode.insertBefore(${exprText}, ${anchor});`;
155
122
 
156
123
  default:
157
- needsSlot = true;
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 === 'attributes') {
227
- for (let j = 0, m = slot.attributes.names.length; j < m; j++) {
228
- let name = slot.attributes.names[j];
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
- sourceFile,
236
- currentChecker
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
- let binding = generateAttributeBinding(
214
+ code.push(
215
+ generateAttributeBinding(
248
216
  elementVar,
249
217
  name,
250
218
  exprTexts[index++] || 'undefined',
251
- slot.attributes.statics[name] || ''
252
- );
253
-
254
- trackBindingUsage(binding);
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(generateNodeBinding(elementVar, exprTexts[index] || 'undefined', exprNodes[index], sourceFile));
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 hasArraySlotUsage(node: ts.Node): boolean {
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 && hasArraySlotUsage(child)) {
320
- found = true;
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 === 'html';
293
+ return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
345
294
  }
346
295
 
347
- function isNestedTemplate(template: TemplateInfo, allTemplates: TemplateInfo[]): boolean {
348
- for (let i = 0, n = allTemplates.length; i < n; i++) {
349
- let other = allTemplates[i];
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 (other === template) {
352
- continue;
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(expr: ts.Expression, sourceFile: ts.SourceFile): string {
308
+ function rewriteExpression(ctx: CodegenContext, expr: ts.Expression): string {
368
309
  if (isNestedHtmlTemplate(expr)) {
369
- return generateNestedTemplateCode(expr, sourceFile);
310
+ return generateNestedTemplateCode(ctx, expr);
370
311
  }
371
312
 
372
- if (!hasNestedTemplates(expr)) {
373
- return ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }).printNode(ts.EmitHint.Expression, expr, sourceFile);
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 exprStart = expr.getStart(),
377
- replacements: Replacement[] = [];
317
+ let replacements: Replacement[] = [];
378
318
 
379
- collectNestedTemplateReplacements(expr, exprStart, sourceFile, replacements);
319
+ collectNestedTemplateReplacements(ctx, expr, expr.getStart(), replacements);
380
320
 
381
- return applyReplacementsReverse(expr.getText(sourceFile), replacements);
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 addImport(code, '@esportsplus/template', ['ArraySlot']);
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
- hoistedFactories.clear();
404
- htmlToTemplateId.clear();
405
- nameArraySlot = uid('ArraySlot');
406
- nameAttr = uid('attr');
407
- nameEffectSlot = uid('EffectSlot');
408
- nameEvent = uid('event');
409
- nameSlot = uid('slot');
410
- nameTemplate = uid('template');
411
- needsArraySlot = false;
412
- needsAttr = false;
413
- needsEffectSlot = false;
414
- needsEvent = false;
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 replacements: Replacement[] = [];
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], sourceFile));
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 = applyReplacementsReverse(originalCode, replacements);
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} = ${nameTemplate}(\`${html}\`);`);
405
+ for (let [id, html] of ctx.hoistedFactories) {
406
+ factories.push(`const ${id} = ${COMPILER_NAMESPACE}.template(\`${html}\`);`);
472
407
  }
473
408
 
474
- code = generateImports() + '\n\n' + factories.join('\n') + '\n\n' + 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 printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }),
486
- result = code;
420
+ let replacements: Replacement[] = [];
487
421
 
488
- for (let i = calls.length - 1; i >= 0; i--) {
422
+ for (let i = 0, n = calls.length; i < n; i++) {
489
423
  let call = calls[i];
490
424
 
491
- result = result.slice(0, call.start);
492
- result += `new ${nameArraySlot}(
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
- result += result.slice(call.end);
430
+ )`,
431
+ start: call.start
432
+ });
497
433
  }
498
434
 
499
- return result;
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 hasArraySlotUsage(sourceFile) && !hasArraySlotImport(sourceFile);
510
- };
511
-
512
- const setTypeChecker = (checker: ts.TypeChecker | undefined): void => {
513
- currentChecker = checker;
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, getNames, needsArraySlotImport, setTypeChecker };
447
+ export { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport };
518
448
  export type { CodegenResult };
@@ -1,5 +1,6 @@
1
- import { mightNeedTransform } from '@esportsplus/typescript/transformer';
2
- import { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport, setTypeChecker } from './codegen';
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 = ['html`', 'html.reactive'];
15
+ const PATTERNS = [`${COMPILER_ENTRYPOINT}\``, `${COMPILER_ENTRYPOINT}.${COMPILER_ENTRYPOINT_REACTIVITY}`];
15
16
 
17
+ const REGEX_BACKSLASH = /\\/g;
16
18
 
17
- function createTransformer(program: ts.Program): ts.TransformerFactory<ts.SourceFile> {
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
- if (!mightNeedTransform(code, { patterns: PATTERNS })) {
25
- return sourceFile;
26
- }
27
-
28
- setTypeChecker(typeChecker);
22
+ const transform = (sourceFile: ts.SourceFile, program: ts.Program): TransformResult => {
23
+ let code = sourceFile.getFullText();
29
24
 
30
- let result = transformCode(code, sourceFile);
25
+ if (!c.contains(code, { patterns: PATTERNS })) {
26
+ return { changed: false, code, sourceFile };
27
+ }
31
28
 
32
- if (!result.changed) {
33
- return sourceFile;
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
- return result.sourceFile;
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
- return { changed, code: result, sourceFile };
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
- let programSourceFile = program.getSourceFile(sourceFile.fileName);
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 transformCode(code, sourceFile);
74
+ return { changed, code: result, sourceFile };
94
75
  };
95
76
 
96
77
 
97
- export type { TransformResult };
98
- export { createTransformer, mightNeedTransform, PATTERNS, transform };
78
+ export { transform };