@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
package/README.md CHANGED
@@ -26,50 +26,28 @@ The library requires a build-time transformer to convert template literals into
26
26
  ```typescript
27
27
  // vite.config.ts
28
28
  import { defineConfig } from 'vite';
29
- import templatePlugin from '@esportsplus/template/plugins/vite';
29
+ import template from '@esportsplus/template/compiler/vite';
30
30
 
31
31
  export default defineConfig({
32
- plugins: [
33
- templatePlugin()
34
- ]
32
+ plugins: [template]
35
33
  });
36
34
  ```
37
35
 
38
- **Options:**
39
-
40
- ```typescript
41
- templatePlugin({
42
- root: string // Optional: project root (defaults to config.root)
43
- })
44
- ```
45
-
46
36
  ### TypeScript Compiler (tsc)
47
37
 
48
- For direct `tsc` compilation, use the transformer factory:
38
+ For direct `tsc` compilation, use the transformer:
49
39
 
50
40
  ```typescript
51
41
  // tsconfig.json (with ts-patch or ttypescript)
52
42
  {
53
43
  "compilerOptions": {
54
44
  "plugins": [
55
- { "transform": "@esportsplus/template/plugins/tsc" }
45
+ { "transform": "@esportsplus/template/compiler/tsc" }
56
46
  ]
57
47
  }
58
48
  }
59
49
  ```
60
50
 
61
- Or programmatically:
62
-
63
- ```typescript
64
- import { ts } from '@esportsplus/typescript';
65
- import templateTransformer from '@esportsplus/template/plugins/tsc';
66
-
67
- const program = ts.createProgram(['src/index.ts'], {});
68
- const result = ts.emit(program, undefined, undefined, false, {
69
- before: [templateTransformer(program)]
70
- });
71
- ```
72
-
73
51
  ## Basic Usage
74
52
 
