@esportsplus/template 0.34.1 → 0.35.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/attributes.d.ts +3 -3
- package/build/attributes.js +4 -1
- package/build/compiler/codegen.d.ts +3 -5
- package/build/compiler/codegen.js +48 -61
- package/build/compiler/index.d.ts +0 -6
- package/build/compiler/index.js +16 -17
- package/build/compiler/ts-parser.d.ts +2 -2
- package/build/compiler/ts-parser.js +38 -12
- package/build/compiler/type-analyzer.d.ts +2 -2
- package/build/compiler/type-analyzer.js +17 -105
- package/build/constants.d.ts +1 -3
- package/build/constants.js +1 -4
- package/build/index.d.ts +6 -1
- package/build/index.js +11 -1
- package/build/utilities.js +1 -1
- package/package.json +7 -13
- package/src/attributes.ts +9 -6
- package/src/compiler/codegen.ts +65 -88
- package/src/compiler/index.ts +21 -19
- package/src/compiler/parser.ts +1 -1
- package/src/compiler/ts-parser.ts +53 -12
- package/src/compiler/type-analyzer.ts +24 -142
- package/src/constants.ts +3 -12
- package/src/index.ts +16 -1
- package/src/utilities.ts +2 -2
- package/test/vite.config.ts +1 -1
- package/build/runtime.d.ts +0 -1
- package/build/runtime.js +0 -5
- package/src/runtime.ts +0 -8
package/build/attributes.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Attributes, Element } from './types.js';
|
|
2
|
-
declare const setClass: (element: Element, classlist: false | string
|
|
2
|
+
declare const setClass: (element: Element, classlist: false | string, value: unknown) => void;
|
|
3
3
|
declare const setProperty: (element: Element, name: string, value: unknown) => void;
|
|
4
|
-
declare const setProperties: (element: Element, value
|
|
5
|
-
declare const setStyle: (element: Element, styles: false | string
|
|
4
|
+
declare const setProperties: (element: Element, value?: Attributes | Attributes[] | false | null | undefined) => void;
|
|
5
|
+
declare const setStyle: (element: Element, styles: false | string, value: unknown) => void;
|
|
6
6
|
export { setClass, setProperty, setProperties, setStyle };
|
package/build/attributes.js
CHANGED
|
@@ -199,7 +199,10 @@ const setProperty = (element, name, value) => {
|
|
|
199
199
|
}
|
|
200
200
|
};
|
|
201
201
|
const setProperties = function (element, value) {
|
|
202
|
-
if (
|
|
202
|
+
if (!value) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
else if (isObject(value)) {
|
|
203
206
|
for (let name in value) {
|
|
204
207
|
let v = value[name];
|
|
205
208
|
if (v == null || v === false || v === '') {
|
|
@@ -4,9 +4,7 @@ type CodegenResult = {
|
|
|
4
4
|
changed: boolean;
|
|
5
5
|
code: string;
|
|
6
6
|
};
|
|
7
|
-
declare const
|
|
8
|
-
declare const
|
|
9
|
-
|
|
10
|
-
declare const needsArraySlotImport: (sourceFile: ts.SourceFile) => boolean;
|
|
11
|
-
export { addImport, generateCode, generateReactiveInlining, needsArraySlotImport };
|
|
7
|
+
declare const generateCode: (templates: TemplateInfo[], originalCode: string, sourceFile: ts.SourceFile, checker?: ts.TypeChecker, existingAliases?: Map<string, string>) => CodegenResult;
|
|
8
|
+
declare const generateReactiveInlining: (calls: ReactiveCallInfo[], code: string, sourceFile: ts.SourceFile, arraySlotAlias: string) => string;
|
|
9
|
+
export { generateCode, generateReactiveInlining };
|
|
12
10
|
export type { CodegenResult };
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import { ast, code as c, uid } from '@esportsplus/typescript/compiler';
|
|
2
|
+
import { ast, code as c, imports, uid } from '@esportsplus/typescript/compiler';
|
|
3
|
+
import { COMPILER_ENTRYPOINT, COMPILER_TYPES, PACKAGE } from '../constants.js';
|
|
3
4
|
import { analyzeExpression, generateAttributeBinding, generateSpreadBindings } from './type-analyzer.js';
|
|
4
|
-
import { COMPILER_ENTRYPOINT, COMPILER_NAMESPACE, COMPILER_TYPES, PACKAGE, PACKAGE_COMPILER } from '../constants.js';
|
|
5
5
|
import parser from './parser.js';
|
|
6
6
|
const ARROW_EMPTY_PARAMS = /\(\s*\)\s*=>\s*$/;
|
|
7
|
-
const REGEX_PACKAGE_IMPORT = new RegExp(`import\\s*\\{[^}]*\\}\\s*from\\s*['"]${PACKAGE.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"];?\\n?`, 'g');
|
|
8
7
|
let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
8
|
+
function addImport(ctx, name) {
|
|
9
|
+
let alias = ctx.imports.get(name);
|
|
10
|
+
if (!alias) {
|
|
11
|
+
alias = uid(name);
|
|
12
|
+
ctx.imports.set(name, alias);
|
|
13
|
+
}
|
|
14
|
+
return alias;
|
|
15
|
+
}
|
|
9
16
|
function collectNestedTemplateReplacements(ctx, node, exprStart, replacements) {
|
|
10
17
|
if (isNestedHtmlTemplate(node)) {
|
|
11
18
|
replacements.push({
|
|
@@ -36,7 +43,7 @@ function generateNestedTemplateCode(ctx, node) {
|
|
|
36
43
|
}
|
|
37
44
|
function generateNodeBinding(ctx, anchor, exprText, exprNode) {
|
|
38
45
|
if (!exprNode) {
|
|
39
|
-
return `${
|
|
46
|
+
return `${addImport(ctx, 'slot')}(${anchor}, ${exprText});`;
|
|
40
47
|
}
|
|
41
48
|
if (isNestedHtmlTemplate(exprNode)) {
|
|
42
49
|
return `${anchor}.parentNode.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
|
|
@@ -44,15 +51,15 @@ function generateNodeBinding(ctx, anchor, exprText, exprNode) {
|
|
|
44
51
|
let slotType = analyzeExpression(exprNode, ctx.checker);
|
|
45
52
|
switch (slotType) {
|
|
46
53
|
case COMPILER_TYPES.Effect:
|
|
47
|
-
return `new ${
|
|
54
|
+
return `new ${addImport(ctx, 'EffectSlot')}(${anchor}, ${exprText});`;
|
|
48
55
|
case COMPILER_TYPES.ArraySlot:
|
|
49
|
-
return `new ${
|
|
56
|
+
return `new ${addImport(ctx, 'ArraySlot')}(${anchor}, ${exprText});`;
|
|
50
57
|
case COMPILER_TYPES.Static:
|
|
51
58
|
return `${anchor}.textContent = ${exprText};`;
|
|
52
59
|
case COMPILER_TYPES.DocumentFragment:
|
|
53
60
|
return `${anchor}.parentNode.insertBefore(${exprText}, ${anchor});`;
|
|
54
61
|
default:
|
|
55
|
-
return `${
|
|
62
|
+
return `${addImport(ctx, 'slot')}(${anchor}, ${exprText});`;
|
|
56
63
|
}
|
|
57
64
|
}
|
|
58
65
|
function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArrowBody) {
|
|
@@ -71,22 +78,25 @@ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArro
|
|
|
71
78
|
if (nodes.has(key)) {
|
|
72
79
|
continue;
|
|
73
80
|
}
|
|
74
|
-
let
|
|
81
|
+
let ancestor = root, start = 0;
|
|
75
82
|
for (let j = path.length - 1; j >= 0; j--) {
|
|
76
83
|
let prefix = path.slice(0, j).join('.');
|
|
77
84
|
if (nodes.has(prefix)) {
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
ancestor = nodes.get(prefix);
|
|
86
|
+
start = j;
|
|
80
87
|
break;
|
|
81
88
|
}
|
|
82
89
|
}
|
|
83
|
-
let name = uid('element'),
|
|
84
|
-
|
|
90
|
+
let alias = addImport(ctx, 'Element'), name = uid('element'), segments = path.slice(start), value = `${ancestor}.${segments.join('!.')}`;
|
|
91
|
+
if (ancestor === root && segments[0] === 'firstChild') {
|
|
92
|
+
value = value.replace(`${ancestor}.firstChild!`, `(${ancestor}.firstChild! as ${alias})`);
|
|
93
|
+
}
|
|
94
|
+
declarations.push(`${name} = ${value} as ${alias}`);
|
|
85
95
|
nodes.set(key, name);
|
|
86
96
|
}
|
|
87
97
|
code.push(isArrowBody ? '{' : `(() => {`, `let ${declarations.join(',\n')};`);
|
|
88
98
|
for (let i = 0, n = slots.length; i < n; i++) {
|
|
89
|
-
let
|
|
99
|
+
let element = slots[i].path.length === 0
|
|
90
100
|
? root
|
|
91
101
|
: (nodes.get(slots[i].path.join('.')) || root), slot = slots[i];
|
|
92
102
|
if (slot.type === COMPILER_TYPES.Attribute) {
|
|
@@ -94,19 +104,19 @@ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArro
|
|
|
94
104
|
for (let j = 0, m = names.length; j < m; j++) {
|
|
95
105
|
let name = names[j];
|
|
96
106
|
if (name === COMPILER_TYPES.Attributes) {
|
|
97
|
-
let bindings = generateSpreadBindings(
|
|
107
|
+
let bindings = generateSpreadBindings(exprTexts[index] || 'undefined', element, n => addImport(ctx, n));
|
|
98
108
|
for (let k = 0, o = bindings.length; k < o; k++) {
|
|
99
109
|
code.push(bindings[k]);
|
|
100
110
|
}
|
|
101
111
|
index++;
|
|
102
112
|
}
|
|
103
113
|
else {
|
|
104
|
-
code.push(generateAttributeBinding(
|
|
114
|
+
code.push(generateAttributeBinding(element, name, exprTexts[index++] || 'undefined', slot.attributes.statics[name] || '', n => addImport(ctx, n)));
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
117
|
}
|
|
108
118
|
else {
|
|
109
|
-
code.push(generateNodeBinding(ctx,
|
|
119
|
+
code.push(generateNodeBinding(ctx, element, exprTexts[index] || 'undefined', exprNodes[index]));
|
|
110
120
|
index++;
|
|
111
121
|
}
|
|
112
122
|
}
|
|
@@ -115,32 +125,13 @@ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArro
|
|
|
115
125
|
return code.join('\n');
|
|
116
126
|
}
|
|
117
127
|
function getOrCreateTemplateId(ctx, html) {
|
|
118
|
-
let id = ctx.
|
|
128
|
+
let id = ctx.templates.get(html);
|
|
119
129
|
if (!id) {
|
|
120
|
-
id = uid('
|
|
121
|
-
ctx.
|
|
122
|
-
ctx.htmlToTemplateId.set(html, id);
|
|
130
|
+
id = uid('template');
|
|
131
|
+
ctx.templates.set(html, id);
|
|
123
132
|
}
|
|
124
133
|
return id;
|
|
125
134
|
}
|
|
126
|
-
function hasArraySlotImport(sourceFile) {
|
|
127
|
-
for (let i = 0, n = sourceFile.statements.length; i < n; i++) {
|
|
128
|
-
let stmt = sourceFile.statements[i];
|
|
129
|
-
if (!ts.isImportDeclaration(stmt) || !stmt.importClause?.namedBindings) {
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
let bindings = stmt.importClause.namedBindings;
|
|
133
|
-
if (!ts.isNamedImports(bindings)) {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
for (let j = 0, m = bindings.elements.length; j < m; j++) {
|
|
137
|
-
if (bindings.elements[j].name.text === 'ArraySlot') {
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
135
|
function isNestedHtmlTemplate(expr) {
|
|
145
136
|
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
|
|
146
137
|
}
|
|
@@ -155,13 +146,7 @@ function rewriteExpression(ctx, expr) {
|
|
|
155
146
|
collectNestedTemplateReplacements(ctx, expr, expr.getStart(), replacements);
|
|
156
147
|
return c.replaceReverse(expr.getText(ctx.sourceFile), replacements);
|
|
157
148
|
}
|
|
158
|
-
const
|
|
159
|
-
return `import * as ${COMPILER_NAMESPACE} from '${PACKAGE_COMPILER}';\n` + code;
|
|
160
|
-
};
|
|
161
|
-
const removePackageImport = (code) => {
|
|
162
|
-
return code.replace(REGEX_PACKAGE_IMPORT, '');
|
|
163
|
-
};
|
|
164
|
-
const generateCode = (templates, originalCode, sourceFile, checker) => {
|
|
149
|
+
const generateCode = (templates, originalCode, sourceFile, checker, existingAliases) => {
|
|
165
150
|
if (templates.length === 0) {
|
|
166
151
|
return { changed: false, code: originalCode };
|
|
167
152
|
}
|
|
@@ -178,11 +163,11 @@ const generateCode = (templates, originalCode, sourceFile, checker) => {
|
|
|
178
163
|
}
|
|
179
164
|
let ctx = {
|
|
180
165
|
checker,
|
|
181
|
-
|
|
182
|
-
|
|
166
|
+
templates: new Map(),
|
|
167
|
+
imports: existingAliases ?? new Map(),
|
|
183
168
|
printer,
|
|
184
169
|
sourceFile
|
|
185
|
-
}, replacements = [];
|
|
170
|
+
}, replacements = [], templateAlias = addImport(ctx, 'template');
|
|
186
171
|
for (let i = 0, n = rootTemplates.length; i < n; i++) {
|
|
187
172
|
let exprTexts = [], template = rootTemplates[i];
|
|
188
173
|
for (let j = 0, m = template.expressions.length; j < m; j++) {
|
|
@@ -207,16 +192,23 @@ const generateCode = (templates, originalCode, sourceFile, checker) => {
|
|
|
207
192
|
});
|
|
208
193
|
}
|
|
209
194
|
let changed = replacements.length > 0, code = c.replaceReverse(originalCode, replacements);
|
|
210
|
-
if (changed && ctx.
|
|
211
|
-
let factories = [];
|
|
212
|
-
for (let [
|
|
213
|
-
|
|
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}\`);`);
|
|
214
202
|
}
|
|
215
|
-
code =
|
|
203
|
+
code = imports.modify(code, sourceFile, PACKAGE, {
|
|
204
|
+
add: new Set(aliasedImports),
|
|
205
|
+
remove: [COMPILER_ENTRYPOINT]
|
|
206
|
+
});
|
|
207
|
+
code = factories.join('\n') + '\n\n' + code;
|
|
216
208
|
}
|
|
217
209
|
return { changed, code };
|
|
218
210
|
};
|
|
219
|
-
const generateReactiveInlining = (calls, code, sourceFile) => {
|
|
211
|
+
const generateReactiveInlining = (calls, code, sourceFile, arraySlotAlias) => {
|
|
220
212
|
if (calls.length === 0) {
|
|
221
213
|
return code;
|
|
222
214
|
}
|
|
@@ -225,7 +217,7 @@ const generateReactiveInlining = (calls, code, sourceFile) => {
|
|
|
225
217
|
let call = calls[i];
|
|
226
218
|
replacements.push({
|
|
227
219
|
end: call.end,
|
|
228
|
-
newText: `new ${
|
|
220
|
+
newText: `new ${arraySlotAlias}(
|
|
229
221
|
${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)},
|
|
230
222
|
${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)}
|
|
231
223
|
)`,
|
|
@@ -234,9 +226,4 @@ const generateReactiveInlining = (calls, code, sourceFile) => {
|
|
|
234
226
|
}
|
|
235
227
|
return c.replaceReverse(code, replacements);
|
|
236
228
|
};
|
|
237
|
-
|
|
238
|
-
return ast.hasMatch(sourceFile, n => ts.isNewExpression(n) &&
|
|
239
|
-
ts.isPropertyAccessExpression(n.expression) &&
|
|
240
|
-
n.expression.name.text === 'ArraySlot') && !hasArraySlotImport(sourceFile);
|
|
241
|
-
};
|
|
242
|
-
export { addImport, generateCode, generateReactiveInlining, needsArraySlotImport };
|
|
229
|
+
export { generateCode, generateReactiveInlining };
|
|
@@ -6,9 +6,3 @@ type TransformResult = {
|
|
|
6
6
|
};
|
|
7
7
|
declare const transform: (sourceFile: ts.SourceFile, program: ts.Program) => TransformResult;
|
|
8
8
|
export { transform };
|
|
9
|
-
export * from '../attributes.js';
|
|
10
|
-
export * from '../event/index.js';
|
|
11
|
-
export { ArraySlot } from '../slot/array.js';
|
|
12
|
-
export { EffectSlot } from '../slot/effect.js';
|
|
13
|
-
export type * from '../types.js';
|
|
14
|
-
export * from '../utilities.js';
|
package/build/compiler/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { code as c } from '@esportsplus/typescript/compiler';
|
|
2
|
-
import { addImport, generateCode, generateReactiveInlining, needsArraySlotImport } from './codegen.js';
|
|
3
|
-
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY } from '../constants.js';
|
|
4
|
-
import { findHtmlTemplates, findReactiveCalls } from './ts-parser.js';
|
|
5
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { code as c, imports, uid } from '@esportsplus/typescript/compiler';
|
|
3
|
+
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, PACKAGE } from '../constants.js';
|
|
4
|
+
import { generateCode, generateReactiveInlining } from './codegen.js';
|
|
5
|
+
import { findHtmlTemplates, findReactiveCalls } from './ts-parser.js';
|
|
6
6
|
const PATTERNS = [`${COMPILER_ENTRYPOINT}\``, `${COMPILER_ENTRYPOINT}.${COMPILER_ENTRYPOINT_REACTIVITY}`];
|
|
7
7
|
const REGEX_BACKSLASH = /\\/g;
|
|
8
8
|
const REGEX_FORWARD_SLASH = /\//g;
|
|
@@ -18,25 +18,29 @@ const transform = (sourceFile, program) => {
|
|
|
18
18
|
checker = program.getTypeChecker();
|
|
19
19
|
sourceFile = programSourceFile;
|
|
20
20
|
}
|
|
21
|
-
let changed = false, codegenChanged = false,
|
|
21
|
+
let changed = false, codegenChanged = false, existingAliases = new Map(), reactiveCalls = findReactiveCalls(sourceFile, checker), result = code;
|
|
22
22
|
if (reactiveCalls.length > 0) {
|
|
23
|
+
let arraySlotAlias = uid('ArraySlot');
|
|
23
24
|
changed = true;
|
|
24
|
-
|
|
25
|
-
result = generateReactiveInlining(reactiveCalls, result, sourceFile);
|
|
25
|
+
existingAliases.set('ArraySlot', arraySlotAlias);
|
|
26
|
+
result = generateReactiveInlining(reactiveCalls, result, sourceFile, arraySlotAlias);
|
|
26
27
|
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
27
|
-
needsImport = needsArraySlotImport(sourceFile);
|
|
28
28
|
}
|
|
29
|
-
let templates = findHtmlTemplates(sourceFile);
|
|
29
|
+
let templates = findHtmlTemplates(sourceFile, checker);
|
|
30
30
|
if (templates.length > 0) {
|
|
31
|
-
let codegenResult = generateCode(templates, result, sourceFile, checker);
|
|
31
|
+
let codegenResult = generateCode(templates, result, sourceFile, checker, existingAliases);
|
|
32
32
|
if (codegenResult.changed) {
|
|
33
33
|
changed = true;
|
|
34
34
|
codegenChanged = true;
|
|
35
35
|
result = codegenResult.code;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
if (
|
|
39
|
-
|
|
38
|
+
if (existingAliases.size > 0 && !codegenChanged) {
|
|
39
|
+
let aliasedImports = [];
|
|
40
|
+
for (let [name, alias] of existingAliases) {
|
|
41
|
+
aliasedImports.push(`${name} as ${alias}`);
|
|
42
|
+
}
|
|
43
|
+
result = imports.modify(result, sourceFile, PACKAGE, { add: aliasedImports });
|
|
40
44
|
}
|
|
41
45
|
if (changed) {
|
|
42
46
|
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
@@ -44,8 +48,3 @@ const transform = (sourceFile, program) => {
|
|
|
44
48
|
return { changed, code: result, sourceFile };
|
|
45
49
|
};
|
|
46
50
|
export { transform };
|
|
47
|
-
export * from '../attributes.js';
|
|
48
|
-
export * from '../event/index.js';
|
|
49
|
-
export { ArraySlot } from '../slot/array.js';
|
|
50
|
-
export { EffectSlot } from '../slot/effect.js';
|
|
51
|
-
export * from '../utilities.js';
|
|
@@ -14,7 +14,7 @@ type TemplateInfo = {
|
|
|
14
14
|
node: ts.TaggedTemplateExpression;
|
|
15
15
|
start: number;
|
|
16
16
|
};
|
|
17
|
-
declare const findHtmlTemplates: (sourceFile: ts.SourceFile) => TemplateInfo[];
|
|
18
|
-
declare const findReactiveCalls: (sourceFile: ts.SourceFile) => ReactiveCallInfo[];
|
|
17
|
+
declare const findHtmlTemplates: (sourceFile: ts.SourceFile, checker?: ts.TypeChecker) => TemplateInfo[];
|
|
18
|
+
declare const findReactiveCalls: (sourceFile: ts.SourceFile, checker?: ts.TypeChecker) => ReactiveCallInfo[];
|
|
19
19
|
export { findHtmlTemplates, findReactiveCalls };
|
|
20
20
|
export type { ReactiveCallInfo, TemplateInfo };
|
|
@@ -1,12 +1,38 @@
|
|
|
1
|
-
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY } from '../constants.js';
|
|
2
1
|
import { ts } from '@esportsplus/typescript';
|
|
3
|
-
|
|
2
|
+
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, PACKAGE } from '../constants.js';
|
|
3
|
+
function isHtmlFromPackage(node, checker) {
|
|
4
|
+
if (node.text !== COMPILER_ENTRYPOINT) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
if (!checker) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
let symbol = checker.getSymbolAtLocation(node);
|
|
11
|
+
if (!symbol) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
15
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
16
|
+
}
|
|
17
|
+
let declarations = symbol.getDeclarations();
|
|
18
|
+
if (!declarations || declarations.length === 0) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
for (let i = 0, n = declarations.length; i < n; i++) {
|
|
22
|
+
let fileName = declarations[i].getSourceFile().fileName;
|
|
23
|
+
if (fileName.includes(PACKAGE) || fileName.includes('@esportsplus/template')) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
function visitReactiveCalls(node, calls, checker) {
|
|
4
30
|
if (ts.isCallExpression(node) &&
|
|
5
31
|
ts.isPropertyAccessExpression(node.expression) &&
|
|
6
32
|
ts.isIdentifier(node.expression.expression) &&
|
|
7
|
-
node.expression.expression.text === COMPILER_ENTRYPOINT &&
|
|
8
33
|
node.expression.name.text === COMPILER_ENTRYPOINT_REACTIVITY &&
|
|
9
|
-
node.arguments.length === 2
|
|
34
|
+
node.arguments.length === 2 &&
|
|
35
|
+
isHtmlFromPackage(node.expression.expression, checker)) {
|
|
10
36
|
calls.push({
|
|
11
37
|
arrayArg: node.arguments[0],
|
|
12
38
|
callbackArg: node.arguments[1],
|
|
@@ -15,13 +41,13 @@ function visitReactiveCalls(node, calls) {
|
|
|
15
41
|
start: node.getStart()
|
|
16
42
|
});
|
|
17
43
|
}
|
|
18
|
-
ts.forEachChild(node, child => visitReactiveCalls(child, calls));
|
|
44
|
+
ts.forEachChild(node, child => visitReactiveCalls(child, calls, checker));
|
|
19
45
|
}
|
|
20
|
-
function visitTemplates(node, depth, templates) {
|
|
46
|
+
function visitTemplates(node, depth, templates, checker) {
|
|
21
47
|
let nextDepth = (ts.isArrowFunction(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isMethodDeclaration(node))
|
|
22
48
|
? depth + 1
|
|
23
49
|
: depth;
|
|
24
|
-
if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && node.tag
|
|
50
|
+
if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && isHtmlFromPackage(node.tag, checker)) {
|
|
25
51
|
let expressions = [], literals = [], template = node.template;
|
|
26
52
|
if (ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
27
53
|
literals.push(template.text);
|
|
@@ -43,17 +69,17 @@ function visitTemplates(node, depth, templates) {
|
|
|
43
69
|
start: node.getStart()
|
|
44
70
|
});
|
|
45
71
|
}
|
|
46
|
-
ts.forEachChild(node, child => visitTemplates(child, nextDepth, templates));
|
|
72
|
+
ts.forEachChild(node, child => visitTemplates(child, nextDepth, templates, checker));
|
|
47
73
|
}
|
|
48
|
-
const findHtmlTemplates = (sourceFile) => {
|
|
74
|
+
const findHtmlTemplates = (sourceFile, checker) => {
|
|
49
75
|
let templates = [];
|
|
50
|
-
visitTemplates(sourceFile, 0, templates);
|
|
76
|
+
visitTemplates(sourceFile, 0, templates, checker);
|
|
51
77
|
templates.sort((a, b) => a.depth !== b.depth ? b.depth - a.depth : a.start - b.start);
|
|
52
78
|
return templates;
|
|
53
79
|
};
|
|
54
|
-
const findReactiveCalls = (sourceFile) => {
|
|
80
|
+
const findReactiveCalls = (sourceFile, checker) => {
|
|
55
81
|
let calls = [];
|
|
56
|
-
visitReactiveCalls(sourceFile, calls);
|
|
82
|
+
visitReactiveCalls(sourceFile, calls, checker);
|
|
57
83
|
return calls;
|
|
58
84
|
};
|
|
59
85
|
export { findHtmlTemplates, findReactiveCalls };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { COMPILER_TYPES } from '../constants.js';
|
|
2
2
|
import { ts } from '@esportsplus/typescript';
|
|
3
3
|
declare const analyzeExpression: (expr: ts.Expression, checker?: ts.TypeChecker) => COMPILER_TYPES;
|
|
4
|
-
declare const generateAttributeBinding: (elementVar: string, name: string, expr: string, staticValue: string,
|
|
5
|
-
declare const generateSpreadBindings: (
|
|
4
|
+
declare const generateAttributeBinding: (elementVar: string, name: string, expr: string, staticValue: string, addImport: (name: string) => string) => string;
|
|
5
|
+
declare const generateSpreadBindings: (exprCode: string, elementVar: string, addImport: (name: string) => string) => string[];
|
|
6
6
|
export { analyzeExpression, generateAttributeBinding, generateSpreadBindings };
|
|
@@ -1,45 +1,5 @@
|
|
|
1
1
|
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../constants.js';
|
|
2
2
|
import { ts } from '@esportsplus/typescript';
|
|
3
|
-
function analyzeSpread(expr, checker) {
|
|
4
|
-
while (ts.isParenthesizedExpression(expr)) {
|
|
5
|
-
expr = expr.expression;
|
|
6
|
-
}
|
|
7
|
-
if (ts.isObjectLiteralExpression(expr)) {
|
|
8
|
-
let keys = [];
|
|
9
|
-
for (let i = 0, n = expr.properties.length; i < n; i++) {
|
|
10
|
-
let prop = expr.properties[i];
|
|
11
|
-
if (ts.isPropertyAssignment(prop)) {
|
|
12
|
-
if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) {
|
|
13
|
-
keys.push(prop.name.text);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
else if (ts.isShorthandPropertyAssignment(prop)) {
|
|
17
|
-
keys.push(prop.name.text);
|
|
18
|
-
}
|
|
19
|
-
else if (ts.isSpreadAssignment(prop)) {
|
|
20
|
-
return { canUnpack: false, keys: [] };
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return { canUnpack: true, keys };
|
|
24
|
-
}
|
|
25
|
-
if (checker && (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr))) {
|
|
26
|
-
try {
|
|
27
|
-
let keys = [], props = checker.getTypeAtLocation(expr).getProperties();
|
|
28
|
-
for (let i = 0, n = props.length; i < n; i++) {
|
|
29
|
-
let name = props[i].getName();
|
|
30
|
-
if (name.startsWith('__') || name.startsWith('[')) {
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
keys.push(name);
|
|
34
|
-
}
|
|
35
|
-
if (keys.length > 0) {
|
|
36
|
-
return { canUnpack: true, keys };
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
catch { }
|
|
40
|
-
}
|
|
41
|
-
return { canUnpack: false, keys: [] };
|
|
42
|
-
}
|
|
43
3
|
function inferCOMPILER_TYPES(expr, checker) {
|
|
44
4
|
while (ts.isParenthesizedExpression(expr)) {
|
|
45
5
|
expr = expr.expression;
|
|
@@ -57,9 +17,6 @@ function inferCOMPILER_TYPES(expr, checker) {
|
|
|
57
17
|
if (ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT) {
|
|
58
18
|
return COMPILER_TYPES.DocumentFragment;
|
|
59
19
|
}
|
|
60
|
-
if (ts.isArrayLiteralExpression(expr)) {
|
|
61
|
-
return COMPILER_TYPES.ArraySlot;
|
|
62
|
-
}
|
|
63
20
|
if (ts.isNumericLiteral(expr) ||
|
|
64
21
|
ts.isStringLiteral(expr) ||
|
|
65
22
|
ts.isNoSubstitutionTemplateLiteral(expr) ||
|
|
@@ -88,98 +45,53 @@ function inferCOMPILER_TYPES(expr, checker) {
|
|
|
88
45
|
if (isTypeFunction(type, checker)) {
|
|
89
46
|
return COMPILER_TYPES.Effect;
|
|
90
47
|
}
|
|
91
|
-
if (isTypeArray(type, checker)) {
|
|
92
|
-
return COMPILER_TYPES.ArraySlot;
|
|
93
|
-
}
|
|
94
48
|
}
|
|
95
49
|
catch {
|
|
96
50
|
}
|
|
97
51
|
}
|
|
98
52
|
return COMPILER_TYPES.Unknown;
|
|
99
53
|
}
|
|
100
|
-
function isTypeArray(type, checker) {
|
|
101
|
-
if (checker.isArrayType(type)) {
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
return type.getSymbol()?.getName() === 'ReactiveArray';
|
|
105
|
-
}
|
|
106
54
|
function isTypeFunction(type, checker) {
|
|
107
|
-
if (type.getCallSignatures().length > 0) {
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
55
|
if (type.isUnion()) {
|
|
56
|
+
let allFunctions = true, hasFunction = false;
|
|
111
57
|
for (let i = 0, n = type.types.length; i < n; i++) {
|
|
112
58
|
if (isTypeFunction(type.types[i], checker)) {
|
|
113
|
-
|
|
59
|
+
hasFunction = true;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
allFunctions = false;
|
|
114
63
|
}
|
|
115
64
|
}
|
|
65
|
+
return hasFunction && allFunctions;
|
|
116
66
|
}
|
|
117
|
-
return
|
|
67
|
+
return type.getCallSignatures().length > 0;
|
|
118
68
|
}
|
|
119
69
|
const analyzeExpression = (expr, checker) => {
|
|
120
70
|
return inferCOMPILER_TYPES(expr, checker);
|
|
121
71
|
};
|
|
122
|
-
const generateAttributeBinding = (elementVar, name, expr, staticValue,
|
|
72
|
+
const generateAttributeBinding = (elementVar, name, expr, staticValue, addImport) => {
|
|
123
73
|
if (name.startsWith('on') && name.length > 2) {
|
|
124
74
|
let event = name.slice(2).toLowerCase(), key = name.toLowerCase();
|
|
125
75
|
if (LIFECYCLE_EVENTS.has(key)) {
|
|
126
|
-
return `${
|
|
76
|
+
return `${addImport(key)}(${elementVar}, ${expr});`;
|
|
127
77
|
}
|
|
128
78
|
if (DIRECT_ATTACH_EVENTS.has(key)) {
|
|
129
|
-
return `${
|
|
79
|
+
return `${addImport('on')}(${elementVar}, '${event}', ${expr});`;
|
|
130
80
|
}
|
|
131
|
-
return `${
|
|
81
|
+
return `${addImport('delegate')}(${elementVar}, '${event}', ${expr});`;
|
|
132
82
|
}
|
|
133
83
|
if (name === 'class') {
|
|
134
|
-
return `${
|
|
84
|
+
return `${addImport('setClass')}(${elementVar}, '${staticValue}', ${expr});`;
|
|
135
85
|
}
|
|
136
86
|
if (name === COMPILER_TYPES.Attributes) {
|
|
137
|
-
return `${
|
|
87
|
+
return `${addImport('setProperties')}(${elementVar}, ${expr});`;
|
|
138
88
|
}
|
|
139
89
|
if (name === 'style') {
|
|
140
|
-
return `${
|
|
90
|
+
return `${addImport('setStyle')}(${elementVar}, '${staticValue}', ${expr});`;
|
|
141
91
|
}
|
|
142
|
-
return `${
|
|
92
|
+
return `${addImport('setProperty')}(${elementVar}, '${name}', ${expr});`;
|
|
143
93
|
};
|
|
144
|
-
const generateSpreadBindings = (
|
|
145
|
-
|
|
146
|
-
expr = expr.expression;
|
|
147
|
-
}
|
|
148
|
-
let analysis = analyzeSpread(expr, checker);
|
|
149
|
-
if (!analysis.canUnpack) {
|
|
150
|
-
return [`${ns}.setProperties(${elementVar}, ${exprCode});`];
|
|
151
|
-
}
|
|
152
|
-
let lines = [];
|
|
153
|
-
if (ts.isObjectLiteralExpression(expr)) {
|
|
154
|
-
for (let i = 0, n = analysis.keys.length; i < n; i++) {
|
|
155
|
-
let key = analysis.keys[i], value = null;
|
|
156
|
-
for (let j = 0, m = expr.properties.length; j < m; j++) {
|
|
157
|
-
let prop = expr.properties[j];
|
|
158
|
-
if (ts.isPropertyAssignment(prop)) {
|
|
159
|
-
let text = ts.isIdentifier(prop.name)
|
|
160
|
-
? prop.name.text
|
|
161
|
-
: ts.isStringLiteral(prop.name) ? prop.name.text : null;
|
|
162
|
-
if (text === key) {
|
|
163
|
-
value = prop.initializer.getText();
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
else if (ts.isShorthandPropertyAssignment(prop) && prop.name.text === key) {
|
|
168
|
-
value = prop.name.text;
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
if (value !== null) {
|
|
173
|
-
lines.push(generateAttributeBinding(elementVar, key, value, '', ns));
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
for (let i = 0, n = analysis.keys.length; i < n; i++) {
|
|
179
|
-
let key = analysis.keys[i];
|
|
180
|
-
lines.push(generateAttributeBinding(elementVar, key, `${exprCode}.${key}`, '', ns));
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return lines;
|
|
94
|
+
const generateSpreadBindings = (exprCode, elementVar, addImport) => {
|
|
95
|
+
return [`${addImport('setProperties')}(${elementVar}, ${exprCode});`];
|
|
184
96
|
};
|
|
185
97
|
export { analyzeExpression, generateAttributeBinding, generateSpreadBindings };
|
package/build/constants.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ 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;
|
|
6
5
|
declare const enum COMPILER_TYPES {
|
|
7
6
|
ArraySlot = "array-slot",
|
|
8
7
|
Attributes = "attributes",
|
|
@@ -17,10 +16,9 @@ declare const enum COMPILER_TYPES {
|
|
|
17
16
|
declare const DIRECT_ATTACH_EVENTS: Set<string>;
|
|
18
17
|
declare const LIFECYCLE_EVENTS: Set<string>;
|
|
19
18
|
declare const PACKAGE = "@esportsplus/template";
|
|
20
|
-
declare const PACKAGE_COMPILER = "@esportsplus/template/compiler";
|
|
21
19
|
declare const SLOT_HTML = "<!--$-->";
|
|
22
20
|
declare const STATE_HYDRATING = 0;
|
|
23
21
|
declare const STATE_NONE = 1;
|
|
24
22
|
declare const STATE_WAITING = 2;
|
|
25
23
|
declare const STORE: unique symbol;
|
|
26
|
-
export { ARRAY_SLOT, CLEANUP, COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY,
|
|
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, };
|
package/build/constants.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { uid } from '@esportsplus/typescript/compiler';
|
|
2
1
|
const ARRAY_SLOT = Symbol('template.array.slot');
|
|
3
2
|
const CLEANUP = Symbol('template.cleanup');
|
|
4
3
|
const COMPILER_ENTRYPOINT = 'html';
|
|
5
4
|
const COMPILER_ENTRYPOINT_REACTIVITY = 'reactive';
|
|
6
|
-
const COMPILER_NAMESPACE = uid('template');
|
|
7
5
|
var COMPILER_TYPES;
|
|
8
6
|
(function (COMPILER_TYPES) {
|
|
9
7
|
COMPILER_TYPES["ArraySlot"] = "array-slot";
|
|
@@ -30,10 +28,9 @@ const LIFECYCLE_EVENTS = new Set([
|
|
|
30
28
|
'onconnect', 'ondisconnect', 'onrender', 'onresize', 'ontick'
|
|
31
29
|
]);
|
|
32
30
|
const PACKAGE = '@esportsplus/template';
|
|
33
|
-
const PACKAGE_COMPILER = '@esportsplus/template/compiler';
|
|
34
31
|
const SLOT_HTML = '<!--$-->';
|
|
35
32
|
const STATE_HYDRATING = 0;
|
|
36
33
|
const STATE_NONE = 1;
|
|
37
34
|
const STATE_WAITING = 2;
|
|
38
35
|
const STORE = Symbol('template.store');
|
|
39
|
-
export { ARRAY_SLOT, CLEANUP, COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY,
|
|
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, };
|