@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.
Files changed (39) 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/slot/array.d.ts +2 -2
  6. package/build/slot/array.js +5 -4
  7. package/build/slot/render.js +3 -2
  8. package/build/transformer/codegen.d.ts +2 -8
  9. package/build/transformer/codegen.js +87 -138
  10. package/build/transformer/index.d.ts +2 -3
  11. package/build/transformer/index.js +32 -31
  12. package/build/transformer/parser.d.ts +3 -2
  13. package/build/transformer/parser.js +4 -4
  14. package/build/transformer/plugins/tsc.js +8 -2
  15. package/build/transformer/plugins/vite.js +7 -9
  16. package/build/transformer/ts-parser.d.ts +1 -2
  17. package/build/transformer/ts-parser.js +25 -34
  18. package/build/transformer/type-analyzer.d.ts +4 -5
  19. package/build/transformer/type-analyzer.js +61 -71
  20. package/build/types.d.ts +1 -1
  21. package/package.json +7 -7
  22. package/src/attributes.ts +1 -4
  23. package/src/constants.ts +42 -6
  24. package/src/html.ts +3 -3
  25. package/src/slot/array.ts +9 -6
  26. package/src/slot/render.ts +5 -2
  27. package/src/transformer/codegen.ts +113 -175
  28. package/src/transformer/index.ts +43 -40
  29. package/src/transformer/parser.ts +10 -7
  30. package/src/transformer/plugins/tsc.ts +10 -2
  31. package/src/transformer/plugins/vite.ts +10 -14
  32. package/src/transformer/ts-parser.ts +31 -44
  33. package/src/transformer/type-analyzer.ts +75 -93
  34. package/src/types.ts +1 -1
  35. package/test/vite.config.ts +1 -1
  36. package/build/event/constants.d.ts +0 -3
  37. package/build/event/constants.js +0 -13
  38. package/src/event/constants.ts +0 -16
  39. package/storage/rewrite-analysis-2026-01-04.md +0 -439