75
53
  ```typescript
@@ -399,6 +377,7 @@ Key optimizations:
399
377
 
400
378
  - `@esportsplus/reactivity` - Reactive primitives
401
379
  - `@esportsplus/queue` - Object pooling
380
+ - `@esportsplus/typescript` - TypeScript compiler utilities
402
381
  - `@esportsplus/utilities` - Utility functions
403
382
 
404
383
  ## License
@@ -1,11 +1,12 @@
1
1
  import type { ReplacementIntent } from '@esportsplus/typescript/compiler';
2
- import { ts } from '@esportsplus/typescript';
3
2
  import type { TemplateInfo } from './ts-parser.js';
3
+ import { ts } from '@esportsplus/typescript';
4
4
  type CodegenResult = {
5
5
  prepend: string[];
6
6
  replacements: ReplacementIntent[];
7
7
  templates: Map<string, string>;
8
8
  };
9
+ declare let printer: ts.Printer;
9
10
  declare const generateCode: (templates: TemplateInfo[], sourceFile: ts.SourceFile, checker?: ts.TypeChecker) => CodegenResult;
10
- export { generateCode };
11
+ export { generateCode, printer };
11
12
  export type { CodegenResult };
@@ -1,90 +1,93 @@
1
- import { ts } from '@esportsplus/typescript';
1
+ import { analyze } from './ts-analyzer.js';
2
2
  import { ast, uid } from '@esportsplus/typescript/compiler';
3
- import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../constants.js';
4
- import { analyze } from './analyzer.js';
3
+ import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../constants.js';
4
+ import { ENTRYPOINT, ENTRYPOINT_REACTIVITY, NAMESPACE, TYPES } from './constants.js';
5
+ import { extractTemplateParts } from './ts-parser.js';
6
+ import { ts } from '@esportsplus/typescript';
5
7
  import parser from './parser.js';
6
- const ARROW_EMPTY_PARAMS = /\(\s*\)\s*=>\s*$/;
7
8
  let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
8
- function collectNestedReplacements(ctx, node, exprStart, replacements) {
9
+ function collectNestedReplacements(ctx, node, replacements) {
9
10
  if (isNestedHtmlTemplate(node)) {
10
11
  replacements.push({
11
- end: node.end - exprStart,
12
- newText: generateNestedTemplateCode(ctx, node),
13
- start: node.getStart(ctx.sourceFile) - exprStart
12
+ end: node.end,
13
+ start: node.getStart(ctx.sourceFile),
14
+ text: generateNestedTemplateCode(ctx, node)
14
15
  });
16
+ return;
15
17
  }
16
- else if (isReactiveCall(node)) {
18
+ if (isReactiveCall(node)) {
19
+ let call = node;
17
20
  replacements.push({
18
- end: node.end - exprStart,
19
- newText: rewriteNestedReactiveCall(ctx, node),
20
- start: node.getStart(ctx.sourceFile) - exprStart
21
+ end: node.end,
22
+ start: node.getStart(ctx.sourceFile),
23
+ text: `new ${NAMESPACE}.ArraySlot(${rewriteExpression(ctx, call.arguments[0])}, ${rewriteExpression(ctx, call.arguments[1])})`
21
24
  });
25
+ return;
22
26
  }
23
- else {
24
- ts.forEachChild(node, child => collectNestedReplacements(ctx, child, exprStart, replacements));
27
+ ts.forEachChild(node, child => collectNestedReplacements(ctx, child, replacements));
28
+ }
29
+ function discoverTemplatesInExpression(ctx, node) {
30
+ if (isNestedHtmlTemplate(node)) {
31
+ let { expressions, literals } = extractTemplateParts(node.template), parsed = parser.parse(literals);
32
+ getOrCreateTemplateId(ctx, parsed.html);
33
+ for (let i = 0, n = expressions.length; i < n; i++) {
34
+ discoverTemplatesInExpression(ctx, expressions[i]);
35
+ }
36
+ return;
25
37
  }
38
+ ts.forEachChild(node, child => discoverTemplatesInExpression(ctx, child));
26
39
  }
27
40
  function generateAttributeBinding(element, name, expr, staticValue) {
28
41
  if (name.startsWith('on') && name.length > 2) {
29
42
  let event = name.slice(2).toLowerCase(), key = name.toLowerCase();
30
43
  if (LIFECYCLE_EVENTS.has(key)) {
31
- return `${COMPILER_NAMESPACE}.${key}(${element}, ${expr});`;
44
+ return `${NAMESPACE}.${key}(${element}, ${expr});`;
32
45
  }
33
46
  if (DIRECT_ATTACH_EVENTS.has(key)) {
34
- return `${COMPILER_NAMESPACE}.on(${element}, '${event}', ${expr});`;
47
+ return `${NAMESPACE}.on(${element}, '${event}', ${expr});`;
35
48
  }
36
- return `${COMPILER_NAMESPACE}.delegate(${element}, '${event}', ${expr});`;
49
+ return `${NAMESPACE}.delegate(${element}, '${event}', ${expr});`;
37
50
  }
38
51
  if (name === 'class') {
39
- return `${COMPILER_NAMESPACE}.setClass(${element}, '${staticValue}', ${expr});`;
52
+ return `${NAMESPACE}.setClass(${element}, '${staticValue}', ${expr});`;
40
53
  }
41
54
  if (name === 'style') {
42
- return `${COMPILER_NAMESPACE}.setStyle(${element}, '${staticValue}', ${expr});`;
55
+ return `${NAMESPACE}.setStyle(${element}, '${staticValue}', ${expr});`;
43
56
  }
44
- return `${COMPILER_NAMESPACE}.setProperty(${element}, '${name}', ${expr});`;
57
+ return `${NAMESPACE}.setProperty(${element}, '${name}', ${expr});`;
45
58
  }
46
59
  function generateNestedTemplateCode(ctx, node) {
47
- let expressions = [], exprTexts = [], literals = [], template = node.template;
48
- if (ts.isNoSubstitutionTemplateLiteral(template)) {
49
- literals.push(template.text);
60
+ let { expressions, literals } = extractTemplateParts(node.template), exprTexts = [];
61
+ for (let i = 0, n = expressions.length; i < n; i++) {
62
+ exprTexts.push(rewriteExpression(ctx, expressions[i]));
50
63
  }
51
- else if (ts.isTemplateExpression(template)) {
52
- literals.push(template.head.text);
53
- for (let i = 0, n = template.templateSpans.length; i < n; i++) {
54
- let expr = template.templateSpans[i].expression;
55
- expressions.push(expr);
56
- literals.push(template.templateSpans[i].literal.text);
57
- exprTexts.push(rewriteExpression(ctx, expr));
58
- }
59
- }
60
- return generateTemplateCode(ctx, parser.parse(literals), exprTexts, expressions, false);
64
+ return generateTemplateCode(ctx, parser.parse(literals), exprTexts, expressions, node);
61
65
  }
62
66
  function generateNodeBinding(ctx, anchor, exprText, exprNode) {
63
67
  if (!exprNode) {
64
- return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
68
+ return `${NAMESPACE}.slot(${anchor}, ${exprText});`;
65
69
  }
66
70
  if (isNestedHtmlTemplate(exprNode)) {
67
71
  return `${anchor}.parentNode!.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
