@esportsplus/template 0.35.1 → 0.38.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/build/compiler/analyzer.d.ts +4 -0
- package/build/compiler/{type-analyzer.js → analyzer.js} +15 -47
- package/build/compiler/codegen.d.ts +8 -6
- package/build/compiler/codegen.js +77 -75
- package/build/compiler/index.d.ts +5 -8
- package/build/compiler/index.js +32 -44
- package/build/compiler/plugins/tsc.js +2 -2
- package/build/compiler/plugins/vite.d.ts +4 -4
- package/build/compiler/plugins/vite.js +2 -2
- package/build/compiler/reactive-inlining.d.ts +5 -0
- package/build/compiler/reactive-inlining.js +75 -0
- package/build/compiler/ts-parser.js +2 -2
- package/package.json +4 -4
- package/src/compiler/analyzer.ts +92 -0
- package/src/compiler/codegen.ts +116 -110
- package/src/compiler/index.ts +35 -65
- package/src/compiler/plugins/tsc.ts +2 -2
- package/src/compiler/plugins/vite.ts +2 -2
- package/src/compiler/reactive-inlining.ts +116 -0
- package/src/compiler/ts-parser.ts +2 -2
- package/test/vite.config.ts +1 -1
- package/build/compiler/type-analyzer.d.ts +0 -6
- package/src/compiler/type-analyzer.ts +0 -148
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_TYPES
|
|
1
|
+
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_TYPES } from '../constants.js';
|
|
2
2
|
import { ts } from '@esportsplus/typescript';
|
|
3
|
-
function
|
|
3
|
+
function isTypeFunction(type, checker) {
|
|
4
|
+
if (type.isUnion()) {
|
|
5
|
+
for (let i = 0, n = type.types.length; i < n; i++) {
|
|
6
|
+
if (!isTypeFunction(type.types[i], checker)) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return type.types.length > 0;
|
|
11
|
+
}
|
|
12
|
+
return type.getCallSignatures().length > 0;
|
|
13
|
+
}
|
|
14
|
+
const analyze = (expr, checker) => {
|
|
4
15
|
while (ts.isParenthesizedExpression(expr)) {
|
|
5
16
|
expr = expr.expression;
|
|
6
17
|
}
|
|
@@ -30,7 +41,7 @@ function inferCOMPILER_TYPES(expr, checker) {
|
|
|
30
41
|
return COMPILER_TYPES.Primitive;
|
|
31
42
|
}
|
|
32
43
|
if (ts.isConditionalExpression(expr)) {
|
|
33
|
-
let whenFalse =
|
|
44
|
+
let whenFalse = analyze(expr.whenFalse, checker), whenTrue = analyze(expr.whenTrue, checker);
|
|
34
45
|
if (whenTrue === whenFalse) {
|
|
35
46
|
return whenTrue;
|
|
36
47
|
}
|
|
@@ -50,48 +61,5 @@ function inferCOMPILER_TYPES(expr, checker) {
|
|
|
50
61
|
}
|
|
51
62
|
}
|
|
52
63
|
return COMPILER_TYPES.Unknown;
|
|
53
|
-
}
|
|
54
|
-
function isTypeFunction(type, checker) {
|
|
55
|
-
if (type.isUnion()) {
|
|
56
|
-
let allFunctions = true, hasFunction = false;
|
|
57
|
-
for (let i = 0, n = type.types.length; i < n; i++) {
|
|
58
|
-
if (isTypeFunction(type.types[i], checker)) {
|
|
59
|
-
hasFunction = true;
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
allFunctions = false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return hasFunction && allFunctions;
|
|
66
|
-
}
|
|
67
|
-
return type.getCallSignatures().length > 0;
|
|
68
|
-
}
|
|
69
|
-
const analyzeExpression = (expr, checker) => {
|
|
70
|
-
return inferCOMPILER_TYPES(expr, checker);
|
|
71
|
-
};
|
|
72
|
-
const generateAttributeBinding = (elementVar, name, expr, staticValue, addImport) => {
|
|
73
|
-
if (name.startsWith('on') && name.length > 2) {
|
|
74
|
-
let event = name.slice(2).toLowerCase(), key = name.toLowerCase();
|
|
75
|
-
if (LIFECYCLE_EVENTS.has(key)) {
|
|
76
|
-
return `${addImport(key)}(${elementVar}, ${expr});`;
|
|
77
|
-
}
|
|
78
|
-
if (DIRECT_ATTACH_EVENTS.has(key)) {
|
|
79
|
-
return `${addImport('on')}(${elementVar}, '${event}', ${expr});`;
|
|
80
|
-
}
|
|
81
|
-
return `${addImport('delegate')}(${elementVar}, '${event}', ${expr});`;
|
|
82
|
-
}
|
|
83
|
-
if (name === 'class') {
|
|
84
|
-
return `${addImport('setClass')}(${elementVar}, '${staticValue}', ${expr});`;
|
|
85
|
-
}
|
|
86
|
-
if (name === COMPILER_TYPES.Attributes) {
|
|
87
|
-
return `${addImport('setProperties')}(${elementVar}, ${expr});`;
|
|
88
|
-
}
|
|
89
|
-
if (name === 'style') {
|
|
90
|
-
return `${addImport('setStyle')}(${elementVar}, '${staticValue}', ${expr});`;
|
|
91
|
-
}
|
|
92
|
-
return `${addImport('setProperty')}(${elementVar}, '${name}', ${expr});`;
|
|
93
|
-
};
|
|
94
|
-
const generateSpreadBindings = (exprCode, elementVar, addImport) => {
|
|
95
|
-
return [`${addImport('setProperties')}(${elementVar}, ${exprCode});`];
|
|
96
64
|
};
|
|
97
|
-
export {
|
|
65
|
+
export { analyze };
|
|
@@ -1,10 +1,12 @@
|
|
|
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
|
+
imports: Map<string, string>;
|
|
6
|
+
prepend: string[];
|
|
7
|
+
replacements: ReplacementIntent[];
|
|
8
|
+
templates: Map<string, string>;
|
|
6
9
|
};
|
|
7
|
-
declare const generateCode: (templates: TemplateInfo[],
|
|
8
|
-
|
|
9
|
-
export { generateCode, generateReactiveInlining };
|
|
10
|
+
declare const generateCode: (templates: TemplateInfo[], sourceFile: ts.SourceFile, checker?: ts.TypeChecker, existingAliases?: Map<string, string>) => CodegenResult;
|
|
11
|
+
export { generateCode };
|
|
10
12
|
export type { CodegenResult };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import { ast,
|
|
3
|
-
import { COMPILER_ENTRYPOINT, COMPILER_TYPES,
|
|
4
|
-
import {
|
|
2
|
+
import { ast, uid } from '@esportsplus/typescript/compiler';
|
|
3
|
+
import { COMPILER_ENTRYPOINT, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../constants.js';
|
|
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 });
|
|
@@ -18,13 +18,32 @@ function collectNestedTemplateReplacements(ctx, node, exprStart, replacements) {
|
|
|
18
18
|
replacements.push({
|
|
19
19
|
end: node.end - exprStart,
|
|
20
20
|
newText: generateNestedTemplateCode(ctx, node),
|
|
21
|
-
start: node.getStart() - exprStart
|
|
21
|
+
start: node.getStart(ctx.sourceFile) - exprStart
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
else {
|
|
25
25
|
ts.forEachChild(node, child => collectNestedTemplateReplacements(ctx, child, exprStart, replacements));
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
function generateAttributeBinding(ctx, element, name, expr, staticValue) {
|
|
29
|
+
if (name.startsWith('on') && name.length > 2) {
|
|
30
|
+
let event = name.slice(2).toLowerCase(), key = name.toLowerCase();
|
|
31
|
+
if (LIFECYCLE_EVENTS.has(key)) {
|
|
32
|
+
return `${addImport(ctx, key)}(${element}, ${expr});`;
|
|
33
|
+
}
|
|
34
|
+
if (DIRECT_ATTACH_EVENTS.has(key)) {
|
|
35
|
+
return `${addImport(ctx, 'on')}(${element}, '${event}', ${expr});`;
|
|
36
|
+
}
|
|
37
|
+
return `${addImport(ctx, 'delegate')}(${element}, '${event}', ${expr});`;
|
|
38
|
+
}
|
|
39
|
+
if (name === 'class') {
|
|
40
|
+
return `${addImport(ctx, 'setClass')}(${element}, '${staticValue}', ${expr});`;
|
|
41
|
+
}
|
|
42
|
+
if (name === 'style') {
|
|
43
|
+
return `${addImport(ctx, 'setStyle')}(${element}, '${staticValue}', ${expr});`;
|
|
44
|
+
}
|
|
45
|
+
return `${addImport(ctx, 'setProperty')}(${element}, '${name}', ${expr});`;
|
|
46
|
+
}
|
|
28
47
|
function generateNestedTemplateCode(ctx, node) {
|
|
29
48
|
let expressions = [], exprTexts = [], literals = [], template = node.template;
|
|
30
49
|
if (ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
@@ -48,7 +67,7 @@ function generateNodeBinding(ctx, anchor, exprText, exprNode) {
|
|
|
48
67
|
if (isNestedHtmlTemplate(exprNode)) {
|
|
49
68
|
return `${anchor}.parentNode.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
|
|
50
69
|
}
|
|
51
|
-
let slotType =
|
|
70
|
+
let slotType = analyze(exprNode, ctx.checker);
|
|
52
71
|
switch (slotType) {
|
|
53
72
|
case COMPILER_TYPES.Effect:
|
|
54
73
|
return `new ${addImport(ctx, 'EffectSlot')}(${anchor}, ${exprText});`;
|
|
@@ -104,14 +123,11 @@ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArro
|
|
|
104
123
|
for (let j = 0, m = names.length; j < m; j++) {
|
|
105
124
|
let name = names[j];
|
|
106
125
|
if (name === COMPILER_TYPES.Attributes) {
|
|
107
|
-
|
|
108
|
-
for (let k = 0, o = bindings.length; k < o; k++) {
|
|
109
|
-
code.push(bindings[k]);
|
|
110
|
-
}
|
|
126
|
+
code.push(`${addImport(ctx, 'setProperties')}(${element}, ${exprTexts[index] || 'undefined'});`);
|
|
111
127
|
index++;
|
|
112
128
|
}
|
|
113
129
|
else {
|
|
114
|
-
code.push(generateAttributeBinding(element, name, exprTexts[index++] || 'undefined', slot.attributes.statics[name] || ''
|
|
130
|
+
code.push(generateAttributeBinding(ctx, element, name, exprTexts[index++] || 'undefined', slot.attributes.statics[name] || ''));
|
|
115
131
|
}
|
|
116
132
|
}
|
|
117
133
|
}
|
|
@@ -135,6 +151,14 @@ function getOrCreateTemplateId(ctx, html) {
|
|
|
135
151
|
function isNestedHtmlTemplate(expr) {
|
|
136
152
|
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
|
|
137
153
|
}
|
|
154
|
+
function replaceReverse(text, replacements) {
|
|
155
|
+
let sorted = replacements.slice().sort((a, b) => b.start - a.start);
|
|
156
|
+
for (let i = 0, n = sorted.length; i < n; i++) {
|
|
157
|
+
let r = sorted[i];
|
|
158
|
+
text = text.slice(0, r.start) + r.newText + text.slice(r.end);
|
|
159
|
+
}
|
|
160
|
+
return text;
|
|
161
|
+
}
|
|
138
162
|
function rewriteExpression(ctx, expr) {
|
|
139
163
|
if (isNestedHtmlTemplate(expr)) {
|
|
140
164
|
return generateNestedTemplateCode(ctx, expr);
|
|
@@ -143,87 +167,65 @@ function rewriteExpression(ctx, expr) {
|
|
|
143
167
|
return ctx.printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
|
|
144
168
|
}
|
|
145
169
|
let replacements = [];
|
|
146
|
-
collectNestedTemplateReplacements(ctx, expr, expr.getStart(), replacements);
|
|
147
|
-
return
|
|
170
|
+
collectNestedTemplateReplacements(ctx, expr, expr.getStart(ctx.sourceFile), replacements);
|
|
171
|
+
return replaceReverse(expr.getText(ctx.sourceFile), replacements);
|
|
148
172
|
}
|
|
149
|
-
const generateCode = (templates,
|
|
173
|
+
const generateCode = (templates, sourceFile, checker, existingAliases) => {
|
|
174
|
+
let result = {
|
|
175
|
+
imports: existingAliases ?? new Map(),
|
|
176
|
+
prepend: [],
|
|
177
|
+
replacements: [],
|
|
178
|
+
templates: new Map()
|
|
179
|
+
};
|
|
150
180
|
if (templates.length === 0) {
|
|
151
|
-
return
|
|
181
|
+
return result;
|
|
152
182
|
}
|
|
153
183
|
let ranges = [];
|
|
154
184
|
for (let i = 0, n = templates.length; i < n; i++) {
|
|
155
185
|
let exprs = templates[i].expressions;
|
|
156
186
|
for (let j = 0, m = exprs.length; j < m; j++) {
|
|
157
|
-
ranges.push({ end: exprs[j].end, start: exprs[j].getStart() });
|
|
187
|
+
ranges.push({ end: exprs[j].end, start: exprs[j].getStart(sourceFile) });
|
|
158
188
|
}
|
|
159
189
|
}
|
|
160
|
-
let rootTemplates = templates.filter(t => !ast.inRange(ranges, t.
|
|
190
|
+
let rootTemplates = templates.filter(t => !ast.inRange(ranges, t.node.getStart(sourceFile), t.node.end));
|
|
161
191
|
if (rootTemplates.length === 0) {
|
|
162
|
-
return
|
|
192
|
+
return result;
|
|
163
193
|
}
|
|
164
194
|
let ctx = {
|
|
165
195
|
checker,
|
|
166
|
-
|
|
167
|
-
imports: existingAliases ?? new Map(),
|
|
196
|
+
imports: result.imports,
|
|
168
197
|
printer,
|
|
169
|
-
sourceFile
|
|
170
|
-
|
|
198
|
+
sourceFile,
|
|
199
|
+
templates: result.templates
|
|
200
|
+
}, templateAlias = addImport(ctx, 'template');
|
|
171
201
|
for (let i = 0, n = rootTemplates.length; i < n; i++) {
|
|
172
|
-
let
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
let changed = replacements.length > 0, code = c.replaceReverse(originalCode, replacements);
|
|
195
|
-
if (changed && ctx.templates.size > 0) {
|
|
196
|
-
let aliasedImports = [], factories = [];
|
|
197
|
-
for (let [name, alias] of ctx.imports) {
|
|
198
|
-
aliasedImports.push(`${name} as ${alias}`);
|
|
199
|
-
}
|
|
200
|
-
for (let [html, id] of ctx.templates) {
|
|
201
|
-
factories.push(`const ${id} = ${templateAlias}(\`${html}\`);`);
|
|
202
|
-
}
|
|
203
|
-
code = imports.modify(code, sourceFile, PACKAGE, {
|
|
204
|
-
add: new Set(aliasedImports),
|
|
205
|
-
remove: [COMPILER_ENTRYPOINT]
|
|
202
|
+
let template = rootTemplates[i];
|
|
203
|
+
result.replacements.push({
|
|
204
|
+
generate: (sf) => {
|
|
205
|
+
let codeBefore = sf.getFullText().slice(0, template.node.getStart(sf)), exprTexts = [], isArrowBody = codeBefore.trimEnd().endsWith('=>'), localCtx = {
|
|
206
|
+
checker,
|
|
207
|
+
imports: ctx.imports,
|
|
208
|
+
printer,
|
|
209
|
+
sourceFile: sf,
|
|
210
|
+
templates: ctx.templates
|
|
211
|
+
}, parsed = parser.parse(template.literals);
|
|
212
|
+
for (let j = 0, m = template.expressions.length; j < m; j++) {
|
|
213
|
+
exprTexts.push(rewriteExpression(localCtx, template.expressions[j]));
|
|
214
|
+
}
|
|
215
|
+
if (isArrowBody && (!parsed.slots || parsed.slots.length === 0)) {
|
|
216
|
+
let arrowMatch = codeBefore.match(ARROW_EMPTY_PARAMS);
|
|
217
|
+
if (arrowMatch) {
|
|
218
|
+
return getOrCreateTemplateId(localCtx, parsed.html);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return generateTemplateCode(localCtx, parsed, exprTexts, template.expressions, isArrowBody);
|
|
222
|
+
},
|
|
223
|
+
node: template.node
|
|
206
224
|
});
|
|
207
|
-
code = factories.join('\n') + '\n\n' + code;
|
|
208
|
-
}
|
|
209
|
-
return { changed, code };
|
|
210
|
-
};
|
|
211
|
-
const generateReactiveInlining = (calls, code, sourceFile, arraySlotAlias) => {
|
|
212
|
-
if (calls.length === 0) {
|
|
213
|
-
return code;
|
|
214
225
|
}
|
|
215
|
-
let
|
|
216
|
-
|
|
217
|
-
let call = calls[i];
|
|
218
|
-
replacements.push({
|
|
219
|
-
end: call.end,
|
|
220
|
-
newText: `new ${arraySlotAlias}(
|
|
221
|
-
${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)},
|
|
222
|
-
${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)}
|
|
223
|
-
)`,
|
|
224
|
-
start: call.start
|
|
225
|
-
});
|
|
226
|
+
for (let [html, id] of ctx.templates) {
|
|
227
|
+
result.prepend.push(`const ${id} = ${templateAlias}(\`${html}\`);`);
|
|
226
228
|
}
|
|
227
|
-
return
|
|
229
|
+
return result;
|
|
228
230
|
};
|
|
229
|
-
export { generateCode
|
|
231
|
+
export { generateCode };
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
};
|
|
7
|
-
declare const transform: (sourceFile: ts.SourceFile, program: ts.Program) => TransformResult;
|
|
8
|
-
export { transform };
|
|
1
|
+
import type { Plugin } from '@esportsplus/typescript/compiler';
|
|
2
|
+
import reactiveInliningPlugin from './reactive-inlining.js';
|
|
3
|
+
declare const templatePlugin: Plugin;
|
|
4
|
+
export default templatePlugin;
|
|
5
|
+
export { reactiveInliningPlugin, templatePlugin };
|
package/build/compiler/index.js
CHANGED
|
@@ -1,50 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
let changed = false, codegenChanged = false, existingAliases = new Map(), reactiveCalls = findReactiveCalls(sourceFile, checker), result = code;
|
|
22
|
-
if (reactiveCalls.length > 0) {
|
|
23
|
-
let arraySlotAlias = uid('ArraySlot');
|
|
24
|
-
changed = true;
|
|
25
|
-
existingAliases.set('ArraySlot', arraySlotAlias);
|
|
26
|
-
result = generateReactiveInlining(reactiveCalls, result, sourceFile, arraySlotAlias);
|
|
27
|
-
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
28
|
-
}
|
|
29
|
-
let templates = findHtmlTemplates(sourceFile, checker);
|
|
30
|
-
if (templates.length > 0) {
|
|
31
|
-
let codegenResult = generateCode(templates, result, sourceFile, checker, existingAliases);
|
|
32
|
-
if (codegenResult.changed) {
|
|
33
|
-
changed = true;
|
|
34
|
-
codegenChanged = true;
|
|
35
|
-
result = codegenResult.code;
|
|
1
|
+
import { COMPILER_ENTRYPOINT, PACKAGE } from '../constants.js';
|
|
2
|
+
import { generateCode } from './codegen.js';
|
|
3
|
+
import reactiveInliningPlugin, { SHARED_KEY } from './reactive-inlining.js';
|
|
4
|
+
import { findHtmlTemplates } from './ts-parser.js';
|
|
5
|
+
const PATTERNS = [`${COMPILER_ENTRYPOINT}\``];
|
|
6
|
+
const templatePlugin = {
|
|
7
|
+
patterns: PATTERNS,
|
|
8
|
+
transform: (ctx) => {
|
|
9
|
+
let templates = findHtmlTemplates(ctx.sourceFile, ctx.checker);
|
|
10
|
+
if (templates.length === 0) {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
let existingAliases = new Map(), existingArraySlotAlias = ctx.shared.get(SHARED_KEY);
|
|
14
|
+
if (existingArraySlotAlias) {
|
|
15
|
+
existingAliases.set('ArraySlot', existingArraySlotAlias);
|
|
16
|
+
}
|
|
17
|
+
let result = generateCode(templates, ctx.sourceFile, ctx.checker, existingAliases);
|
|
18
|
+
if (result.replacements.length === 0) {
|
|
19
|
+
return {};
|
|
36
20
|
}
|
|
37
|
-
}
|
|
38
|
-
if (existingAliases.size > 0 && !codegenChanged) {
|
|
39
21
|
let aliasedImports = [];
|
|
40
|
-
for (let [name, alias] of
|
|
22
|
+
for (let [name, alias] of result.imports) {
|
|
41
23
|
aliasedImports.push(`${name} as ${alias}`);
|
|
42
24
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
25
|
+
let imports = [{
|
|
26
|
+
add: aliasedImports,
|
|
27
|
+
package: PACKAGE,
|
|
28
|
+
remove: [COMPILER_ENTRYPOINT]
|
|
29
|
+
}];
|
|
30
|
+
return {
|
|
31
|
+
imports,
|
|
32
|
+
prepend: result.prepend,
|
|
33
|
+
replacements: result.replacements
|
|
34
|
+
};
|
|
47
35
|
}
|
|
48
|
-
return { changed, code: result, sourceFile };
|
|
49
36
|
};
|
|
50
|
-
export
|
|
37
|
+
export default templatePlugin;
|
|
38
|
+
export { reactiveInliningPlugin, templatePlugin };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { plugin } from '@esportsplus/typescript/compiler';
|
|
2
|
-
import {
|
|
3
|
-
export default plugin.tsc(
|
|
2
|
+
import { reactiveInliningPlugin, templatePlugin } from '../index.js';
|
|
3
|
+
export default plugin.tsc([reactiveInliningPlugin, templatePlugin]);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
declare const _default: ({ root }?: {
|
|
2
2
|
root?: string;
|
|
3
3
|
}) => {
|
|
4
|
-
configResolved(config:
|
|
5
|
-
enforce:
|
|
4
|
+
configResolved: (config: unknown) => void;
|
|
5
|
+
enforce: "pre";
|
|
6
6
|
name: string;
|
|
7
|
-
transform(code: string, id: string)
|
|
7
|
+
transform: (code: string, id: string) => {
|
|
8
8
|
code: string;
|
|
9
9
|
map: null;
|
|
10
10
|
} | null;
|
|
11
|
-
watchChange(id: string)
|
|
11
|
+
watchChange: (id: string) => void;
|
|
12
12
|
};
|
|
13
13
|
export default _default;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { plugin } from '@esportsplus/typescript/compiler';
|
|
2
2
|
import { PACKAGE } from '../../constants.js';
|
|
3
|
-
import {
|
|
3
|
+
import { reactiveInliningPlugin, templatePlugin } from '../index.js';
|
|
4
4
|
export default plugin.vite({
|
|
5
5
|
name: PACKAGE,
|
|
6
|
-
|
|
6
|
+
plugins: [reactiveInliningPlugin, templatePlugin]
|
|
7
7
|
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { uid } from '@esportsplus/typescript/compiler';
|
|
3
|
+
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, PACKAGE } from '../constants.js';
|
|
4
|
+
const PATTERNS = [`${COMPILER_ENTRYPOINT}.${COMPILER_ENTRYPOINT_REACTIVITY}`];
|
|
5
|
+
const SHARED_KEY = 'template:arraySlotAlias';
|
|
6
|
+
let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
7
|
+
function isHtmlFromPackage(node, checker) {
|
|
8
|
+
if (node.text !== COMPILER_ENTRYPOINT) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
if (!checker) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
let symbol = checker.getSymbolAtLocation(node);
|
|
15
|
+
if (!symbol) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
19
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
20
|
+
}
|
|
21
|
+
let declarations = symbol.getDeclarations();
|
|
22
|
+
if (!declarations || declarations.length === 0) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
for (let i = 0, n = declarations.length; i < n; i++) {
|
|
26
|
+
let filename = declarations[i].getSourceFile().fileName;
|
|
27
|
+
if (filename.includes(PACKAGE) || filename.includes('@esportsplus/template')) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
function visit(node, calls, checker) {
|
|
34
|
+
if (ts.isCallExpression(node) &&
|
|
35
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
36
|
+
ts.isIdentifier(node.expression.expression) &&
|
|
37
|
+
node.expression.name.text === COMPILER_ENTRYPOINT_REACTIVITY &&
|
|
38
|
+
node.arguments.length === 2 &&
|
|
39
|
+
isHtmlFromPackage(node.expression.expression, checker)) {
|
|
40
|
+
calls.push({
|
|
41
|
+
arrayArg: node.arguments[0],
|
|
42
|
+
callbackArg: node.arguments[1],
|
|
43
|
+
node
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
ts.forEachChild(node, child => visit(child, calls, checker));
|
|
47
|
+
}
|
|
48
|
+
const plugin = {
|
|
49
|
+
patterns: PATTERNS,
|
|
50
|
+
transform: (ctx) => {
|
|
51
|
+
let calls = [];
|
|
52
|
+
visit(ctx.sourceFile, calls, ctx.checker);
|
|
53
|
+
if (calls.length === 0) {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
let arraySlotAlias = uid('ArraySlot'), replacements = [];
|
|
57
|
+
ctx.shared.set(SHARED_KEY, arraySlotAlias);
|
|
58
|
+
for (let i = 0, n = calls.length; i < n; i++) {
|
|
59
|
+
let call = calls[i];
|
|
60
|
+
replacements.push({
|
|
61
|
+
generate: (sourceFile) => `new ${arraySlotAlias}(${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)}, ${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)})`,
|
|
62
|
+
node: call.node
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
imports: [{
|
|
67
|
+
add: [`ArraySlot as ${arraySlotAlias}`],
|
|
68
|
+
package: PACKAGE
|
|
69
|
+
}],
|
|
70
|
+
replacements
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
export default plugin;
|
|
75
|
+
export { SHARED_KEY };
|
|
@@ -19,8 +19,8 @@ function isHtmlFromPackage(node, checker) {
|
|
|
19
19
|
return true;
|
|
20
20
|
}
|
|
21
21
|
for (let i = 0, n = declarations.length; i < n; i++) {
|
|
22
|
-
let
|
|
23
|
-
if (
|
|
22
|
+
let filename = declarations[i].getSourceFile().fileName;
|
|
23
|
+
if (filename.includes(PACKAGE) || filename.includes('@esportsplus/template')) {
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
26
26
|
}
|
package/package.json
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
"author": "ICJR",
|
|
3
3
|
"dependencies": {
|
|
4
4
|
"@esportsplus/queue": "^0.2.0",
|
|
5
|
-
"@esportsplus/reactivity": "^0.
|
|
5
|
+
"@esportsplus/reactivity": "^0.28.0",
|
|
6
|
+
"@esportsplus/typescript": "^0.25.0",
|
|
6
7
|
"@esportsplus/utilities": "^0.27.2",
|
|
7
8
|
"serve": "^14.2.5"
|
|
8
9
|
},
|
|
9
10
|
"devDependencies": {
|
|
10
|
-
"@esportsplus/typescript": "^0.22.0",
|
|
11
11
|
"@types/node": "^25.0.3",
|
|
12
|
-
"vite": "^7.3.
|
|
12
|
+
"vite": "^7.3.1",
|
|
13
13
|
"vite-tsconfig-paths": "^6.0.3"
|
|
14
14
|
},
|
|
15
15
|
"exports": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"type": "module",
|
|
37
37
|
"types": "./build/index.d.ts",
|
|
38
|
-
"version": "0.
|
|
38
|
+
"version": "0.38.0",
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "tsc",
|
|
41
41
|
"build:test": "vite build --config test/vite.config.ts",
|