@@ -1,8 +1,14 @@
1
- import { addImport, applyReplacementsReverse, uid } from '@esportsplus/typescript/transformer';
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: 'attributes';
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: 'slot';
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, sourceFile),
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, 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}`);
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(node: ts.TaggedTemplateExpression, sourceFile: ts.SourceFile): string {
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(expr, sourceFile));
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, sourceFile: ts.SourceFile): string {
101
+ function generateNodeBinding(ctx: CodegenContext, anchor: string, exprText: string, exprNode: ts.Expression | undefined): string {
130
102
  if (!exprNode) {
131
- needsSlot = true;
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(exprNode, sourceFile)}, ${anchor});`;
107
+ return `${anchor}.parentNode.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
137
108
  }
138
109
 
139
- let slotType = analyzeExpression(exprNode, currentChecker);
110
+ let slotType = analyzeExpression(exprNode, ctx.checker);
140
111
 
141
112
  switch (slotType) {
142
- case 'effect':
143
- needsEffectSlot = true;
144
- return `new ${nameEffectSlot}(${anchor}, ${exprText});`;
113
+ case COMPILER_TYPES.Effect:
114
+ return `new ${COMPILER_NAMESPACE}.EffectSlot(${anchor}, ${exprText});`;
145
115
 
146
- case 'array-slot':
147
- needsArraySlot = true;
148
- return `new ${nameArraySlot}(${anchor}, ${exprText});`;
116
+ case COMPILER_TYPES.ArraySlot:
117
+ return `new ${COMPILER_NAMESPACE}.ArraySlot(${anchor}, ${exprText});`;
149
118
 
150
- case 'static':
119
+ case COMPILER_TYPES.Static:
151
120
  return `${anchor}.textContent = ${exprText};`;
152
121
 
153
- case 'document-fragment':
122
+ case COMPILER_TYPES.DocumentFragment:
154
123
  return `${anchor}.parentNode.insertBefore(${exprText}, ${anchor});`;
155
124
 
156
125
  default:
157
- needsSlot = true;
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 === 'attributes') {
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
- currentChecker
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], sourceFile));
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 hasArraySlotUsage(node: ts.Node): boolean {
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 && hasArraySlotUsage(child)) {
320
- found = true;
283
+ if (!found) {
284
+ found = hasMatch(child, predicate);
321
285
  }
322
286
  });
323
287
 
324
288
  return found;
325
289
  }
326
290
 
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;
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
- return found;
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 === 'html';
304
+ return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
345
305
  }
346
306
 
347
- function isNestedTemplate(template: TemplateInfo, allTemplates: TemplateInfo[]): boolean {
348
- for (let i = 0, n = allTemplates.length; i < n; i++) {
349
- let other = allTemplates[i];
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
- if (template.start >= expr.getStart() && template.end <= expr.end) {
359
- return true;
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(expr: ts.Expression, sourceFile: ts.SourceFile): string {
319
+ function rewriteExpression(ctx: CodegenContext, expr: ts.Expression): string {
368
320
  if (isNestedHtmlTemplate(expr)) {
369
- return generateNestedTemplateCode(expr, sourceFile);
321
+ return generateNestedTemplateCode(ctx, expr);
370
322
  }
371
323
 
372
324
  if (!hasNestedTemplates(expr)) {
373
- return ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }).printNode(ts.EmitHint.Expression, expr, sourceFile);
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, sourceFile, replacements);
331
+ collectNestedTemplateReplacements(ctx, expr, exprStart, replacements);
380
332
 
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
- }
333
+ return c.replaceReverse(expr.getText(ctx.sourceFile), replacements);
391
334
  }
392
335
 
393
336
 
394
337
  const addArraySlotImport = (code: string): string => {
395
- return addImport(code, '@esportsplus/template', ['ArraySlot']);
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
- 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));
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 replacements: Replacement[] = [];
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], sourceFile));
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 = applyReplacementsReverse(originalCode, replacements);
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} = ${nameTemplate}(\`${html}\`);`);
417
+ for (let [id, html] of ctx.hoistedFactories) {
418
+ factories.push(`const ${id} = ${COMPILER_NAMESPACE}.template(\`${html}\`);`);
472
419
  }
473
420
 
474
- code = generateImports() + '\n\n' + factories.join('\n') + '\n\n' + 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 printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }),
486
- result = code;
432
+ let replacements: Replacement[] = [];
487
433
 
488
- for (let i = calls.length - 1; i >= 0; i--) {
434
+ for (let i = 0, n = calls.length; i < n; i++) {
489
435
  let call = calls[i];
490
436
 
491
- result = result.slice(0, call.start);
492
- result += `new ${nameArraySlot}(
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
- result += result.slice(call.end);
442
+ )`,
443
+ start: call.start
444
+ });
497
445
  }
498
446
 
499
- return result;
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, getNames, needsArraySlotImport, setTypeChecker };
455
+ export { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport };
518
456
  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,7 +12,7 @@ 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
 
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 (!mightNeedTransform(code, { patterns: PATTERNS })) {
25
+ if (!c.contains(code, { patterns: PATTERNS })) {
25
26
  return sourceFile;
26
27
  }
27
28
 
28
- setTypeChecker(typeChecker);
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
- let reactiveCalls = findReactiveCalls(sourceFile);
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
- if (needsArraySlotImport(sourceFile)) {
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
- 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 };
89
+ if (needsImport && !codegenChanged) {
90
+ result = addArraySlotImport(result);
81
91
  }
82
92
 
83
- let programSourceFile = program.getSourceFile(sourceFile.fileName);
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 transformCode(code, sourceFile);
97
+ return { changed, code: result, sourceFile };
94
98
  };
95
99
 
96
100
 
97
- export type { TransformResult };
98
- export { createTransformer, mightNeedTransform, PATTERNS, transform };
101
+ export { createTransformer, transform, transformCode, PATTERNS };