68
72
  }
69
- let slotType = analyze(exprNode, ctx.checker);
70
- switch (slotType) {
71
- case COMPILER_TYPES.ArraySlot:
72
- return `${anchor}.parentNode!.insertBefore(new ${COMPILER_NAMESPACE}.ArraySlot(${exprText}).fragment, ${anchor});`;
73
- case COMPILER_TYPES.DocumentFragment:
73
+ switch (analyze(exprNode, ctx.checker)) {
74
+ case TYPES.ArraySlot:
75
+ return `${anchor}.parentNode!.insertBefore(new ${NAMESPACE}.ArraySlot(${exprText}).fragment, ${anchor});`;
76
+ case TYPES.DocumentFragment:
74
77
  return `${anchor}.parentNode!.insertBefore(${exprText}, ${anchor});`;
75
- case COMPILER_TYPES.Effect:
76
- return `new ${COMPILER_NAMESPACE}.EffectSlot(${anchor}, ${exprText});`;
77
- case COMPILER_TYPES.Static:
78
+ case TYPES.Effect:
79
+ return `new ${NAMESPACE}.EffectSlot(${anchor}, ${exprText});`;
80
+ case TYPES.Static:
78
81
  return `${anchor}.textContent = ${exprText};`;
79
82
  default:
80
- return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
83
+ return `${NAMESPACE}.slot(${anchor}, ${exprText});`;
81
84
  }
82
85
  }
83
- function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArrowBody) {
86
+ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, templateNode) {
84
87
  if (!slots || slots.length === 0) {
85
88
  return `${getOrCreateTemplateId(ctx, html)}()`;
86
89
  }
87
- let code = [], declarations = [], index = 0, nodes = new Map(), root = uid('root');
90
+ let code = [], declarations = [], index = 0, isArrowBody = isArrowExpressionBody(templateNode), nodes = new Map(), root = uid('root');
88
91
  declarations.push(`${root} = ${getOrCreateTemplateId(ctx, html)}()`);
89
92
  nodes.set('', root);
90
93
  for (let i = 0, n = slots.length; i < n; i++) {
@@ -107,22 +110,23 @@ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArro
107
110
  }
108
111
  let name = uid('element'), segments = path.slice(start), value = `${ancestor}.${segments.join('!.')}`;
109
112
  if (ancestor === root && segments[0] === 'firstChild') {
110
- value = value.replace(`${ancestor}.firstChild!`, `(${ancestor}.firstChild! as ${COMPILER_NAMESPACE}.Element)`);
113
+ value = value.replace(`${ancestor}.firstChild!`, `(${root}.firstChild! as ${NAMESPACE}.Element)`);
111
114
  }
112
- declarations.push(`${name} = ${value} as ${COMPILER_NAMESPACE}.Element`);
115
+ declarations.push(`${name} = ${value} as ${NAMESPACE}.Element`);
113
116
  nodes.set(key, name);
114
117
  }
