@esportsplus/template 0.39.0 → 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 +1 -1
- package/build/compiler/codegen.js +100 -141
- 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 +22 -23
- 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/ts-analyzer.d.ts +2 -2
- package/build/compiler/ts-analyzer.js +15 -16
- package/build/compiler/ts-parser.js +6 -6
- package/build/constants.d.ts +1 -16
- package/build/constants.js +1 -19
- package/package.json +7 -3
- package/src/compiler/codegen.ts +126 -192
- package/src/compiler/constants.ts +26 -0
- package/src/compiler/index.ts +24 -35
- 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/ts-analyzer.ts +16 -18
- package/src/compiler/ts-parser.ts +6 -7
- 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,6 +1,6 @@
|
|
|
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[];
|
|
@@ -1,28 +1,60 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { analyze } from './ts-analyzer.js';
|
|
2
2
|
import { ast, uid } from '@esportsplus/typescript/compiler';
|
|
3
|
-
import {
|
|
3
|
+
import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../constants.js';
|
|
4
|
+
import { ENTRYPOINT, ENTRYPOINT_REACTIVITY, NAMESPACE, TYPES } from './constants.js';
|
|
4
5
|
import { extractTemplateParts } from './ts-parser.js';
|
|
5
|
-
import {
|
|
6
|
+
import { ts } from '@esportsplus/typescript';
|
|
6
7
|
import parser from './parser.js';
|
|
7
8
|
let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
9
|
+
function collectNestedReplacements(ctx, node, replacements) {
|
|
10
|
+
if (isNestedHtmlTemplate(node)) {
|
|
11
|
+
replacements.push({
|
|
12
|
+
end: node.end,
|
|
13
|
+
start: node.getStart(ctx.sourceFile),
|
|
14
|
+
text: generateNestedTemplateCode(ctx, node)
|
|
15
|
+
});
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (isReactiveCall(node)) {
|
|
19
|
+
let call = node;
|
|
20
|
+
replacements.push({
|
|
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])})`
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
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;
|
|
37
|
+
}
|
|
38
|
+
ts.forEachChild(node, child => discoverTemplatesInExpression(ctx, child));
|
|
39
|
+
}
|
|
8
40
|
function generateAttributeBinding(element, name, expr, staticValue) {
|
|
9
41
|
if (name.startsWith('on') && name.length > 2) {
|
|
10
42
|
let event = name.slice(2).toLowerCase(), key = name.toLowerCase();
|
|
11
43
|
if (LIFECYCLE_EVENTS.has(key)) {
|
|
12
|
-
return `${
|
|
44
|
+
return `${NAMESPACE}.${key}(${element}, ${expr});`;
|
|
13
45
|
}
|
|
14
46
|
if (DIRECT_ATTACH_EVENTS.has(key)) {
|
|
15
|
-
return `${
|
|
47
|
+
return `${NAMESPACE}.on(${element}, '${event}', ${expr});`;
|
|
16
48
|
}
|
|
17
|
-
return `${
|
|
49
|
+
return `${NAMESPACE}.delegate(${element}, '${event}', ${expr});`;
|
|
18
50
|
}
|
|
19
51
|
if (name === 'class') {
|
|
20
|
-
return `${
|
|
52
|
+
return `${NAMESPACE}.setClass(${element}, '${staticValue}', ${expr});`;
|
|
21
53
|
}
|
|
22
54
|
if (name === 'style') {
|
|
23
|
-
return `${
|
|
55
|
+
return `${NAMESPACE}.setStyle(${element}, '${staticValue}', ${expr});`;
|
|
24
56
|
}
|
|
25
|
-
return `${
|
|
57
|
+
return `${NAMESPACE}.setProperty(${element}, '${name}', ${expr});`;
|
|
26
58
|
}
|
|
27
59
|
function generateNestedTemplateCode(ctx, node) {
|
|
28
60
|
let { expressions, literals } = extractTemplateParts(node.template), exprTexts = [];
|
|
@@ -33,22 +65,22 @@ function generateNestedTemplateCode(ctx, node) {
|
|
|
33
65
|
}
|
|
34
66
|
function generateNodeBinding(ctx, anchor, exprText, exprNode) {
|
|
35
67
|
if (!exprNode) {
|
|
36
|
-
return `${
|
|
68
|
+
return `${NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
37
69
|
}
|
|
38
70
|
if (isNestedHtmlTemplate(exprNode)) {
|
|
39
71
|
return `${anchor}.parentNode!.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
|
|
40
72
|
}
|
|
41
73
|
switch (analyze(exprNode, ctx.checker)) {
|
|
42
|
-
case
|
|
43
|
-
return `${anchor}.parentNode!.insertBefore(new ${
|
|
44
|
-
case
|
|
74
|
+
case TYPES.ArraySlot:
|
|
75
|
+
return `${anchor}.parentNode!.insertBefore(new ${NAMESPACE}.ArraySlot(${exprText}).fragment, ${anchor});`;
|
|
76
|
+
case TYPES.DocumentFragment:
|
|
45
77
|
return `${anchor}.parentNode!.insertBefore(${exprText}, ${anchor});`;
|
|
46
|
-
case
|
|
47
|
-
return `new ${
|
|
48
|
-
case
|
|
78
|
+
case TYPES.Effect:
|
|
79
|
+
return `new ${NAMESPACE}.EffectSlot(${anchor}, ${exprText});`;
|
|
80
|
+
case TYPES.Static:
|
|
49
81
|
return `${anchor}.textContent = ${exprText};`;
|
|
50
82
|
default:
|
|
51
|
-
return `${
|
|
83
|
+
return `${NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
52
84
|
}
|
|
53
85
|
}
|
|
54
86
|
function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, templateNode) {
|
|
@@ -78,22 +110,23 @@ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, templa
|
|
|
78
110
|
}
|
|
79
111
|
let name = uid('element'), segments = path.slice(start), value = `${ancestor}.${segments.join('!.')}`;
|
|
80
112
|
if (ancestor === root && segments[0] === 'firstChild') {
|
|
81
|
-
value = value.replace(`${ancestor}.firstChild!`, `(${
|
|
113
|
+
value = value.replace(`${ancestor}.firstChild!`, `(${root}.firstChild! as ${NAMESPACE}.Element)`);
|
|
82
114
|
}
|
|
83
|
-
declarations.push(`${name} = ${value} as ${
|
|
115
|
+
declarations.push(`${name} = ${value} as ${NAMESPACE}.Element`);
|
|
84
116
|
nodes.set(key, name);
|
|
85
117
|
}
|
|
86
|
-
code.push(isArrowBody ? '{' : `(() => {
|
|
118
|
+
code.push(isArrowBody ? '{' : `(() => {`);
|
|
119
|
+
code.push(`let ${declarations.join(',\n')};`);
|
|
87
120
|
for (let i = 0, n = slots.length; i < n; i++) {
|
|
88
121
|
let element = slots[i].path.length === 0
|
|
89
122
|
? root
|
|
90
123
|
: (nodes.get(slots[i].path.join('.')) || root), slot = slots[i];
|
|
91
|
-
if (slot.type ===
|
|
124
|
+
if (slot.type === TYPES.Attribute) {
|
|
92
125
|
let names = slot.attributes.names;
|
|
93
126
|
for (let j = 0, m = names.length; j < m; j++) {
|
|
94
127
|
let name = names[j];
|
|
95
|
-
if (name ===
|
|
96
|
-
code.push(`${
|
|
128
|
+
if (name === TYPES.Attributes) {
|
|
129
|
+
code.push(`${NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`);
|
|
97
130
|
index++;
|
|
98
131
|
}
|
|
99
132
|
else {
|
|
@@ -118,117 +151,37 @@ function getOrCreateTemplateId(ctx, html) {
|
|
|
118
151
|
}
|
|
119
152
|
return id;
|
|
120
153
|
}
|
|
121
|
-
function isNestedHtmlTemplate(expr) {
|
|
122
|
-
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
|
|
123
|
-
}
|
|
124
154
|
function isArrowExpressionBody(node) {
|
|
125
155
|
return ts.isArrowFunction(node.parent) && node.parent.body === node;
|
|
126
156
|
}
|
|
157
|
+
function isNestedHtmlTemplate(expr) {
|
|
158
|
+
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === ENTRYPOINT;
|
|
159
|
+
}
|
|
127
160
|
function isReactiveCall(expr) {
|
|
128
161
|
return (ts.isCallExpression(expr) &&
|
|
129
162
|
ts.isPropertyAccessExpression(expr.expression) &&
|
|
130
163
|
ts.isIdentifier(expr.expression.expression) &&
|
|
131
|
-
expr.expression.expression.text ===
|
|
132
|
-
expr.expression.name.text ===
|
|
164
|
+
expr.expression.expression.text === ENTRYPOINT &&
|
|
165
|
+
expr.expression.name.text === ENTRYPOINT_REACTIVITY);
|
|
133
166
|
}
|
|
134
167
|
function rewriteExpression(ctx, expr) {
|
|
135
168
|
if (isNestedHtmlTemplate(expr)) {
|
|
136
169
|
return generateNestedTemplateCode(ctx, expr);
|
|
137
170
|
}
|
|
138
171
|
if (isReactiveCall(expr)) {
|
|
139
|
-
return
|
|
140
|
-
${printer.printNode(ts.EmitHint.Expression, expr.arguments[0], ctx.sourceFile)},
|
|
141
|
-
${rewriteExpression(ctx, expr.arguments[1])}
|
|
142
|
-
`;
|
|
172
|
+
return `${rewriteExpression(ctx, expr.arguments[0])}, ${rewriteExpression(ctx, expr.arguments[1])}`;
|
|
143
173
|
}
|
|
144
|
-
if (!ast.
|
|
174
|
+
if (!ast.test(expr, n => isNestedHtmlTemplate(n) || isReactiveCall(n))) {
|
|
145
175
|
return printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
|
|
146
176
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (isReactiveCall(node)) {
|
|
154
|
-
let call = node;
|
|
155
|
-
return `new ${COMPILER_NAMESPACE}.ArraySlot(${printer.printNode(ts.EmitHint.Expression, call.arguments[0], ctx.sourceFile)}, ${rewriteExpression(ctx, call.arguments[1])})`;
|
|
156
|
-
}
|
|
157
|
-
if (!ast.hasMatch(node, n => isNestedHtmlTemplate(n) || isReactiveCall(n))) {
|
|
158
|
-
return printer.printNode(ts.EmitHint.Expression, node, ctx.sourceFile);
|
|
159
|
-
}
|
|
160
|
-
if (ts.isConditionalExpression(node)) {
|
|
161
|
-
return `${walkExpression(ctx, node.condition)} ? ${walkExpression(ctx, node.whenTrue)} : ${walkExpression(ctx, node.whenFalse)}`;
|
|
162
|
-
}
|
|
163
|
-
if (ts.isBinaryExpression(node)) {
|
|
164
|
-
return `${walkExpression(ctx, node.left)} ${node.operatorToken.getText(ctx.sourceFile)} ${walkExpression(ctx, node.right)}`;
|
|
165
|
-
}
|
|
166
|
-
if (ts.isCallExpression(node)) {
|
|
167
|
-
let args = [];
|
|
168
|
-
for (let i = 0, n = node.arguments.length; i < n; i++) {
|
|
169
|
-
args.push(walkExpression(ctx, node.arguments[i]));
|
|
170
|
-
}
|
|
171
|
-
return `${walkExpression(ctx, node.expression)}(${args.join(', ')})`;
|
|
172
|
-
}
|
|
173
|
-
if (ts.isPropertyAccessExpression(node)) {
|
|
174
|
-
return `${walkExpression(ctx, node.expression)}.${node.name.text}`;
|
|
175
|
-
}
|
|
176
|
-
if (ts.isElementAccessExpression(node)) {
|
|
177
|
-
return `${walkExpression(ctx, node.expression)}[${walkExpression(ctx, node.argumentExpression)}]`;
|
|
178
|
-
}
|
|
179
|
-
if (ts.isParenthesizedExpression(node)) {
|
|
180
|
-
return `(${walkExpression(ctx, node.expression)})`;
|
|
181
|
-
}
|
|
182
|
-
if (ts.isArrowFunction(node)) {
|
|
183
|
-
return `(${node.parameters.map(p => p.getText(ctx.sourceFile)).join(', ')}) => ${ts.isBlock(node.body) ? node.body.getText(ctx.sourceFile) : walkExpression(ctx, node.body)}`;
|
|
184
|
-
}
|
|
185
|
-
if (ts.isArrayLiteralExpression(node)) {
|
|
186
|
-
let elements = [];
|
|
187
|
-
for (let i = 0, n = node.elements.length; i < n; i++) {
|
|
188
|
-
elements.push(walkExpression(ctx, node.elements[i]));
|
|
189
|
-
}
|
|
190
|
-
return `[${elements.join(', ')}]`;
|
|
191
|
-
}
|
|
192
|
-
if (ts.isObjectLiteralExpression(node)) {
|
|
193
|
-
let properties = [];
|
|
194
|
-
for (let i = 0, n = node.properties.length; i < n; i++) {
|
|
195
|
-
let prop = node.properties[i];
|
|
196
|
-
if (ts.isPropertyAssignment(prop)) {
|
|
197
|
-
properties.push(`${prop.name.getText(ctx.sourceFile)}: ${walkExpression(ctx, prop.initializer)}`);
|
|
198
|
-
}
|
|
199
|
-
else if (ts.isShorthandPropertyAssignment(prop)) {
|
|
200
|
-
properties.push(prop.name.text);
|
|
201
|
-
}
|
|
202
|
-
else if (ts.isSpreadAssignment(prop)) {
|
|
203
|
-
properties.push(`...${walkExpression(ctx, prop.expression)}`);
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
properties.push(prop.getText(ctx.sourceFile));
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return `{ ${properties.join(', ')} }`;
|
|
210
|
-
}
|
|
211
|
-
if (ts.isPrefixUnaryExpression(node)) {
|
|
212
|
-
return `${ts.tokenToString(node.operator)}${walkExpression(ctx, node.operand)}`;
|
|
213
|
-
}
|
|
214
|
-
if (ts.isPostfixUnaryExpression(node)) {
|
|
215
|
-
return `${walkExpression(ctx, node.operand)}${ts.tokenToString(node.operator)}`;
|
|
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);
|
|
216
183
|
}
|
|
217
|
-
|
|
218
|
-
let parts = [node.head.text];
|
|
219
|
-
for (let i = 0, n = node.templateSpans.length; i < n; i++) {
|
|
220
|
-
let span = node.templateSpans[i];
|
|
221
|
-
parts.push('${' + walkExpression(ctx, span.expression) + '}' + span.literal.text);
|
|
222
|
-
}
|
|
223
|
-
return '`' + parts.join('') + '`';
|
|
224
|
-
}
|
|
225
|
-
if (ts.isAsExpression(node) || ts.isTypeAssertionExpression(node)) {
|
|
226
|
-
return walkExpression(ctx, node.expression);
|
|
227
|
-
}
|
|
228
|
-
if (ts.isNonNullExpression(node)) {
|
|
229
|
-
return `${walkExpression(ctx, node.expression)}!`;
|
|
230
|
-
}
|
|
231
|
-
return printer.printNode(ts.EmitHint.Expression, node, ctx.sourceFile);
|
|
184
|
+
return text;
|
|
232
185
|
}
|
|
233
186
|
const generateCode = (templates, sourceFile, checker) => {
|
|
234
187
|
let result = {
|
|
@@ -246,8 +199,8 @@ const generateCode = (templates, sourceFile, checker) => {
|
|
|
246
199
|
ranges.push({ end: exprs[j].end, start: exprs[j].getStart(sourceFile) });
|
|
247
200
|
}
|
|
248
201
|
}
|
|
249
|
-
let
|
|
250
|
-
if (
|
|
202
|
+
let root = templates.filter(t => !ast.inRange(ranges, t.node.getStart(sourceFile), t.node.end));
|
|
203
|
+
if (root.length === 0) {
|
|
251
204
|
return result;
|
|
252
205
|
}
|
|
253
206
|
let ctx = {
|
|
@@ -255,30 +208,36 @@ const generateCode = (templates, sourceFile, checker) => {
|
|
|
255
208
|
sourceFile,
|
|
256
209
|
templates: result.templates
|
|
257
210
|
};
|
|
258
|
-
for (let i = 0, n =
|
|
259
|
-
let template =
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
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
|
+
}
|
|
279
238
|
}
|
|
280
239
|
for (let [html, id] of ctx.templates) {
|
|
281
|
-
result.prepend.push(`const ${id} = ${
|
|
240
|
+
result.prepend.push(`const ${id} = ${NAMESPACE}.template(\`${html}\`);`);
|
|
282
241
|
}
|
|
283
242
|
return result;
|
|
284
243
|
};
|
|
@@ -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,29 +1,33 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
2
|
import { ast } from '@esportsplus/typescript/compiler';
|
|
3
|
-
import {
|
|
3
|
+
import { ENTRYPOINT, ENTRYPOINT_REACTIVITY, NAMESPACE, PACKAGE } from './constants.js';
|
|
4
4
|
import { generateCode, printer } from './codegen.js';
|
|
5
5
|
import { findHtmlTemplates, findReactiveCalls } from './ts-parser.js';
|
|
6
|
-
|
|
6
|
+
export default {
|
|
7
7
|
patterns: [
|
|
8
|
-
`${
|
|
9
|
-
`${
|
|
8
|
+
`${ENTRYPOINT}\``,
|
|
9
|
+
`${ENTRYPOINT}.${ENTRYPOINT_REACTIVITY}`
|
|
10
10
|
],
|
|
11
11
|
transform: (ctx) => {
|
|
12
|
-
let
|
|
12
|
+
let intents = {
|
|
13
|
+
imports: [],
|
|
14
|
+
prepend: [],
|
|
15
|
+
replacements: []
|
|
16
|
+
}, ranges = [], remove = [], templates = findHtmlTemplates(ctx.sourceFile, ctx.checker);
|
|
13
17
|
for (let i = 0, n = templates.length; i < n; i++) {
|
|
14
18
|
ranges.push({
|
|
15
19
|
end: templates[i].end,
|
|
16
20
|
start: templates[i].start
|
|
17
21
|
});
|
|
18
22
|
}
|
|
19
|
-
let
|
|
20
|
-
for (let i = 0, n =
|
|
21
|
-
let call =
|
|
23
|
+
let calls = findReactiveCalls(ctx.sourceFile, ctx.checker);
|
|
24
|
+
for (let i = 0, n = calls.length; i < n; i++) {
|
|
25
|
+
let call = calls[i];
|
|
22
26
|
if (ast.inRange(ranges, call.start, call.end)) {
|
|
23
27
|
continue;
|
|
24
28
|
}
|
|
25
|
-
replacements.push({
|
|
26
|
-
generate: (sourceFile) => `new ${
|
|
29
|
+
intents.replacements.push({
|
|
30
|
+
generate: (sourceFile) => `new ${NAMESPACE}.ArraySlot(
|
|
27
31
|
${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)},
|
|
28
32
|
${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)}
|
|
29
33
|
)`,
|
|
@@ -32,23 +36,18 @@ const plugin = {
|
|
|
32
36
|
}
|
|
33
37
|
if (templates.length > 0) {
|
|
34
38
|
let result = generateCode(templates, ctx.sourceFile, ctx.checker);
|
|
35
|
-
prepend.push(...result.prepend);
|
|
36
|
-
replacements.push(...result.replacements);
|
|
37
|
-
|
|
39
|
+
intents.prepend.push(...result.prepend);
|
|
40
|
+
intents.replacements.push(...result.replacements);
|
|
41
|
+
remove.push(ENTRYPOINT);
|
|
38
42
|
}
|
|
39
|
-
if (replacements.length === 0 && prepend.length === 0) {
|
|
43
|
+
if (intents.replacements.length === 0 && intents.prepend.length === 0) {
|
|
40
44
|
return {};
|
|
41
45
|
}
|
|
42
|
-
|
|
43
|
-
namespace:
|
|
46
|
+
intents.imports.push({
|
|
47
|
+
namespace: NAMESPACE,
|
|
44
48
|
package: PACKAGE,
|
|
45
|
-
remove:
|
|
49
|
+
remove: remove
|
|
46
50
|
});
|
|
47
|
-
return
|
|
48
|
-
imports: importsIntent,
|
|
49
|
-
prepend,
|
|
50
|
-
replacements
|
|
51
|
-
};
|
|
51
|
+
return intents;
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
-
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
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { COMPILER_TYPES } from '../constants.js';
|
|
2
1
|
import { ts } from '@esportsplus/typescript';
|
|
3
|
-
|
|
2
|
+
import { TYPES } from './constants.js';
|
|
3
|
+
declare const analyze: (expr: ts.Expression, checker?: ts.TypeChecker) => TYPES;
|
|
4
4
|
export { analyze };
|