@esportsplus/template 0.38.2 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +5 -26
  2. package/build/compiler/codegen.d.ts +3 -2
  3. package/build/compiler/codegen.js +102 -142
  4. package/build/compiler/constants.d.ts +16 -0
  5. package/build/compiler/constants.js +19 -0
  6. package/build/compiler/index.d.ts +6 -3
  7. package/build/compiler/index.js +29 -38
  8. package/build/compiler/parser.d.ts +3 -3
  9. package/build/compiler/parser.js +5 -4
  10. package/build/compiler/plugins/tsc.d.ts +3 -2
  11. package/build/compiler/plugins/tsc.js +4 -2
  12. package/build/compiler/plugins/vite.js +4 -3
  13. package/build/compiler/{analyzer.d.ts → ts-analyzer.d.ts} +2 -2
  14. package/build/compiler/{analyzer.js → ts-analyzer.js} +16 -18
  15. package/build/compiler/ts-parser.d.ts +5 -1
  16. package/build/compiler/ts-parser.js +27 -45
  17. package/build/constants.d.ts +1 -16
  18. package/build/constants.js +1 -19
  19. package/package.json +7 -3
  20. package/src/compiler/codegen.ts +135 -217
  21. package/src/compiler/constants.ts +26 -0
  22. package/src/compiler/index.ts +33 -58
  23. package/src/compiler/parser.ts +7 -6
  24. package/src/compiler/plugins/tsc.ts +4 -2
  25. package/src/compiler/plugins/vite.ts +4 -3
  26. package/src/compiler/{analyzer.ts → ts-analyzer.ts} +17 -20
  27. package/src/compiler/ts-parser.ts +35 -67
  28. package/src/constants.ts +0 -25
  29. package/test/counter.ts +113 -0
  30. package/test/effects.ts +1 -1
  31. package/test/events.ts +1 -1
  32. package/test/imported-values.ts +1 -1
  33. package/test/integration/tsconfig.json +0 -1
  34. package/test/nested.ts +20 -1
  35. package/test/slots.ts +1 -1
  36. package/test/spread.ts +1 -1
  37. package/test/static.ts +1 -1
  38. package/test/templates.ts +1 -1
  39. package/test/vite.config.ts +2 -1
@@ -1,9 +1,11 @@
1
1
  import type { ReplacementIntent } from '@esportsplus/typescript/compiler';
2
- import { ts } from '@esportsplus/typescript';
3
- import { ast, uid } from '@esportsplus/typescript/compiler';
4
- import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '~/constants';
5
2
  import type { TemplateInfo } from './ts-parser';
6
- import { analyze } from './analyzer';
3
+ import { analyze } from './ts-analyzer';
4
+ import { ast, uid } from '@esportsplus/typescript/compiler';
5
+ import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../constants';
6
+ import { ENTRYPOINT, ENTRYPOINT_REACTIVITY, NAMESPACE, TYPES } from './constants';
7
+ import { extractTemplateParts } from './ts-parser';
8
+ import { ts } from '@esportsplus/typescript';
7
9
  import parser from './parser';
8
10
 
9
11
 
@@ -13,12 +15,11 @@ type Attribute = {
13
15
  statics: Record<string, string>;
14
16
  };
15
17
  path: string[];
16
- type: COMPILER_TYPES.Attribute;
18
+ type: TYPES.Attribute;
17
19
  };
18
20
 
19
21
  type CodegenContext = {
20
22
  checker?: ts.TypeChecker;
21
- printer: ts.Printer;
22
23
  sourceFile: ts.SourceFile;
23
24
  templates: Map<string, string>;
24
25
  };