115
- code.push(isArrowBody ? '{' : `(() => {`, `let ${declarations.join(',\n')};`);
118
+ code.push(isArrowBody ? '{' : `(() => {`);
119
+ code.push(`let ${declarations.join(',\n')};`);
116
120
  for (let i = 0, n = slots.length; i < n; i++) {
117
121
  let element = slots[i].path.length === 0
118
122
  ? root
119
123
  : (nodes.get(slots[i].path.join('.')) || root), slot = slots[i];
120
- if (slot.type === COMPILER_TYPES.Attribute) {
124
+ if (slot.type === TYPES.Attribute) {
121
125
  let names = slot.attributes.names;
122
126
  for (let j = 0, m = names.length; j < m; j++) {
123
127
  let name = names[j];
124
- if (name === COMPILER_TYPES.Attributes) {
125
- code.push(`${COMPILER_NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`);
128
+ if (name === TYPES.Attributes) {
129
+ code.push(`${NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`);
126
130
  index++;
127
131
  }
128
132
  else {
@@ -147,83 +151,37 @@ function getOrCreateTemplateId(ctx, html) {
147
151
  }
148
152
  return id;
149
153
  }
154
+ function isArrowExpressionBody(node) {
155
+ return ts.isArrowFunction(node.parent) && node.parent.body === node;
156
+ }
150
157
  function isNestedHtmlTemplate(expr) {
151
- return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
158
+ return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === ENTRYPOINT;
152
159
  }
153
160
  function isReactiveCall(expr) {
154
161
  return (ts.isCallExpression(expr) &&
155
162
  ts.isPropertyAccessExpression(expr.expression) &&
156
163
  ts.isIdentifier(expr.expression.expression) &&
157
- expr.expression.expression.text === COMPILER_ENTRYPOINT &&
158
- expr.expression.name.text === COMPILER_ENTRYPOINT_REACTIVITY);
159
- }
160
- function replaceReverse(text, replacements) {
161
- let sorted = replacements.slice().sort((a, b) => b.start - a.start);
162
- for (let i = 0, n = sorted.length; i < n; i++) {
163
- let r = sorted[i];
164
- text = text.slice(0, r.start) + r.newText + text.slice(r.end);
165
- }
166
- return text;
164
+ expr.expression.expression.text === ENTRYPOINT &&
165
+ expr.expression.name.text === ENTRYPOINT_REACTIVITY);
167
166
  }
168
167
  function rewriteExpression(ctx, expr) {
169
168
  if (isNestedHtmlTemplate(expr)) {
170
169
  return generateNestedTemplateCode(ctx, expr);
171
170
  }
172
171
  if (isReactiveCall(expr)) {
173
- return rewriteReactiveCall(ctx, expr);
174
- }
175
- if (!ast.hasMatch(expr, n => isNestedHtmlTemplate(n) || isReactiveCall(n))) {
176
- return ctx.printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
177
- }
178
- let replacements = [];
179
- collectNestedReplacements(ctx, expr, expr.getStart(ctx.sourceFile), replacements);
180
- return replaceReverse(expr.getText(ctx.sourceFile), replacements);
181
- }
182
- function rewriteReactiveCall(ctx, node) {
183
- let arrayArg = node.arguments[0], arrayText = ctx.printer.printNode(ts.EmitHint.Expression, arrayArg, ctx.sourceFile), callbackArg = node.arguments[1], callbackText = rewriteExpression(ctx, callbackArg);
184
- return `${arrayText}, ${callbackText}`;
185
- }
186
- function rewriteNestedReactiveCall(ctx, node) {
187
- let arrayArg = node.arguments[0], arrayText = ctx.printer.printNode(ts.EmitHint.Expression, arrayArg, ctx.sourceFile), callbackArg = node.arguments[1], callbackText = rewriteExpression(ctx, callbackArg);
188
- return `new ${COMPILER_NAMESPACE}.ArraySlot(${arrayText}, ${callbackText})`;
189
- }
190
- function discoverTemplatesInExpression(ctx, node) {
191
- if (isNestedHtmlTemplate(node)) {
192
- let template = node, expressions = [], literals = [], tpl = template.template;
193
- if (ts.isNoSubstitutionTemplateLiteral(tpl)) {
194
- literals.push(tpl.text);
195
- }
196
- else if (ts.isTemplateExpression(tpl)) {
197
- literals.push(tpl.head.text);
198
- for (let i = 0, n = tpl.templateSpans.length; i < n; i++) {
199
- expressions.push(tpl.templateSpans[i].expression);
200
- literals.push(tpl.templateSpans[i].literal.text);
201
- }
202
- }
203
- let parsed = parser.parse(literals);
204
- getOrCreateTemplateId(ctx, parsed.html);
205
- for (let i = 0, n = expressions.length; i < n; i++) {
206
- discoverTemplatesInExpression(ctx, expressions[i]);
207
- }
208
- }
209
- else if (isReactiveCall(node)) {
210
- let call = node;
211
- if (call.arguments.length >= 2) {
212
- discoverTemplatesInExpression(ctx, call.arguments[1]);
213
- }
172
+ return `${rewriteExpression(ctx, expr.arguments[0])}, ${rewriteExpression(ctx, expr.arguments[1])}`;
214
173
  }
215
- else {
216
- ts.forEachChild(node, child => discoverTemplatesInExpression(ctx, child));
174
+ if (!ast.test(expr, n => isNestedHtmlTemplate(n) || isReactiveCall(n))) {
175
+ return printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
217
176
  }
218
- }
219
- function discoverAllTemplates(ctx, templates) {
220
- for (let i = 0, n = templates.length; i < n; i++) {
221
- let parsed = parser.parse(templates[i].literals);
222
- getOrCreateTemplateId(ctx, parsed.html);
223
- for (let j = 0, m = templates[i].expressions.length; j < m; j++) {
224
- discoverTemplatesInExpression(ctx, templates[i].expressions[j]);
225
- }
177
+ let exprStart = expr.getStart(ctx.sourceFile), replacements = [], text = expr.getText(ctx.sourceFile);
178
+ ts.forEachChild(expr, child => collectNestedReplacements(ctx, child, replacements));
179
+ replacements.sort((a, b) => b.start - a.start);
180
+ for (let i = 0, n = replacements.length; i < n; i++) {
181
+ let r = replacements[i];
182
+ text = text.slice(0, r.start - exprStart) + r.text + text.slice(r.end - exprStart);
226
183
  }
184
+ return text;
227
185
  }
228
186
  const generateCode = (templates, sourceFile, checker) => {
229
187
  let result = {
@@ -241,44 +199,46 @@ const generateCode = (templates, sourceFile, checker) => {
241
199
  ranges.push({ end: exprs[j].end, start: exprs[j].getStart(sourceFile) });
242
200
  }
243
201
  }
244
- let rootTemplates = templates.filter(t => !ast.inRange(ranges, t.node.getStart(sourceFile), t.node.end));
245
- if (rootTemplates.length === 0) {
202
+ let root = templates.filter(t => !ast.inRange(ranges, t.node.getStart(sourceFile), t.node.end));
203
+ if (root.length === 0) {
246
204
  return result;
247
205
  }
248
206
  let ctx = {
249
207
  checker,
250
- printer,
251
208
  sourceFile,
252
209
  templates: result.templates
253
210
  };
254
- for (let i = 0, n = rootTemplates.length; i < n; i++) {
255
- let template = rootTemplates[i];
256
- result.replacements.push({
257
- generate: (sf) => {
258
- let codeBefore = sf.getFullText().slice(0, template.node.getStart(sf)), exprTexts = [], isArrowBody = codeBefore.trimEnd().endsWith('=>'), localCtx = {
259
- checker,
260
- printer,
261
- sourceFile: sf,
262
- templates: ctx.templates
263
- }, parsed = parser.parse(template.literals);
264
- for (let j = 0, m = template.expressions.length; j < m; j++) {
265
- exprTexts.push(rewriteExpression(localCtx, template.expressions[j]));
266
- }
267
- if (isArrowBody && (!parsed.slots || parsed.slots.length === 0)) {
268
- let arrowMatch = codeBefore.match(ARROW_EMPTY_PARAMS);
269
- if (arrowMatch) {
270
- return getOrCreateTemplateId(localCtx, parsed.html);
271
- }
272
- }
273
- return generateTemplateCode(localCtx, parsed, exprTexts, template.expressions, isArrowBody);
274
- },
275
- node: template.node
276
- });
211
+ for (let i = 0, n = root.length; i < n; i++) {
212
+ let exprTexts = [], parsed = parser.parse(root[i].literals), template = root[i];
213
+ for (let j = 0, m = template.expressions.length; j < m; j++) {
214
+ exprTexts.push(rewriteExpression(ctx, template.expressions[j]));
215
+ }
216
+ if (isArrowExpressionBody(template.node) &&
217
+ template.node.parent.parameters.length === 0 &&
218
+ (!parsed.slots || parsed.slots.length === 0)) {
219
+ let code = getOrCreateTemplateId(ctx, parsed.html);
220
+ result.replacements.push({
221
+ generate: () => code,
222
+ node: template.node
223
+ });
224
+ }
225
+ else {
226
+ let code = generateTemplateCode(ctx, parsed, exprTexts, template.expressions, template.node);
227
+ result.replacements.push({
228
+ generate: () => code,
229
+ node: template.node
230
+ });
231
+ }
232
+ }
233
+ for (let i = 0, n = templates.length; i < n; i++) {
234
+ getOrCreateTemplateId(ctx, parser.parse(templates[i].literals).html);
235
+ for (let j = 0, m = templates[i].expressions.length; j < m; j++) {
236
+ discoverTemplatesInExpression(ctx, templates[i].expressions[j]);
237
+ }
277
238
  }
278
- discoverAllTemplates(ctx, templates);
279
239
  for (let [html, id] of ctx.templates) {
280
- result.prepend.push(`const ${id} = ${COMPILER_NAMESPACE}.template(\`${html}\`);`);
240
+ result.prepend.push(`const ${id} = ${NAMESPACE}.template(\`${html}\`);`);
281
241
  }
282
242
  return result;
283
243
  };
284
- export { generateCode };
244
+ export { generateCode, printer };
@@ -0,0 +1,16 @@
1
+ declare const ENTRYPOINT = "html";
2
+ declare const ENTRYPOINT_REACTIVITY = "reactive";
3
+ declare const NAMESPACE: string;
4
+ declare const PACKAGE = "@esportsplus/template";
5
+ declare const enum TYPES {
6
+ ArraySlot = "array-slot",
7
+ Attributes = "attributes",
8
+ Attribute = "attribute",
9
+ DocumentFragment = "document-fragment",
10
+ Effect = "effect",
11
+ Node = "node",
12
+ Primitive = "primitive",
13
+ Static = "static",
14
+ Unknown = "unknown"
15
+ }
16
+ export { ENTRYPOINT, ENTRYPOINT_REACTIVITY, NAMESPACE, PACKAGE, TYPES };
@@ -0,0 +1,19 @@
1
+ import { uid } from '@esportsplus/typescript/compiler';
2
+ const ENTRYPOINT = 'html';
3
+ const ENTRYPOINT_REACTIVITY = 'reactive';
4
+ const NAMESPACE = uid('template');
5
+ const PACKAGE = '@esportsplus/template';
6
+ var TYPES;
7
+ (function (TYPES) {
8
+ TYPES["ArraySlot"] = "array-slot";
9
+ TYPES["Attributes"] = "attributes";
10
+ TYPES["Attribute"] = "attribute";
11
+ TYPES["DocumentFragment"] = "document-fragment";
12
+ TYPES["Effect"] = "effect";
13
+ TYPES["Node"] = "node";
14
+ TYPES["Primitive"] = "primitive";
15
+ TYPES["Static"] = "static";
16
+ TYPES["Unknown"] = "unknown";
17
+ })(TYPES || (TYPES = {}));
18
+ ;
19
+ export { ENTRYPOINT, ENTRYPOINT_REACTIVITY, NAMESPACE, PACKAGE, TYPES };
@@ -1,3 +1,6 @@
1
- import type { Plugin } from '@esportsplus/typescript/compiler';
2
- declare const plugin: Plugin;
3
- export default plugin;
1
+ import type { TransformContext } from '@esportsplus/typescript/compiler';
2
+ declare const _default: {
3
+ patterns: string[];
4
+ transform: (ctx: TransformContext) => {};
5
+ };
6
+ export default _default;
@@ -1,62 +1,53 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, PACKAGE } from '../constants.js';
3
- import { generateCode } from './codegen.js';
2
+ import { ast } from '@esportsplus/typescript/compiler';
3
+ import { ENTRYPOINT, ENTRYPOINT_REACTIVITY, NAMESPACE, PACKAGE } from './constants.js';
4
+ import { generateCode, printer } from './codegen.js';
4
5
  import { findHtmlTemplates, findReactiveCalls } from './ts-parser.js';
5
- function isInRange(ranges, start, end) {
6
- for (let i = 0, n = ranges.length; i < n; i++) {
7
- let range = ranges[i];
8
- if (start >= range.start && end <= range.end) {
9
- return true;
10
- }
11
- }
12
- return false;
13
- }
14
- let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
15
- const plugin = {
6
+ export default {
16
7
  patterns: [
17
- `${COMPILER_ENTRYPOINT}\``,
18
- `${COMPILER_ENTRYPOINT}.${COMPILER_ENTRYPOINT_REACTIVITY}`
8
+ `${ENTRYPOINT}\``,
9
+ `${ENTRYPOINT}.${ENTRYPOINT_REACTIVITY}`
19
10
  ],
20
11
  transform: (ctx) => {
21
- let importsIntent = [], prepend = [], replacements = [], removeImports = [];
22
- let templates = findHtmlTemplates(ctx.sourceFile, ctx.checker);
23
- let templateRanges = [];
12
+ let intents = {
13
+ imports: [],
14
+ prepend: [],
15
+ replacements: []
16
+ }, ranges = [], remove = [], templates = findHtmlTemplates(ctx.sourceFile, ctx.checker);
24
17
  for (let i = 0, n = templates.length; i < n; i++) {
25
- templateRanges.push({
18
+ ranges.push({
26
19
  end: templates[i].end,
27
20
  start: templates[i].start
28
21
  });
29
22
  }
30
- let reactiveCalls = findReactiveCalls(ctx.sourceFile, ctx.checker);
31
- for (let i = 0, n = reactiveCalls.length; i < n; i++) {
32
- let call = reactiveCalls[i];
33
- if (isInRange(templateRanges, call.start, call.end)) {
23
+ let calls = findReactiveCalls(ctx.sourceFile, ctx.checker);
24
+ for (let i = 0, n = calls.length; i < n; i++) {
25
+ let call = calls[i];
26
+ if (ast.inRange(ranges, call.start, call.end)) {
34
27
  continue;
35
28
  }
36
- replacements.push({
37
- generate: (sourceFile) => `new ${COMPILER_NAMESPACE}.ArraySlot(${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)}, ${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)})`,
29
+ intents.replacements.push({
30
+ generate: (sourceFile) => `new ${NAMESPACE}.ArraySlot(
31
+ ${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)},
32
+ ${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)}
33
+ )`,
38
34
  node: call.node
39
35
  });
40
36
  }
41
37
  if (templates.length > 0) {
42
38
  let result = generateCode(templates, ctx.sourceFile, ctx.checker);
43
- prepend.push(...result.prepend);
44
- replacements.push(...result.replacements);
45
- removeImports.push(COMPILER_ENTRYPOINT);
39
+ intents.prepend.push(...result.prepend);
40
+ intents.replacements.push(...result.replacements);
41
+ remove.push(ENTRYPOINT);
46
42
  }
47
- if (replacements.length === 0 && prepend.length === 0) {
43
+ if (intents.replacements.length === 0 && intents.prepend.length === 0) {
48
44
  return {};
49
45
  }
50
- importsIntent.push({
51
- namespace: COMPILER_NAMESPACE,
46
+ intents.imports.push({
47
+ namespace: NAMESPACE,
52
48
  package: PACKAGE,
53
- remove: removeImports
49
+ remove: remove
54
50
  });
55
- return {
56
- imports: importsIntent,
57
- prepend,
58
- replacements
59
- };
51
+ return intents;
60
52
  }
61
53
  };
62
- export default plugin;
@@ -1,18 +1,18 @@
1
- import { COMPILER_TYPES } from '../constants.js';
1
+ import { TYPES } from './constants.js';
2
2
  type NodePath = ('firstChild' | 'firstElementChild' | 'nextElementSibling' | 'nextSibling')[];
3
3
  declare const _default: {
4
4
  parse: (literals: string[]) => {
5
5
  html: string;
6
6
  slots: ({
7
7
  path: NodePath;
8
- type: COMPILER_TYPES.Node;
8
+ type: TYPES.Node;
9
9
  } | {
10
10
  attributes: {
11
11
  names: string[];
12
12
  statics: Record<string, string>;
13
13
  };
14
14
  path: NodePath;
15
- type: COMPILER_TYPES.Attribute;
15
+ type: TYPES.Attribute;
16
16
  })[] | null;
17
17
  };
18
18
  };
@@ -1,4 +1,5 @@
1
- import { COMPILER_TYPES, PACKAGE, SLOT_HTML } from '../constants.js';
1
+ import { SLOT_HTML } from '../constants.js';
2
+ import { PACKAGE, TYPES } from './constants.js';
2
3
  const ATTRIBUTE_DELIMITERS = {
3
4
  class: ' ',
4
5
  style: ';'
@@ -102,7 +103,7 @@ const parse = (literals) => {
102
103
  }
103
104
  }
104
105
  else {
105
- names.push(COMPILER_TYPES.Attributes);
106
+ names.push(TYPES.Attributes);
106
107
  }
107
108
  }
108
109
  }
@@ -124,7 +125,7 @@ const parse = (literals) => {
124
125
  if (!attrs) {
125
126
  throw new Error(`${PACKAGE}: attribute metadata could not be found for '${attr}'`);
126
127
  }
127
- slots.push({ attributes: attrs, path, type: COMPILER_TYPES.Attribute });
128
+ slots.push({ attributes: attrs, path, type: TYPES.Attribute });
128
129
  for (let i = 0, n = attrs.names.length; i < n; i++) {
129
130
  buffer += parsed[slot++];
130
131
  }
@@ -138,7 +139,7 @@ const parse = (literals) => {
138
139
  buffer += parsed[slot++] + SLOT_HTML;
139
140
  slots.push({
140
141
  path: methods(parent.children, parent.path, 'firstChild', 'nextSibling'),
141
- type: COMPILER_TYPES.Node
142
+ type: TYPES.Node
142
143
  });
143
144
  }
144
145
  if (n === slot) {
@@ -1,2 +1,3 @@
1
- import plugin from '../index.js';
2
- export default plugin;
1
+ import { plugin } from '@esportsplus/typescript/compiler';
2
+ declare const _default: ReturnType<typeof plugin.tsc>;
3
+ export default _default;
@@ -1,2 +1,4 @@
1
- import plugin from '../index.js';
2
- export default plugin;
1
+ import { plugin } from '@esportsplus/typescript/compiler';
2
+ import reactivity from '@esportsplus/reactivity/compiler';
3
+ import template from '../index.js';
4
+ export default plugin.tsc([reactivity, template]);
@@ -1,7 +1,8 @@
1
1
  import { plugin } from '@esportsplus/typescript/compiler';
2
- import { PACKAGE } from '../../constants.js';
3
- import templatePlugin from '../index.js';
2
+ import { PACKAGE } from '../constants.js';
3
+ import reactivity from '@esportsplus/reactivity/compiler';
4
+ import template from '../index.js';
4
5
  export default plugin.vite({
5
6
  name: PACKAGE,
6
- plugins: [templatePlugin]
7
+ plugins: [reactivity, template]
7
8
  });