@esportsplus/template 0.37.0 → 0.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/compiler/codegen.d.ts +7 -6
- package/build/compiler/codegen.js +140 -105
- package/build/compiler/index.d.ts +3 -10
- package/build/compiler/index.js +54 -73
- package/build/compiler/plugins/tsc.d.ts +2 -3
- package/build/compiler/plugins/tsc.js +2 -3
- package/build/compiler/plugins/vite.js +2 -3
- package/build/constants.d.ts +2 -1
- package/build/constants.js +3 -1
- package/package.json +3 -3
- package/src/compiler/codegen.ts +201 -150
- package/src/compiler/index.ts +65 -107
- package/src/compiler/plugins/tsc.ts +2 -3
- package/src/compiler/plugins/vite.ts +3 -4
- package/src/constants.ts +6 -1
- package/test/integration/combined.ts +90 -0
- package/test/integration/tsconfig.json +17 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import type { ReplacementIntent } from '@esportsplus/typescript/compiler';
|
|
1
2
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import type {
|
|
3
|
+
import type { TemplateInfo } from './ts-parser.js';
|
|
3
4
|
type CodegenResult = {
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
prepend: string[];
|
|
6
|
+
replacements: ReplacementIntent[];
|
|
7
|
+
templates: Map<string, string>;
|
|
6
8
|
};
|
|
7
|
-
declare const generateCode: (templates: TemplateInfo[],
|
|
8
|
-
|
|
9
|
-
export { generateCode, generateReactiveInlining };
|
|
9
|
+
declare const generateCode: (templates: TemplateInfo[], sourceFile: ts.SourceFile, checker?: ts.TypeChecker) => CodegenResult;
|
|
10
|
+
export { generateCode };
|
|
10
11
|
export type { CodegenResult };
|
|
@@ -1,48 +1,47 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import { ast,
|
|
3
|
-
import { COMPILER_ENTRYPOINT, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS
|
|
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
4
|
import { analyze } from './analyzer.js';
|
|
5
5
|
import parser from './parser.js';
|
|
6
6
|
const ARROW_EMPTY_PARAMS = /\(\s*\)\s*=>\s*$/;
|
|
7
7
|
let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
function collectNestedReplacements(ctx, node, exprStart, replacements) {
|
|
9
|
+
if (isNestedHtmlTemplate(node)) {
|
|
10
|
+
replacements.push({
|
|
11
|
+
end: node.end - exprStart,
|
|
12
|
+
newText: generateNestedTemplateCode(ctx, node),
|
|
13
|
+
start: node.getStart(ctx.sourceFile) - exprStart
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
else if (isReactiveCall(node)) {
|
|
17
|
+
replacements.push({
|
|
18
|
+
end: node.end - exprStart,
|
|
19
|
+
newText: rewriteReactiveCall(ctx, node),
|
|
20
|
+
start: node.getStart(ctx.sourceFile) - exprStart
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
ts.forEachChild(node, child => collectNestedReplacements(ctx, child, exprStart, replacements));
|
|
13
25
|
}
|
|
14
|
-
return alias;
|
|
15
26
|
}
|
|
16
|
-
function generateAttributeBinding(
|
|
27
|
+
function generateAttributeBinding(element, name, expr, staticValue) {
|
|
17
28
|
if (name.startsWith('on') && name.length > 2) {
|
|
18
29
|
let event = name.slice(2).toLowerCase(), key = name.toLowerCase();
|
|
19
30
|
if (LIFECYCLE_EVENTS.has(key)) {
|
|
20
|
-
return `${
|
|
31
|
+
return `${COMPILER_NAMESPACE}.${key}(${element}, ${expr});`;
|
|
21
32
|
}
|
|
22
33
|
if (DIRECT_ATTACH_EVENTS.has(key)) {
|
|
23
|
-
return `${
|
|
34
|
+
return `${COMPILER_NAMESPACE}.on(${element}, '${event}', ${expr});`;
|
|
24
35
|
}
|
|
25
|
-
return `${
|
|
36
|
+
return `${COMPILER_NAMESPACE}.delegate(${element}, '${event}', ${expr});`;
|
|
26
37
|
}
|
|
27
38
|
if (name === 'class') {
|
|
28
|
-
return `${
|
|
39
|
+
return `${COMPILER_NAMESPACE}.setClass(${element}, '${staticValue}', ${expr});`;
|
|
29
40
|
}
|
|
30
41
|
if (name === 'style') {
|
|
31
|
-
return `${
|
|
32
|
-
}
|
|
33
|
-
return `${addImport(ctx, 'setProperty')}(${element}, '${name}', ${expr});`;
|
|
34
|
-
}
|
|
35
|
-
function collectNestedTemplateReplacements(ctx, node, exprStart, replacements) {
|
|
36
|
-
if (isNestedHtmlTemplate(node)) {
|
|
37
|
-
replacements.push({
|
|
38
|
-
end: node.end - exprStart,
|
|
39
|
-
newText: generateNestedTemplateCode(ctx, node),
|
|
40
|
-
start: node.getStart() - exprStart
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
ts.forEachChild(node, child => collectNestedTemplateReplacements(ctx, child, exprStart, replacements));
|
|
42
|
+
return `${COMPILER_NAMESPACE}.setStyle(${element}, '${staticValue}', ${expr});`;
|
|
45
43
|
}
|
|
44
|
+
return `${COMPILER_NAMESPACE}.setProperty(${element}, '${name}', ${expr});`;
|
|
46
45
|
}
|
|
47
46
|
function generateNestedTemplateCode(ctx, node) {
|
|
48
47
|
let expressions = [], exprTexts = [], literals = [], template = node.template;
|
|
@@ -62,23 +61,23 @@ function generateNestedTemplateCode(ctx, node) {
|
|
|
62
61
|
}
|
|
63
62
|
function generateNodeBinding(ctx, anchor, exprText, exprNode) {
|
|
64
63
|
if (!exprNode) {
|
|
65
|
-
return `${
|
|
64
|
+
return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
66
65
|
}
|
|
67
66
|
if (isNestedHtmlTemplate(exprNode)) {
|
|
68
|
-
return `${anchor}.parentNode
|
|
67
|
+
return `${anchor}.parentNode!.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
|
|
69
68
|
}
|
|
70
69
|
let slotType = analyze(exprNode, ctx.checker);
|
|
71
70
|
switch (slotType) {
|
|
72
|
-
case COMPILER_TYPES.Effect:
|
|
73
|
-
return `new ${addImport(ctx, 'EffectSlot')}(${anchor}, ${exprText});`;
|
|
74
71
|
case COMPILER_TYPES.ArraySlot:
|
|
75
|
-
return
|
|
72
|
+
return `${anchor}.parentNode!.insertBefore(new ${COMPILER_NAMESPACE}.ArraySlot(${exprText}).fragment, ${anchor});`;
|
|
73
|
+
case COMPILER_TYPES.DocumentFragment:
|
|
74
|
+
return `${anchor}.parentNode!.insertBefore(${exprText}, ${anchor});`;
|
|
75
|
+
case COMPILER_TYPES.Effect:
|
|
76
|
+
return `new ${COMPILER_NAMESPACE}.EffectSlot(${anchor}, ${exprText});`;
|
|
76
77
|
case COMPILER_TYPES.Static:
|
|
77
78
|
return `${anchor}.textContent = ${exprText};`;
|
|
78
|
-
case COMPILER_TYPES.DocumentFragment:
|
|
79
|
-
return `${anchor}.parentNode.insertBefore(${exprText}, ${anchor});`;
|
|
80
79
|
default:
|
|
81
|
-
return `${
|
|
80
|
+
return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArrowBody) {
|
|
@@ -106,11 +105,11 @@ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArro
|
|
|
106
105
|
break;
|
|
107
106
|
}
|
|
108
107
|
}
|
|
109
|
-
let
|
|
108
|
+
let name = uid('element'), segments = path.slice(start), value = `${ancestor}.${segments.join('!.')}`;
|
|
110
109
|
if (ancestor === root && segments[0] === 'firstChild') {
|
|
111
|
-
value = value.replace(`${ancestor}.firstChild!`, `(${ancestor}.firstChild! as ${
|
|
110
|
+
value = value.replace(`${ancestor}.firstChild!`, `(${ancestor}.firstChild! as ${COMPILER_NAMESPACE}.Element)`);
|
|
112
111
|
}
|
|
113
|
-
declarations.push(`${name} = ${value} as ${
|
|
112
|
+
declarations.push(`${name} = ${value} as ${COMPILER_NAMESPACE}.Element`);
|
|
114
113
|
nodes.set(key, name);
|
|
115
114
|
}
|
|
116
115
|
code.push(isArrowBody ? '{' : `(() => {`, `let ${declarations.join(',\n')};`);
|
|
@@ -123,11 +122,11 @@ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArro
|
|
|
123
122
|
for (let j = 0, m = names.length; j < m; j++) {
|
|
124
123
|
let name = names[j];
|
|
125
124
|
if (name === COMPILER_TYPES.Attributes) {
|
|
126
|
-
code.push(`${
|
|
125
|
+
code.push(`${COMPILER_NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`);
|
|
127
126
|
index++;
|
|
128
127
|
}
|
|
129
128
|
else {
|
|
130
|
-
code.push(generateAttributeBinding(
|
|
129
|
+
code.push(generateAttributeBinding(element, name, exprTexts[index++] || 'undefined', slot.attributes.statics[name] || ''));
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
132
|
}
|
|
@@ -151,95 +150,131 @@ function getOrCreateTemplateId(ctx, html) {
|
|
|
151
150
|
function isNestedHtmlTemplate(expr) {
|
|
152
151
|
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
|
|
153
152
|
}
|
|
153
|
+
function isReactiveCall(expr) {
|
|
154
|
+
return (ts.isCallExpression(expr) &&
|
|
155
|
+
ts.isPropertyAccessExpression(expr.expression) &&
|
|
156
|
+
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;
|
|
167
|
+
}
|
|
154
168
|
function rewriteExpression(ctx, expr) {
|
|
155
169
|
if (isNestedHtmlTemplate(expr)) {
|
|
156
170
|
return generateNestedTemplateCode(ctx, expr);
|
|
157
171
|
}
|
|
158
|
-
if (
|
|
172
|
+
if (isReactiveCall(expr)) {
|
|
173
|
+
return rewriteReactiveCall(ctx, expr);
|
|
174
|
+
}
|
|
175
|
+
if (!ast.hasMatch(expr, n => isNestedHtmlTemplate(n) || isReactiveCall(n))) {
|
|
159
176
|
return ctx.printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
|
|
160
177
|
}
|
|
161
178
|
let replacements = [];
|
|
162
|
-
|
|
163
|
-
return
|
|
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}`;
|
|
164
185
|
}
|
|
165
|
-
|
|
186
|
+
function discoverTemplatesInExpression(ctx, node) {
|
|
187
|
+
if (isNestedHtmlTemplate(node)) {
|
|
188
|
+
let template = node, expressions = [], literals = [], tpl = template.template;
|
|
189
|
+
if (ts.isNoSubstitutionTemplateLiteral(tpl)) {
|
|
190
|
+
literals.push(tpl.text);
|
|
191
|
+
}
|
|
192
|
+
else if (ts.isTemplateExpression(tpl)) {
|
|
193
|
+
literals.push(tpl.head.text);
|
|
194
|
+
for (let i = 0, n = tpl.templateSpans.length; i < n; i++) {
|
|
195
|
+
expressions.push(tpl.templateSpans[i].expression);
|
|
196
|
+
literals.push(tpl.templateSpans[i].literal.text);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
let parsed = parser.parse(literals);
|
|
200
|
+
getOrCreateTemplateId(ctx, parsed.html);
|
|
201
|
+
for (let i = 0, n = expressions.length; i < n; i++) {
|
|
202
|
+
discoverTemplatesInExpression(ctx, expressions[i]);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else if (isReactiveCall(node)) {
|
|
206
|
+
let call = node;
|
|
207
|
+
if (call.arguments.length >= 2) {
|
|
208
|
+
discoverTemplatesInExpression(ctx, call.arguments[1]);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
ts.forEachChild(node, child => discoverTemplatesInExpression(ctx, child));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function discoverAllTemplates(ctx, templates) {
|
|
216
|
+
for (let i = 0, n = templates.length; i < n; i++) {
|
|
217
|
+
let parsed = parser.parse(templates[i].literals);
|
|
218
|
+
getOrCreateTemplateId(ctx, parsed.html);
|
|
219
|
+
for (let j = 0, m = templates[i].expressions.length; j < m; j++) {
|
|
220
|
+
discoverTemplatesInExpression(ctx, templates[i].expressions[j]);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const generateCode = (templates, sourceFile, checker) => {
|
|
225
|
+
let result = {
|
|
226
|
+
prepend: [],
|
|
227
|
+
replacements: [],
|
|
228
|
+
templates: new Map()
|
|
229
|
+
};
|
|
166
230
|
if (templates.length === 0) {
|
|
167
|
-
return
|
|
231
|
+
return result;
|
|
168
232
|
}
|
|
169
233
|
let ranges = [];
|
|
170
234
|
for (let i = 0, n = templates.length; i < n; i++) {
|
|
171
235
|
let exprs = templates[i].expressions;
|
|
172
236
|
for (let j = 0, m = exprs.length; j < m; j++) {
|
|
173
|
-
ranges.push({ end: exprs[j].end, start: exprs[j].getStart() });
|
|
237
|
+
ranges.push({ end: exprs[j].end, start: exprs[j].getStart(sourceFile) });
|
|
174
238
|
}
|
|
175
239
|
}
|
|
176
|
-
let rootTemplates = templates.filter(t => !ast.inRange(ranges, t.
|
|
240
|
+
let rootTemplates = templates.filter(t => !ast.inRange(ranges, t.node.getStart(sourceFile), t.node.end));
|
|
177
241
|
if (rootTemplates.length === 0) {
|
|
178
|
-
return
|
|
242
|
+
return result;
|
|
179
243
|
}
|
|
180
244
|
let ctx = {
|
|
181
245
|
checker,
|
|
182
|
-
imports: existingAliases ?? new Map(),
|
|
183
246
|
printer,
|
|
184
247
|
sourceFile,
|
|
185
|
-
templates:
|
|
186
|
-
}
|
|
248
|
+
templates: result.templates
|
|
249
|
+
};
|
|
187
250
|
for (let i = 0, n = rootTemplates.length; i < n; i++) {
|
|
188
|
-
let
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
let changed = replacements.length > 0, code = c.replaceReverse(originalCode, replacements);
|
|
211
|
-
if (changed && ctx.templates.size > 0) {
|
|
212
|
-
let aliasedImports = [], factories = [], updatedSourceFile = ts.createSourceFile(sourceFile.fileName, code, sourceFile.languageVersion, true);
|
|
213
|
-
for (let [name, alias] of ctx.imports) {
|
|
214
|
-
aliasedImports.push(`${name} as ${alias}`);
|
|
215
|
-
}
|
|
216
|
-
for (let [html, id] of ctx.templates) {
|
|
217
|
-
factories.push(`const ${id} = ${templateAlias}(\`${html}\`);`);
|
|
218
|
-
}
|
|
219
|
-
code = imports.modify(code, updatedSourceFile, PACKAGE, {
|
|
220
|
-
add: new Set(aliasedImports),
|
|
221
|
-
remove: [COMPILER_ENTRYPOINT]
|
|
251
|
+
let template = rootTemplates[i];
|
|
252
|
+
result.replacements.push({
|
|
253
|
+
generate: (sf) => {
|
|
254
|
+
let codeBefore = sf.getFullText().slice(0, template.node.getStart(sf)), exprTexts = [], isArrowBody = codeBefore.trimEnd().endsWith('=>'), localCtx = {
|
|
255
|
+
checker,
|
|
256
|
+
printer,
|
|
257
|
+
sourceFile: sf,
|
|
258
|
+
templates: ctx.templates
|
|
259
|
+
}, parsed = parser.parse(template.literals);
|
|
260
|
+
for (let j = 0, m = template.expressions.length; j < m; j++) {
|
|
261
|
+
exprTexts.push(rewriteExpression(localCtx, template.expressions[j]));
|
|
262
|
+
}
|
|
263
|
+
if (isArrowBody && (!parsed.slots || parsed.slots.length === 0)) {
|
|
264
|
+
let arrowMatch = codeBefore.match(ARROW_EMPTY_PARAMS);
|
|
265
|
+
if (arrowMatch) {
|
|
266
|
+
return getOrCreateTemplateId(localCtx, parsed.html);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return generateTemplateCode(localCtx, parsed, exprTexts, template.expressions, isArrowBody);
|
|
270
|
+
},
|
|
271
|
+
node: template.node
|
|
222
272
|
});
|
|
223
|
-
code = factories.join('\n') + '\n\n' + code;
|
|
224
|
-
}
|
|
225
|
-
return { changed, code };
|
|
226
|
-
};
|
|
227
|
-
const generateReactiveInlining = (calls, code, sourceFile, arraySlotAlias) => {
|
|
228
|
-
if (calls.length === 0) {
|
|
229
|
-
return code;
|
|
230
273
|
}
|
|
231
|
-
|
|
232
|
-
for (let
|
|
233
|
-
|
|
234
|
-
replacements.push({
|
|
235
|
-
end: call.end,
|
|
236
|
-
newText: `new ${arraySlotAlias}(
|
|
237
|
-
${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)},
|
|
238
|
-
${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)}
|
|
239
|
-
)`,
|
|
240
|
-
start: call.start
|
|
241
|
-
});
|
|
274
|
+
discoverAllTemplates(ctx, templates);
|
|
275
|
+
for (let [html, id] of ctx.templates) {
|
|
276
|
+
result.prepend.push(`const ${id} = ${COMPILER_NAMESPACE}.template(\`${html}\`);`);
|
|
242
277
|
}
|
|
243
|
-
return
|
|
278
|
+
return result;
|
|
244
279
|
};
|
|
245
|
-
export { generateCode
|
|
280
|
+
export { generateCode };
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
changed: boolean;
|
|
5
|
-
code: string;
|
|
6
|
-
sourceFile: ts.SourceFile;
|
|
7
|
-
};
|
|
8
|
-
declare const analyze: (sourceFile: ts.SourceFile, program: ts.Program, context: PluginContext) => void;
|
|
9
|
-
declare const transform: (sourceFile: ts.SourceFile, program: ts.Program, context?: PluginContext) => TransformResult;
|
|
10
|
-
export { analyze, transform };
|
|
1
|
+
import type { Plugin } from '@esportsplus/typescript/compiler';
|
|
2
|
+
declare const plugin: Plugin;
|
|
3
|
+
export default plugin;
|
package/build/compiler/index.js
CHANGED
|
@@ -1,82 +1,63 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { generateCode, generateReactiveInlining } from './codegen.js';
|
|
2
|
+
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, PACKAGE } from '../constants.js';
|
|
3
|
+
import { generateCode } from './codegen.js';
|
|
5
4
|
import { findHtmlTemplates, findReactiveCalls } from './ts-parser.js';
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
let checker = program.getTypeChecker(), filename = sourceFile.fileName, files = context.get(CONTEXT_KEY), programSourceFile = program.getSourceFile(filename)
|
|
19
|
-
|| program.getSourceFile(filename.replace(REGEX_BACKSLASH, '/'))
|
|
20
|
-
|| program.getSourceFile(filename.replace(REGEX_FORWARD_SLASH, '\\'));
|
|
21
|
-
if (programSourceFile) {
|
|
22
|
-
sourceFile = programSourceFile;
|
|
23
|
-
}
|
|
24
|
-
if (!files) {
|
|
25
|
-
files = new Map();
|
|
26
|
-
context.set(CONTEXT_KEY, files);
|
|
5
|
+
const PATTERNS = [
|
|
6
|
+
`${COMPILER_ENTRYPOINT}\``,
|
|
7
|
+
`${COMPILER_ENTRYPOINT}.${COMPILER_ENTRYPOINT_REACTIVITY}`
|
|
8
|
+
];
|
|
9
|
+
function isInRange(ranges, start, end) {
|
|
10
|
+
for (let i = 0, n = ranges.length; i < n; i++) {
|
|
11
|
+
let range = ranges[i];
|
|
12
|
+
if (start >= range.start && end <= range.end) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
27
15
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
19
|
+
const plugin = {
|
|
20
|
+
patterns: PATTERNS,
|
|
21
|
+
transform: (ctx) => {
|
|
22
|
+
let importsIntent = [], prepend = [], replacements = [], removeImports = [];
|
|
23
|
+
let templates = findHtmlTemplates(ctx.sourceFile, ctx.checker);
|
|
24
|
+
let templateRanges = [];
|
|
25
|
+
for (let i = 0, n = templates.length; i < n; i++) {
|
|
26
|
+
templateRanges.push({
|
|
27
|
+
end: templates[i].end,
|
|
28
|
+
start: templates[i].start
|
|
29
|
+
});
|
|
39
30
|
}
|
|
40
|
-
let
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
let reactiveCalls = findReactiveCalls(ctx.sourceFile, ctx.checker);
|
|
32
|
+
for (let i = 0, n = reactiveCalls.length; i < n; i++) {
|
|
33
|
+
let call = reactiveCalls[i];
|
|
34
|
+
if (isInRange(templateRanges, call.start, call.end)) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
replacements.push({
|
|
38
|
+
generate: (sourceFile) => `new ${COMPILER_NAMESPACE}.ArraySlot(${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)}, ${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)})`,
|
|
39
|
+
node: call.node
|
|
40
|
+
});
|
|
45
41
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
let changed = false, codegenChanged = false, existingAliases = new Map(), result = code;
|
|
52
|
-
if (analyzed.reactiveCalls.length > 0) {
|
|
53
|
-
changed = true;
|
|
54
|
-
existingAliases.set('ArraySlot', uid('ArraySlot'));
|
|
55
|
-
result = generateReactiveInlining(analyzed.reactiveCalls, result, sourceFile, existingAliases.get('ArraySlot'));
|
|
56
|
-
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
57
|
-
analyzed = {
|
|
58
|
-
reactiveCalls: [],
|
|
59
|
-
templates: findHtmlTemplates(sourceFile, program.getTypeChecker())
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
if (analyzed.templates.length > 0) {
|
|
63
|
-
let codegenResult = generateCode(analyzed.templates, result, sourceFile, program.getTypeChecker(), existingAliases);
|
|
64
|
-
if (codegenResult.changed) {
|
|
65
|
-
changed = true;
|
|
66
|
-
codegenChanged = true;
|
|
67
|
-
result = codegenResult.code;
|
|
42
|
+
if (templates.length > 0) {
|
|
43
|
+
let result = generateCode(templates, ctx.sourceFile, ctx.checker);
|
|
44
|
+
prepend.push(...result.prepend);
|
|
45
|
+
replacements.push(...result.replacements);
|
|
46
|
+
removeImports.push(COMPILER_ENTRYPOINT);
|
|
68
47
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
let aliasedImports = [];
|
|
72
|
-
for (let [name, alias] of existingAliases) {
|
|
73
|
-
aliasedImports.push(`${name} as ${alias}`);
|
|
48
|
+
if (replacements.length === 0 && prepend.length === 0) {
|
|
49
|
+
return {};
|
|
74
50
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
51
|
+
importsIntent.push({
|
|
52
|
+
namespace: COMPILER_NAMESPACE,
|
|
53
|
+
package: PACKAGE,
|
|
54
|
+
remove: removeImports
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
imports: importsIntent,
|
|
58
|
+
prepend,
|
|
59
|
+
replacements
|
|
60
|
+
};
|
|
79
61
|
}
|
|
80
|
-
return { changed, code: result, sourceFile };
|
|
81
62
|
};
|
|
82
|
-
export
|
|
63
|
+
export default plugin;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export default _default;
|
|
1
|
+
import plugin from '../index.js';
|
|
2
|
+
export default plugin;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export default plugin.tsc({ analyze, transform });
|
|
1
|
+
import plugin from '../index.js';
|
|
2
|
+
export default plugin;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { plugin } from '@esportsplus/typescript/compiler';
|
|
2
2
|
import { PACKAGE } from '../../constants.js';
|
|
3
|
-
import
|
|
3
|
+
import templatePlugin from '../index.js';
|
|
4
4
|
export default plugin.vite({
|
|
5
|
-
analyze,
|
|
6
5
|
name: PACKAGE,
|
|
7
|
-
|
|
6
|
+
plugins: [templatePlugin]
|
|
8
7
|
});
|
package/build/constants.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ declare const ARRAY_SLOT: unique symbol;
|
|
|
2
2
|
declare const CLEANUP: unique symbol;
|
|
3
3
|
declare const COMPILER_ENTRYPOINT = "html";
|
|
4
4
|
declare const COMPILER_ENTRYPOINT_REACTIVITY = "reactive";
|
|
5
|
+
declare const COMPILER_NAMESPACE: string;
|
|
5
6
|
declare const enum COMPILER_TYPES {
|
|
6
7
|
ArraySlot = "array-slot",
|
|
7
8
|
Attributes = "attributes",
|
|
@@ -21,4 +22,4 @@ declare const STATE_HYDRATING = 0;
|
|
|
21
22
|
declare const STATE_NONE = 1;
|
|
22
23
|
declare const STATE_WAITING = 2;
|
|
23
24
|
declare const STORE: unique symbol;
|
|
24
|
-
export { ARRAY_SLOT, CLEANUP, COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, PACKAGE, SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE, };
|
|
25
|
+
export { ARRAY_SLOT, CLEANUP, COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, PACKAGE, SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE, };
|
package/build/constants.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { uid } from '@esportsplus/typescript/compiler';
|
|
1
2
|
const ARRAY_SLOT = Symbol('template.array.slot');
|
|
2
3
|
const CLEANUP = Symbol('template.cleanup');
|
|
3
4
|
const COMPILER_ENTRYPOINT = 'html';
|
|
4
5
|
const COMPILER_ENTRYPOINT_REACTIVITY = 'reactive';
|
|
6
|
+
const COMPILER_NAMESPACE = uid('template');
|
|
5
7
|
var COMPILER_TYPES;
|
|
6
8
|
(function (COMPILER_TYPES) {
|
|
7
9
|
COMPILER_TYPES["ArraySlot"] = "array-slot";
|
|
@@ -33,4 +35,4 @@ const STATE_HYDRATING = 0;
|
|
|
33
35
|
const STATE_NONE = 1;
|
|
34
36
|
const STATE_WAITING = 2;
|
|
35
37
|
const STORE = Symbol('template.store');
|
|
36
|
-
export { ARRAY_SLOT, CLEANUP, COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, PACKAGE, SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE, };
|
|
38
|
+
export { ARRAY_SLOT, CLEANUP, COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, PACKAGE, SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE, };
|
package/package.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"author": "ICJR",
|
|
3
3
|
"dependencies": {
|
|
4
4
|
"@esportsplus/queue": "^0.2.0",
|
|
5
|
-
"@esportsplus/reactivity": "^0.
|
|
6
|
-
"@esportsplus/typescript": "^0.
|
|
5
|
+
"@esportsplus/reactivity": "^0.28.1",
|
|
6
|
+
"@esportsplus/typescript": "^0.25.0",
|
|
7
7
|
"@esportsplus/utilities": "^0.27.2",
|
|
8
8
|
"serve": "^14.2.5"
|
|
9
9
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"type": "module",
|
|
37
37
|
"types": "./build/index.d.ts",
|
|
38
|
-
"version": "0.
|
|
38
|
+
"version": "0.38.1",
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "tsc",
|
|
41
41
|
"build:test": "vite build --config test/vite.config.ts",
|