@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.
- package/README.md +5 -26
- package/build/compiler/codegen.d.ts +3 -2
- package/build/compiler/codegen.js +102 -142
- package/build/compiler/constants.d.ts +16 -0
- package/build/compiler/constants.js +19 -0
- package/build/compiler/index.d.ts +6 -3
- package/build/compiler/index.js +29 -38
- package/build/compiler/parser.d.ts +3 -3
- package/build/compiler/parser.js +5 -4
- package/build/compiler/plugins/tsc.d.ts +3 -2
- package/build/compiler/plugins/tsc.js +4 -2
- package/build/compiler/plugins/vite.js +4 -3
- package/build/compiler/{analyzer.d.ts → ts-analyzer.d.ts} +2 -2
- package/build/compiler/{analyzer.js → ts-analyzer.js} +16 -18
- package/build/compiler/ts-parser.d.ts +5 -1
- package/build/compiler/ts-parser.js +27 -45
- package/build/constants.d.ts +1 -16
- package/build/constants.js +1 -19
- package/package.json +7 -3
- package/src/compiler/codegen.ts +135 -217
- package/src/compiler/constants.ts +26 -0
- package/src/compiler/index.ts +33 -58
- package/src/compiler/parser.ts +7 -6
- package/src/compiler/plugins/tsc.ts +4 -2
- package/src/compiler/plugins/vite.ts +4 -3
- package/src/compiler/{analyzer.ts → ts-analyzer.ts} +17 -20
- package/src/compiler/ts-parser.ts +35 -67
- package/src/constants.ts +0 -25
- package/test/counter.ts +113 -0
- package/test/effects.ts +1 -1
- package/test/events.ts +1 -1
- package/test/imported-values.ts +1 -1
- package/test/integration/tsconfig.json +0 -1
- package/test/nested.ts +20 -1
- package/test/slots.ts +1 -1
- package/test/spread.ts +1 -1
- package/test/static.ts +1 -1
- package/test/templates.ts +1 -1
- 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
|
|
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
|
|
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/
|
|
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 {
|
|
1
|
+
import { analyze } from './ts-analyzer.js';
|
|
2
2
|
import { ast, uid } from '@esportsplus/typescript/compiler';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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,
|
|
9
|
+
function collectNestedReplacements(ctx, node, replacements) {
|
|
9
10
|
if (isNestedHtmlTemplate(node)) {
|
|
10
11
|
replacements.push({
|
|
11
|
-
end: node.end
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
end: node.end,
|
|
13
|
+
start: node.getStart(ctx.sourceFile),
|
|
14
|
+
text: generateNestedTemplateCode(ctx, node)
|
|
14
15
|
});
|
|
16
|
+
return;
|
|
15
17
|
}
|
|
16
|
-
|
|
18
|
+
if (isReactiveCall(node)) {
|
|
19
|
+
let call = node;
|
|
17
20
|
replacements.push({
|
|
18
|
-
end: node.end
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
24
|
-
|
|
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 `${
|
|
44
|
+
return `${NAMESPACE}.${key}(${element}, ${expr});`;
|
|
32
45
|
}
|
|
33
46
|
if (DIRECT_ATTACH_EVENTS.has(key)) {
|
|
34
|
-
return `${
|
|
47
|
+
return `${NAMESPACE}.on(${element}, '${event}', ${expr});`;
|
|
35
48
|
}
|
|
36
|
-
return `${
|
|
49
|
+
return `${NAMESPACE}.delegate(${element}, '${event}', ${expr});`;
|
|
37
50
|
}
|
|
38
51
|
if (name === 'class') {
|
|
39
|
-
return `${
|
|
52
|
+
return `${NAMESPACE}.setClass(${element}, '${staticValue}', ${expr});`;
|
|
40
53
|
}
|
|
41
54
|
if (name === 'style') {
|
|
42
|
-
return `${
|
|
55
|
+
return `${NAMESPACE}.setStyle(${element}, '${staticValue}', ${expr});`;
|
|
43
56
|
}
|
|
44
|
-
return `${
|
|
57
|
+
return `${NAMESPACE}.setProperty(${element}, '${name}', ${expr});`;
|
|
45
58
|
}
|
|
46
59
|
function generateNestedTemplateCode(ctx, node) {
|
|
47
|
-
let expressions
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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 `${
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
76
|
-
return `new ${
|
|
77
|
-
case
|
|
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 `${
|
|
83
|
+
return `${NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
|
-
function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes,
|
|
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!`, `(${
|
|
113
|
+
value = value.replace(`${ancestor}.firstChild!`, `(${root}.firstChild! as ${NAMESPACE}.Element)`);
|
|
111
114
|
}
|
|
112
|
-
declarations.push(`${name} = ${value} as ${
|
|
115
|
+
declarations.push(`${name} = ${value} as ${NAMESPACE}.Element`);
|
|
113
116
|
nodes.set(key, name);
|
|
114
117
|
}
|
|
115
|
-
code.push(isArrowBody ? '{' : `(() => {
|
|
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 ===
|
|
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 ===
|
|
125
|
-
code.push(`${
|
|
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 ===
|
|
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 ===
|
|
158
|
-
expr.expression.name.text ===
|
|
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
|
|
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
|
-
|
|
216
|
-
ts.
|
|
174
|
+
if (!ast.test(expr, n => isNestedHtmlTemplate(n) || isReactiveCall(n))) {
|
|
175
|
+
return printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
|
|
217
176
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
|
245
|
-
if (
|
|
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 =
|
|
255
|
-
let template =
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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} = ${
|
|
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 {
|
|
2
|
-
declare const
|
|
3
|
-
|
|
1
|
+
import type { TransformContext } from '@esportsplus/typescript/compiler';
|
|
2
|
+
declare const _default: {
|
|
3
|
+
patterns: string[];
|
|
4
|
+
transform: (ctx: TransformContext) => {};
|
|
5
|
+
};
|
|
6
|
+
export default _default;
|
package/build/compiler/index.js
CHANGED
|
@@ -1,62 +1,53 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
-
|
|
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
|
-
`${
|
|
18
|
-
`${
|
|
8
|
+
`${ENTRYPOINT}\``,
|
|
9
|
+
`${ENTRYPOINT}.${ENTRYPOINT_REACTIVITY}`
|
|
19
10
|
],
|
|
20
11
|
transform: (ctx) => {
|
|
21
|
-
let
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
18
|
+
ranges.push({
|
|
26
19
|
end: templates[i].end,
|
|
27
20
|
start: templates[i].start
|
|
28
21
|
});
|
|
29
22
|
}
|
|
30
|
-
let
|
|
31
|
-
for (let i = 0, n =
|
|
32
|
-
let call =
|
|
33
|
-
if (
|
|
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 ${
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
namespace:
|
|
46
|
+
intents.imports.push({
|
|
47
|
+
namespace: NAMESPACE,
|
|
52
48
|
package: PACKAGE,
|
|
53
|
-
remove:
|
|
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 {
|
|
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:
|
|
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:
|
|
15
|
+
type: TYPES.Attribute;
|
|
16
16
|
})[] | null;
|
|
17
17
|
};
|
|
18
18
|
};
|
package/build/compiler/parser.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
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(
|
|
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:
|
|
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:
|
|
142
|
+
type: TYPES.Node
|
|
142
143
|
});
|
|
143
144
|
}
|
|
144
145
|
if (n === slot) {
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import plugin from '
|
|
2
|
-
|
|
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 '
|
|
2
|
-
|
|
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 '
|
|
3
|
-
import
|
|
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: [
|
|
7
|
+
plugins: [reactivity, template]
|
|
7
8
|
});
|