@@ -31,7 +32,7 @@ type CodegenResult = {
31
32
 
32
33
  type Node = {
33
34
  path: string[];
34
- type: COMPILER_TYPES.Node;
35
+ type: TYPES.Node;
35
36
  };
36
37
 
37
38
  type ParseResult = {
@@ -39,43 +40,51 @@ type ParseResult = {
39
40
  slots: (Attribute | Node)[] | null;
40
41
  };
41
42
 
42
- type Replacement = {
43
- end: number;
44
- newText: string;
45
- start: number;
46
- };
47
-
48
-
49
- const ARROW_EMPTY_PARAMS = /\(\s*\)\s*=>\s*$/;
50
-
51
43
 
52
44
  let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
53
45
 
54
46
 
55
- function collectNestedReplacements(
56
- ctx: CodegenContext,
57
- node: ts.Node,
58
- exprStart: number,
59
- replacements: Replacement[]
60
- ): void {
47
+ function collectNestedReplacements(ctx: CodegenContext, node: ts.Node, replacements: { end: number; start: number; text: string }[]): void {
61
48
  if (isNestedHtmlTemplate(node as ts.Expression)) {
62
49
  replacements.push({
63
- end: node.end - exprStart,
64
- newText: generateNestedTemplateCode(ctx, node as ts.TaggedTemplateExpression),
65
- start: node.getStart(ctx.sourceFile) - exprStart
50
+ end: node.end,
51
+ start: node.getStart(ctx.sourceFile),
52
+ text: generateNestedTemplateCode(ctx, node as ts.TaggedTemplateExpression)
66
53
  });
54
+
55
+ return;
67
56
  }
68
- else if (isReactiveCall(node as ts.Expression)) {
69
- // Nested reactive calls need full ArraySlot construction (not just args)
57
+
58
+ if (isReactiveCall(node as ts.Expression)) {
59
+ let call = node as ts.CallExpression;
60
+
70
61
  replacements.push({
71
- end: node.end - exprStart,
72
- newText: rewriteNestedReactiveCall(ctx, node as ts.CallExpression),
73
- start: node.getStart(ctx.sourceFile) - exprStart
62
+ end: node.end,
63
+ start: node.getStart(ctx.sourceFile),
64
+ text: `new ${NAMESPACE}.ArraySlot(${rewriteExpression(ctx, call.arguments[0] as ts.Expression)}, ${rewriteExpression(ctx, call.arguments[1] as ts.Expression)})`
74
65
  });
66
+
67
+ return;
75
68
  }
76
- else {
77
- ts.forEachChild(node, child => collectNestedReplacements(ctx, child, exprStart, replacements));
69
+
70
+ ts.forEachChild(node, child => collectNestedReplacements(ctx, child, replacements));
71
+ }
72
+
73
+ function discoverTemplatesInExpression(ctx: CodegenContext, node: ts.Node): void {
74
+ if (isNestedHtmlTemplate(node as ts.Expression)) {
75
+ let { expressions, literals } = extractTemplateParts((node as ts.TaggedTemplateExpression).template),
76
+ parsed = parser.parse(literals) as ParseResult;
77
+
78
+ getOrCreateTemplateId(ctx, parsed.html);
79
+
80
+ for (let i = 0, n = expressions.length; i < n; i++) {
81
+ discoverTemplatesInExpression(ctx, expressions[i]);
82
+ }
83
+
84
+ return;
78
85
  }
86
+
87
+ ts.forEachChild(node, child => discoverTemplatesInExpression(ctx, child));
79
88
  }
80
89
 
81
90
  function generateAttributeBinding(element: string, name: string, expr: string, staticValue: string): string {
@@ -84,46 +93,33 @@ function generateAttributeBinding(element: string, name: string, expr: string, s
84
93
  key = name.toLowerCase();
85
94
 
86
95
  if (LIFECYCLE_EVENTS.has(key)) {
87
- return `${COMPILER_NAMESPACE}.${key}(${element}, ${expr});`;
96
+ return `${NAMESPACE}.${key}(${element}, ${expr});`;
88
97
  }
89
98
 
90
99
  if (DIRECT_ATTACH_EVENTS.has(key)) {
91
- return `${COMPILER_NAMESPACE}.on(${element}, '${event}', ${expr});`;
100
+ return `${NAMESPACE}.on(${element}, '${event}', ${expr});`;
92
101
  }
93
102
 
94
- return `${COMPILER_NAMESPACE}.delegate(${element}, '${event}', ${expr});`;
103
+ return `${NAMESPACE}.delegate(${element}, '${event}', ${expr});`;
95
104
  }
96
105
 
97
106
  if (name === 'class') {
98
- return `${COMPILER_NAMESPACE}.setClass(${element}, '${staticValue}', ${expr});`;
107
+ return `${NAMESPACE}.setClass(${element}, '${staticValue}', ${expr});`;
99
108
  }
100
109
 
101
110
  if (name === 'style') {
102
- return `${COMPILER_NAMESPACE}.setStyle(${element}, '${staticValue}', ${expr});`;
111
+ return `${NAMESPACE}.setStyle(${element}, '${staticValue}', ${expr});`;
103
112
  }
104
113
 
105
- return `${COMPILER_NAMESPACE}.setProperty(${element}, '${name}', ${expr});`;
114
+ return `${NAMESPACE}.setProperty(${element}, '${name}', ${expr});`;
106
115
  }
107
116
 
108
117
  function generateNestedTemplateCode(ctx: CodegenContext, node: ts.TaggedTemplateExpression): string {
109
- let expressions: ts.Expression[] = [],
110
- exprTexts: string[] = [],
111
- literals: string[] = [],
112
- template = node.template;
113
-
114
- if (ts.isNoSubstitutionTemplateLiteral(template)) {
115
- literals.push(template.text);
116
- }
117
- else if (ts.isTemplateExpression(template)) {
118
- literals.push(template.head.text);
118
+ let { expressions, literals } = extractTemplateParts(node.template),
119
+ exprTexts: string[] = [];
119
120
 
120
- for (let i = 0, n = template.templateSpans.length; i < n; i++) {
121
- let expr = template.templateSpans[i].expression;
122
-
123
- expressions.push(expr);
124
- literals.push(template.templateSpans[i].literal.text);
125
- exprTexts.push(rewriteExpression(ctx, expr));
126
- }
121
+ for (let i = 0, n = expressions.length; i < n; i++) {
122
+ exprTexts.push(rewriteExpression(ctx, expressions[i]));
127
123
  }
128
124
 
129
125
  return generateTemplateCode(
@@ -131,36 +127,34 @@ function generateNestedTemplateCode(ctx: CodegenContext, node: ts.TaggedTemplate
131
127
  parser.parse(literals) as ParseResult,
132
128
  exprTexts,
133
129
  expressions,
134
- false
130
+ node
135
131
  );
136
132
  }
137
133
 
138
134
  function generateNodeBinding(ctx: CodegenContext, anchor: string, exprText: string, exprNode: ts.Expression | undefined): string {
139
135
  if (!exprNode) {
140
- return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
136
+ return `${NAMESPACE}.slot(${anchor}, ${exprText});`;
141
137
  }
142
138
 
143
139
  if (isNestedHtmlTemplate(exprNode)) {
144
140
  return `${anchor}.parentNode!.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
145
141
  }
146
142
 
147
- let slotType = analyze(exprNode, ctx.checker);
148
-
149
- switch (slotType) {
150
- case COMPILER_TYPES.ArraySlot:
151
- return `${anchor}.parentNode!.insertBefore(new ${COMPILER_NAMESPACE}.ArraySlot(${exprText}).fragment, ${anchor});`;
143
+ switch (analyze(exprNode, ctx.checker)) {
144
+ case TYPES.ArraySlot:
145
+ return `${anchor}.parentNode!.insertBefore(new ${NAMESPACE}.ArraySlot(${exprText}).fragment, ${anchor});`;
152
146
 
153
- case COMPILER_TYPES.DocumentFragment:
147
+ case TYPES.DocumentFragment:
154
148
  return `${anchor}.parentNode!.insertBefore(${exprText}, ${anchor});`;
155
149
 
156
- case COMPILER_TYPES.Effect:
157
- return `new ${COMPILER_NAMESPACE}.EffectSlot(${anchor}, ${exprText});`;
150
+ case TYPES.Effect:
151
+ return `new ${NAMESPACE}.EffectSlot(${anchor}, ${exprText});`;
158
152
 
159
- case COMPILER_TYPES.Static:
153
+ case TYPES.Static:
160
154
  return `${anchor}.textContent = ${exprText};`;
161
155
 
162
156
  default:
163
- return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
157
+ return `${NAMESPACE}.slot(${anchor}, ${exprText});`;
164
158
  }
165
159
  }
166
160
 
@@ -169,7 +163,7 @@ function generateTemplateCode(
169
163
  { html, slots }: ParseResult,
170
164
  exprTexts: string[],
171
165
  exprNodes: ts.Expression[],
172
- isArrowBody: boolean
166
+ templateNode: ts.Node
173
167
  ): string {
174
168
  if (!slots || slots.length === 0) {
175
169
  return `${getOrCreateTemplateId(ctx, html)}()`;
@@ -178,6 +172,7 @@ function generateTemplateCode(
178
172
  let code: string[] = [],
179
173
  declarations: string[] = [],
180
174
  index = 0,
175
+ isArrowBody = isArrowExpressionBody(templateNode),
181
176
  nodes = new Map<string, string>(),
182
177
  root = uid('root');
183
178
 
@@ -215,17 +210,15 @@ function generateTemplateCode(
215
210
  value = `${ancestor}.${segments.join('!.')}`;
216
211
 
217
212
  if (ancestor === root && segments[0] === 'firstChild') {
218
- value = value.replace(`${ancestor}.firstChild!`, `(${ancestor}.firstChild! as ${COMPILER_NAMESPACE}.Element)`);
213
+ value = value.replace(`${ancestor}.firstChild!`, `(${root}.firstChild! as ${NAMESPACE}.Element)`);
219
214
  }
220
215
 
221
- declarations.push(`${name} = ${value} as ${COMPILER_NAMESPACE}.Element`);
216
+ declarations.push(`${name} = ${value} as ${NAMESPACE}.Element`);
222
217
  nodes.set(key, name);
223
218
  }
224
219
 
225
- code.push(
226
- isArrowBody ? '{' : `(() => {`,
227
- `let ${declarations.join(',\n')};`
228
- );
220
+ code.push(isArrowBody ? '{' : `(() => {`);
221
+ code.push(`let ${declarations.join(',\n')};`);
229
222
 
230
223
  for (let i = 0, n = slots.length; i < n; i++) {
231
224
  let element = slots[i].path.length === 0
@@ -233,15 +226,15 @@ function generateTemplateCode(
233
226
  : (nodes.get(slots[i].path.join('.')) || root),
234
227
  slot = slots[i];
235
228
 
236
- if (slot.type === COMPILER_TYPES.Attribute) {
229
+ if (slot.type === TYPES.Attribute) {
237
230
  let names = slot.attributes.names;
238
231
 
239
232
  for (let j = 0, m = names.length; j < m; j++) {
240
233
  let name = names[j];
241
234
 
242
- if (name === COMPILER_TYPES.Attributes) {
235
+ if (name === TYPES.Attributes) {
243
236
  code.push(
244
- `${COMPILER_NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`
237
+ `${NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`
245
238
  );
246
239
  index++;
247
240
  }
@@ -282,8 +275,12 @@ function getOrCreateTemplateId(ctx: CodegenContext, html: string): string {
282
275
  return id;
283
276
  }
284
277
 
278
+ function isArrowExpressionBody(node: ts.Node): boolean {
279
+ return ts.isArrowFunction(node.parent) && (node.parent as ts.ArrowFunction).body === node;
280
+ }
281
+
285
282
  function isNestedHtmlTemplate(expr: ts.Expression): expr is ts.TaggedTemplateExpression {
286
- return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
283
+ return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === ENTRYPOINT;
287
284
  }
288
285
 
289
286
  function isReactiveCall(expr: ts.Expression): expr is ts.CallExpression {
@@ -291,122 +288,48 @@ function isReactiveCall(expr: ts.Expression): expr is ts.CallExpression {
291
288
  ts.isCallExpression(expr) &&
292
289
  ts.isPropertyAccessExpression(expr.expression) &&
293
290
  ts.isIdentifier(expr.expression.expression) &&
294
- expr.expression.expression.text === COMPILER_ENTRYPOINT &&
295
- expr.expression.name.text === COMPILER_ENTRYPOINT_REACTIVITY
291
+ expr.expression.expression.text === ENTRYPOINT &&
292
+ expr.expression.name.text === ENTRYPOINT_REACTIVITY
296
293
  );
297
294
  }
298
295
 
299
- function replaceReverse(text: string, replacements: Replacement[]): string {
300
- let sorted = replacements.slice().sort((a, b) => b.start - a.start);
301
-
302
- for (let i = 0, n = sorted.length; i < n; i++) {
303
- let r = sorted[i];
304
-
305
- text = text.slice(0, r.start) + r.newText + text.slice(r.end);
306
- }
307
-
308
- return text;
309
- }
310
-
311
296
  function rewriteExpression(ctx: CodegenContext, expr: ts.Expression): string {
312
297
  if (isNestedHtmlTemplate(expr)) {
313
298
  return generateNestedTemplateCode(ctx, expr);
314
299
  }
315
300
 
316
301
  if (isReactiveCall(expr)) {
317
- return rewriteReactiveCall(ctx, expr);
302
+ return `${rewriteExpression(ctx, expr.arguments[0] as ts.Expression)}, ${rewriteExpression(ctx, expr.arguments[1] as ts.Expression)}`;
318
303
  }
319
304
 
320
- if (!ast.hasMatch(expr, n => isNestedHtmlTemplate(n as ts.Expression) || isReactiveCall(n as ts.Expression))) {
321
- return ctx.printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
305
+ if (!ast.test(expr, n => isNestedHtmlTemplate(n as ts.Expression) || isReactiveCall(n as ts.Expression))) {
306
+ return printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
322
307
  }
323
308
 
324
- let replacements: Replacement[] = [];
325
-
326
- collectNestedReplacements(ctx, expr, expr.getStart(ctx.sourceFile), replacements);
327
-
328
- return replaceReverse(expr.getText(ctx.sourceFile), replacements);
329
- }
330
-
331
- // Returns just args "array, callback" - for direct slot usage where generateNodeBinding wraps it
332
- function rewriteReactiveCall(ctx: CodegenContext, node: ts.CallExpression): string {
333
- let arrayArg = node.arguments[0],
334
- arrayText = ctx.printer.printNode(ts.EmitHint.Expression, arrayArg, ctx.sourceFile),
335
- callbackArg = node.arguments[1],
336
- callbackText = rewriteExpression(ctx, callbackArg as ts.Expression);
309
+ let exprStart = expr.getStart(ctx.sourceFile),
310
+ replacements: { end: number; start: number; text: string }[] = [],
311
+ text = expr.getText(ctx.sourceFile);
337
312
 
338
- return `${arrayText}, ${callbackText}`;
339
- }
340
-
341
- // Returns full "new ArraySlot(array, callback)" - for nested reactive calls inside expressions
342
- function rewriteNestedReactiveCall(ctx: CodegenContext, node: ts.CallExpression): string {
343
- let arrayArg = node.arguments[0],
344
- arrayText = ctx.printer.printNode(ts.EmitHint.Expression, arrayArg, ctx.sourceFile),
345
- callbackArg = node.arguments[1],
346
- callbackText = rewriteExpression(ctx, callbackArg as ts.Expression);
347
-
348
- return `new ${COMPILER_NAMESPACE}.ArraySlot(${arrayText}, ${callbackText})`;
349
- }
350
-
351
- // Eager discovery - walk all expressions to find templates before prepend generation
352
- function discoverTemplatesInExpression(ctx: CodegenContext, node: ts.Node): void {
353
- if (isNestedHtmlTemplate(node as ts.Expression)) {
354
- let template = node as ts.TaggedTemplateExpression,
355
- expressions: ts.Expression[] = [],
356
- literals: string[] = [],
357
- tpl = template.template;
358
-
359
- if (ts.isNoSubstitutionTemplateLiteral(tpl)) {
360
- literals.push(tpl.text);
361
- }
362
- else if (ts.isTemplateExpression(tpl)) {
363
- literals.push(tpl.head.text);
313
+ ts.forEachChild(expr, child => collectNestedReplacements(ctx, child, replacements));
364
314
 
365
- for (let i = 0, n = tpl.templateSpans.length; i < n; i++) {
366
- expressions.push(tpl.templateSpans[i].expression);
367
- literals.push(tpl.templateSpans[i].literal.text);
368
- }
369
- }
315
+ replacements.sort((a, b) => b.start - a.start);
370
316
 
371
- let parsed = parser.parse(literals) as ParseResult;
317
+ for (let i = 0, n = replacements.length; i < n; i++) {
318
+ let r = replacements[i];
372
319
 
373
- getOrCreateTemplateId(ctx, parsed.html);
374
-
375
- for (let i = 0, n = expressions.length; i < n; i++) {
376
- discoverTemplatesInExpression(ctx, expressions[i]);
377
- }
320
+ text = text.slice(0, r.start - exprStart) + r.text + text.slice(r.end - exprStart);
378
321
  }
379
- else if (isReactiveCall(node as ts.Expression)) {
380
- let call = node as ts.CallExpression;
381
-
382
- if (call.arguments.length >= 2) {
383
- discoverTemplatesInExpression(ctx, call.arguments[1]);
384
- }
385
- }
386
- else {
387
- ts.forEachChild(node, child => discoverTemplatesInExpression(ctx, child));
388
- }
389
- }
390
-
391
- function discoverAllTemplates(ctx: CodegenContext, templates: TemplateInfo[]): void {
392
- for (let i = 0, n = templates.length; i < n; i++) {
393
- let parsed = parser.parse(templates[i].literals) as ParseResult;
394
-
395
- getOrCreateTemplateId(ctx, parsed.html);
396
322
 
397
- for (let j = 0, m = templates[i].expressions.length; j < m; j++) {
398
- discoverTemplatesInExpression(ctx, templates[i].expressions[j]);
399
- }
400
- }
323
+ return text;
401
324
  }
402
325
 
403
326
 
404
327
  const generateCode = (templates: TemplateInfo[], sourceFile: ts.SourceFile, checker?: ts.TypeChecker): CodegenResult => {
405
328
  let result: CodegenResult = {
406
- prepend: [],
407
- replacements: [],
408
- templates: new Map()
409
- };
329
+ prepend: [],
330
+ replacements: [],
331
+ templates: new Map()
332
+ };
410
333
 
411
334
  if (templates.length === 0) {
412
335
  return result;
@@ -422,69 +345,64 @@ const generateCode = (templates: TemplateInfo[], sourceFile: ts.SourceFile, chec
422
345
  }
423
346
  }
424
347
 
425
- let rootTemplates = templates.filter(t => !ast.inRange(ranges, t.node.getStart(sourceFile), t.node.end));
348
+ let root = templates.filter(t => !ast.inRange(ranges, t.node.getStart(sourceFile), t.node.end));
426
349
 
427
- if (rootTemplates.length === 0) {
350
+ if (root.length === 0) {
428
351
  return result;
429
352
  }
430
353
 
431
354
  let ctx: CodegenContext = {
432
- checker,
433
- printer,
434
- sourceFile,
435
- templates: result.templates
436
- };
355
+ checker,
356
+ sourceFile,
357
+ templates: result.templates
358
+ };
359
+
360
+ for (let i = 0, n = root.length; i < n; i++) {
361
+ let exprTexts: string[] = [],
362
+ parsed = parser.parse(root[i].literals) as ParseResult,
363
+ template = root[i];
364
+
365
+ for (let j = 0, m = template.expressions.length; j < m; j++) {
366
+ exprTexts.push(rewriteExpression(ctx, template.expressions[j]));
367
+ }
437
368
 
438
- for (let i = 0, n = rootTemplates.length; i < n; i++) {
439
- let template = rootTemplates[i];
440
-
441
- result.replacements.push({
442
- generate: (sf) => {
443
- let codeBefore = sf.getFullText().slice(0, template.node.getStart(sf)),
444
- exprTexts: string[] = [],
445
- isArrowBody = codeBefore.trimEnd().endsWith('=>'),
446
- localCtx: CodegenContext = {
447
- checker,
448
- printer,
449
- sourceFile: sf,
450
- templates: ctx.templates
451
- },
452
- parsed = parser.parse(template.literals) as ParseResult;
453
-
454
- for (let j = 0, m = template.expressions.length; j < m; j++) {
455
- exprTexts.push(rewriteExpression(localCtx, template.expressions[j]));
456
- }
369
+ if (
370
+ isArrowExpressionBody(template.node) &&
371
+ (template.node.parent as ts.ArrowFunction).parameters.length === 0 &&
372
+ (!parsed.slots || parsed.slots.length === 0)
373
+ ) {
374
+ let code = getOrCreateTemplateId(ctx, parsed.html);
375
+
376
+ result.replacements.push({
377
+ generate: () => code,
378
+ node: template.node
379
+ });
380
+ }
381
+ else {
382
+ let code = generateTemplateCode(ctx, parsed, exprTexts, template.expressions, template.node);
457
383
 
458
- if (isArrowBody && (!parsed.slots || parsed.slots.length === 0)) {
459
- let arrowMatch = codeBefore.match(ARROW_EMPTY_PARAMS);
384
+ result.replacements.push({
385
+ generate: () => code,
386
+ node: template.node
387
+ });
388
+ }
389
+ }
460
390
 
461
- if (arrowMatch) {
462
- return getOrCreateTemplateId(localCtx, parsed.html);
463
- }
464
- }
391
+ for (let i = 0, n = templates.length; i < n; i++) {
392
+ getOrCreateTemplateId(ctx, parser.parse(templates[i].literals).html);
465
393
 
466
- return generateTemplateCode(
467
- localCtx,
468
- parsed,
469
- exprTexts,
470
- template.expressions,
471
- isArrowBody
472
- );
473
- },
474
- node: template.node
475
- });
394
+ for (let j = 0, m = templates[i].expressions.length; j < m; j++) {
395
+ discoverTemplatesInExpression(ctx, templates[i].expressions[j]);
396
+ }
476
397
  }
477
398
 
478
- // Eager discovery: find all templates before prepend generation
479
- discoverAllTemplates(ctx, templates);
480
-
481
399
  for (let [html, id] of ctx.templates) {
482
- result.prepend.push(`const ${id} = ${COMPILER_NAMESPACE}.template(\`${html}\`);`);
400
+ result.prepend.push(`const ${id} = ${NAMESPACE}.template(\`${html}\`);`);
483
401
  }
484
402
 
485
403
  return result;
486
404
  };
487
405
 
488
406
 
489
- export { generateCode };
407
+ export { generateCode, printer };
490
408
  export type { CodegenResult };
@@ -0,0 +1,26 @@
1
+ import { uid } from '@esportsplus/typescript/compiler';
2
+
3
+
4
+ const ENTRYPOINT = 'html';
5
+
6
+ const ENTRYPOINT_REACTIVITY = 'reactive';
7
+
8
+ const NAMESPACE = uid('template');
9
+
10
+ const PACKAGE = '@esportsplus/template';
11
+
12
+
13
+ const enum TYPES {
14
+ ArraySlot = 'array-slot',
15
+ Attributes = 'attributes',
16
+ Attribute = 'attribute',
17
+ DocumentFragment = 'document-fragment',
18
+ Effect = 'effect',
19
+ Node = 'node',
20
+ Primitive = 'primitive',
21
+ Static = 'static',
22
+ Unknown = 'unknown'
23
+ };
24
+
25
+
26
+ export { ENTRYPOINT, ENTRYPOINT_REACTIVITY, NAMESPACE, PACKAGE, TYPES };