@esportsplus/template 0.32.1 → 0.32.2
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/index.d.ts +3 -3
- package/build/index.js +3 -3
- package/build/transformer/codegen.d.ts +1 -1
- package/build/transformer/codegen.js +15 -21
- package/build/transformer/index.d.ts +1 -4
- package/build/transformer/index.js +8 -25
- package/build/transformer/plugins/tsc.d.ts +2 -2
- package/build/transformer/plugins/tsc.js +3 -10
- package/build/transformer/plugins/vite.d.ts +11 -3
- package/build/transformer/plugins/vite.js +6 -34
- package/build/transformer/ts-parser.js +3 -7
- package/build/transformer/type-analyzer.d.ts +1 -1
- package/build/transformer/type-analyzer.js +25 -60
- package/package.json +3 -3
- package/src/index.ts +5 -3
- package/src/transformer/codegen.ts +27 -35
- package/src/transformer/index.ts +9 -32
- package/src/transformer/plugins/tsc.ts +3 -13
- package/src/transformer/plugins/vite.ts +7 -43
- package/src/transformer/ts-parser.ts +3 -10
- package/src/transformer/type-analyzer.ts +31 -81
package/build/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import './runtime.js';
|
|
2
|
-
export { default as attributes } from './attributes.js';
|
|
3
|
-
export { default as event } from './event/index.js';
|
|
4
2
|
export { default as html } from './html.js';
|
|
5
3
|
export { default as render } from './render.js';
|
|
6
|
-
export { default as slot } from './slot/index.js';
|
|
7
4
|
export { default as svg } from './svg.js';
|
|
5
|
+
export { default as attributes } from './attributes.js';
|
|
6
|
+
export { default as event } from './event/index.js';
|
|
7
|
+
export { default as slot } from './slot/index.js';
|
|
8
8
|
export { ArraySlot } from './slot/array.js';
|
|
9
9
|
export { EffectSlot } from './slot/effect.js';
|
|
10
10
|
export type { Attributes, Element, Renderable } from './types.js';
|
package/build/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import './runtime.js';
|
|
2
|
-
export { default as attributes } from './attributes.js';
|
|
3
|
-
export { default as event } from './event/index.js';
|
|
4
2
|
export { default as html } from './html.js';
|
|
5
3
|
export { default as render } from './render.js';
|
|
6
|
-
export { default as slot } from './slot/index.js';
|
|
7
4
|
export { default as svg } from './svg.js';
|
|
5
|
+
export { default as attributes } from './attributes.js';
|
|
6
|
+
export { default as event } from './event/index.js';
|
|
7
|
+
export { default as slot } from './slot/index.js';
|
|
8
8
|
export { ArraySlot } from './slot/array.js';
|
|
9
9
|
export { EffectSlot } from './slot/effect.js';
|
|
10
10
|
export * from './utilities.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { ts } from '@esportsplus/typescript';
|
|
1
2
|
import { code as c, uid } from '@esportsplus/typescript/transformer';
|
|
2
3
|
import { analyzeExpression, generateAttributeBinding, generateSpreadBindings } from './type-analyzer.js';
|
|
3
|
-
import { ts } from '@esportsplus/typescript';
|
|
4
4
|
import { COMPILER_ENTRYPOINT, COMPILER_NAMESPACE, COMPILER_TYPES, PACKAGE } from '../constants.js';
|
|
5
5
|
import parser from './parser.js';
|
|
6
6
|
const ARROW_EMPTY_PARAMS = /\(\s*\)\s*=>\s*$/;
|
|
@@ -89,18 +89,18 @@ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArro
|
|
|
89
89
|
? root
|
|
90
90
|
: (nodes.get(slots[i].path.join('.')) || root), slot = slots[i];
|
|
91
91
|
if (slot.type === COMPILER_TYPES.AttributeSlot) {
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
let names = slot.attributes.names;
|
|
93
|
+
for (let j = 0, m = names.length; j < m; j++) {
|
|
94
|
+
let name = names[j];
|
|
94
95
|
if (name === 'spread') {
|
|
95
|
-
let bindings = generateSpreadBindings(exprNodes[index], exprTexts[index] || 'undefined', elementVar, ctx.
|
|
96
|
+
let bindings = generateSpreadBindings(exprNodes[index], exprTexts[index] || 'undefined', elementVar, ctx.checker, COMPILER_NAMESPACE);
|
|
96
97
|
for (let k = 0, o = bindings.length; k < o; k++) {
|
|
97
98
|
code.push(bindings[k]);
|
|
98
99
|
}
|
|
99
100
|
index++;
|
|
100
101
|
}
|
|
101
102
|
else {
|
|
102
|
-
|
|
103
|
-
code.push(binding);
|
|
103
|
+
code.push(generateAttributeBinding(elementVar, name, exprTexts[index++] || 'undefined', slot.attributes.statics[name] || '', COMPILER_NAMESPACE));
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
}
|
|
@@ -152,14 +152,6 @@ function hasMatch(node, predicate) {
|
|
|
152
152
|
});
|
|
153
153
|
return found;
|
|
154
154
|
}
|
|
155
|
-
function hasArraySlotUsage(node) {
|
|
156
|
-
return hasMatch(node, n => ts.isNewExpression(n) &&
|
|
157
|
-
ts.isPropertyAccessExpression(n.expression) &&
|
|
158
|
-
n.expression.name.text === 'ArraySlot');
|
|
159
|
-
}
|
|
160
|
-
function hasNestedTemplates(node) {
|
|
161
|
-
return hasMatch(node, n => isNestedHtmlTemplate(n));
|
|
162
|
-
}
|
|
163
155
|
function isNestedHtmlTemplate(expr) {
|
|
164
156
|
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
|
|
165
157
|
}
|
|
@@ -176,11 +168,11 @@ function rewriteExpression(ctx, expr) {
|
|
|
176
168
|
if (isNestedHtmlTemplate(expr)) {
|
|
177
169
|
return generateNestedTemplateCode(ctx, expr);
|
|
178
170
|
}
|
|
179
|
-
if (!
|
|
171
|
+
if (!hasMatch(expr, n => isNestedHtmlTemplate(n))) {
|
|
180
172
|
return ctx.printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
|
|
181
173
|
}
|
|
182
|
-
let
|
|
183
|
-
collectNestedTemplateReplacements(ctx, expr,
|
|
174
|
+
let replacements = [];
|
|
175
|
+
collectNestedTemplateReplacements(ctx, expr, expr.getStart(), replacements);
|
|
184
176
|
return c.replaceReverse(expr.getText(ctx.sourceFile), replacements);
|
|
185
177
|
}
|
|
186
178
|
const addArraySlotImport = (code) => {
|
|
@@ -190,14 +182,14 @@ const generateCode = (templates, originalCode, sourceFile, checker) => {
|
|
|
190
182
|
if (templates.length === 0) {
|
|
191
183
|
return { changed: false, code: originalCode };
|
|
192
184
|
}
|
|
193
|
-
let
|
|
185
|
+
let ranges = [];
|
|
194
186
|
for (let i = 0, n = templates.length; i < n; i++) {
|
|
195
187
|
let exprs = templates[i].expressions;
|
|
196
188
|
for (let j = 0, m = exprs.length; j < m; j++) {
|
|
197
|
-
|
|
189
|
+
ranges.push({ end: exprs[j].end, start: exprs[j].getStart() });
|
|
198
190
|
}
|
|
199
191
|
}
|
|
200
|
-
let rootTemplates = templates.filter(t => !isNestedTemplate(t,
|
|
192
|
+
let rootTemplates = templates.filter(t => !isNestedTemplate(t, ranges));
|
|
201
193
|
if (rootTemplates.length === 0) {
|
|
202
194
|
return { changed: false, code: originalCode };
|
|
203
195
|
}
|
|
@@ -260,6 +252,8 @@ const generateReactiveInlining = (calls, code, sourceFile) => {
|
|
|
260
252
|
return c.replaceReverse(code, replacements);
|
|
261
253
|
};
|
|
262
254
|
const needsArraySlotImport = (sourceFile) => {
|
|
263
|
-
return
|
|
255
|
+
return hasMatch(sourceFile, n => ts.isNewExpression(n) &&
|
|
256
|
+
ts.isPropertyAccessExpression(n.expression) &&
|
|
257
|
+
n.expression.name.text === 'ArraySlot') && !hasArraySlotImport(sourceFile);
|
|
264
258
|
};
|
|
265
259
|
export { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport };
|
|
@@ -4,8 +4,5 @@ type TransformResult = {
|
|
|
4
4
|
code: string;
|
|
5
5
|
sourceFile: ts.SourceFile;
|
|
6
6
|
};
|
|
7
|
-
declare const PATTERNS: string[];
|
|
8
|
-
declare function createTransformer(program: ts.Program): ts.TransformerFactory<ts.SourceFile>;
|
|
9
7
|
declare const transform: (sourceFile: ts.SourceFile, program: ts.Program) => TransformResult;
|
|
10
|
-
|
|
11
|
-
export { createTransformer, transform, transformCode, PATTERNS };
|
|
8
|
+
export { transform };
|
|
@@ -4,44 +4,27 @@ import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY } from '../constant
|
|
|
4
4
|
import { findHtmlTemplates, findReactiveCalls } from './ts-parser.js';
|
|
5
5
|
import { ts } from '@esportsplus/typescript';
|
|
6
6
|
const PATTERNS = [`${COMPILER_ENTRYPOINT}\``, `${COMPILER_ENTRYPOINT}.${COMPILER_ENTRYPOINT_REACTIVITY}`];
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return (_context) => {
|
|
10
|
-
return (sourceFile) => {
|
|
11
|
-
let code = sourceFile.getFullText();
|
|
12
|
-
if (!c.contains(code, { patterns: PATTERNS })) {
|
|
13
|
-
return sourceFile;
|
|
14
|
-
}
|
|
15
|
-
let result = transformCode(code, sourceFile, typeChecker);
|
|
16
|
-
if (!result.changed) {
|
|
17
|
-
return sourceFile;
|
|
18
|
-
}
|
|
19
|
-
return result.sourceFile;
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
}
|
|
7
|
+
const REGEX_BACKSLASH = /\\/g;
|
|
8
|
+
const REGEX_FORWARD_SLASH = /\//g;
|
|
23
9
|
const transform = (sourceFile, program) => {
|
|
24
10
|
let code = sourceFile.getFullText();
|
|
25
11
|
if (!c.contains(code, { patterns: PATTERNS })) {
|
|
26
12
|
return { changed: false, code, sourceFile };
|
|
27
13
|
}
|
|
28
14
|
let checker, fileName = sourceFile.fileName, programSourceFile = program.getSourceFile(fileName)
|
|
29
|
-
|| program.getSourceFile(fileName.replace(
|
|
30
|
-
|| program.getSourceFile(fileName.replace(
|
|
15
|
+
|| program.getSourceFile(fileName.replace(REGEX_BACKSLASH, '/'))
|
|
16
|
+
|| program.getSourceFile(fileName.replace(REGEX_FORWARD_SLASH, '\\'));
|
|
31
17
|
if (programSourceFile) {
|
|
32
18
|
checker = program.getTypeChecker();
|
|
33
19
|
sourceFile = programSourceFile;
|
|
34
20
|
}
|
|
35
|
-
|
|
36
|
-
};
|
|
37
|
-
const transformCode = (code, sourceFile, checker) => {
|
|
38
|
-
let changed = false, codegenChanged = false, needsImport = false, result = code, reactiveCalls = findReactiveCalls(sourceFile);
|
|
21
|
+
let changed = false, codegenChanged = false, needsImport = false, reactiveCalls = findReactiveCalls(sourceFile), result = code;
|
|
39
22
|
if (reactiveCalls.length > 0) {
|
|
40
|
-
result = generateReactiveInlining(reactiveCalls, result, sourceFile);
|
|
41
23
|
changed = true;
|
|
24
|
+
checker = undefined;
|
|
25
|
+
result = generateReactiveInlining(reactiveCalls, result, sourceFile);
|
|
42
26
|
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
43
27
|
needsImport = needsArraySlotImport(sourceFile);
|
|
44
|
-
checker = undefined;
|
|
45
28
|
}
|
|
46
29
|
let templates = findHtmlTemplates(sourceFile);
|
|
47
30
|
if (templates.length > 0) {
|
|
@@ -60,4 +43,4 @@ const transformCode = (code, sourceFile, checker) => {
|
|
|
60
43
|
}
|
|
61
44
|
return { changed, code: result, sourceFile };
|
|
62
45
|
};
|
|
63
|
-
export {
|
|
46
|
+
export { transform };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
declare const _default:
|
|
1
|
+
import { plugin } from '@esportsplus/typescript/transformer';
|
|
2
|
+
declare const _default: ReturnType<typeof plugin.tsc>;
|
|
3
3
|
export default _default;
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return () => {
|
|
5
|
-
return (sourceFile) => {
|
|
6
|
-
let result = transformCode(sourceFile.getFullText(), sourceFile, typeChecker);
|
|
7
|
-
return result.changed ? result.sourceFile : sourceFile;
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
};
|
|
1
|
+
import { plugin } from '@esportsplus/typescript/transformer';
|
|
2
|
+
import { transform } from '../index.js';
|
|
3
|
+
export default plugin.tsc(transform);
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
declare const _default: (options?: {
|
|
1
|
+
declare const _default: ({ root }?: {
|
|
3
2
|
root?: string;
|
|
4
|
-
}) =>
|
|
3
|
+
}) => {
|
|
4
|
+
configResolved(config: import("vite").ResolvedConfig): void;
|
|
5
|
+
enforce: string;
|
|
6
|
+
name: string;
|
|
7
|
+
transform(code: string, id: string): {
|
|
8
|
+
code: string;
|
|
9
|
+
map: null;
|
|
10
|
+
} | null;
|
|
11
|
+
watchChange(id: string): void;
|
|
12
|
+
};
|
|
5
13
|
export default _default;
|
|
@@ -1,35 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { program, TRANSFORM_PATTERN } from '@esportsplus/typescript/transformer';
|
|
1
|
+
import { plugin } from '@esportsplus/typescript/transformer';
|
|
3
2
|
import { PACKAGE } from '../../constants.js';
|
|
4
|
-
import { transform } from '
|
|
5
|
-
export default (
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
root = options?.root ?? config.root;
|
|
10
|
-
},
|
|
11
|
-
enforce: 'pre',
|
|
12
|
-
name: `${PACKAGE}/plugin-vite`,
|
|
13
|
-
transform(code, id) {
|
|
14
|
-
if (!TRANSFORM_PATTERN.test(id) || id.includes('node_modules')) {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
try {
|
|
18
|
-
let result = transform(ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true), program.get(root));
|
|
19
|
-
if (!result.changed) {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
return { code: result.code, map: null };
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
console.error(`${PACKAGE}: Error transforming ${id}:`, error);
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
watchChange(id) {
|
|
30
|
-
if (TRANSFORM_PATTERN.test(id)) {
|
|
31
|
-
program.delete(root);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
};
|
|
3
|
+
import { transform } from '../index.js';
|
|
4
|
+
export default plugin.vite({
|
|
5
|
+
name: PACKAGE,
|
|
6
|
+
transform
|
|
7
|
+
});
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY } from '../constants.js';
|
|
2
2
|
import { ts } from '@esportsplus/typescript';
|
|
3
|
-
function isFunctionNode(node) {
|
|
4
|
-
return (ts.isArrowFunction(node) ||
|
|
5
|
-
ts.isFunctionDeclaration(node) ||
|
|
6
|
-
ts.isFunctionExpression(node) ||
|
|
7
|
-
ts.isMethodDeclaration(node));
|
|
8
|
-
}
|
|
9
3
|
function visitReactiveCalls(node, calls) {
|
|
10
4
|
if (ts.isCallExpression(node) &&
|
|
11
5
|
ts.isPropertyAccessExpression(node.expression) &&
|
|
@@ -24,7 +18,9 @@ function visitReactiveCalls(node, calls) {
|
|
|
24
18
|
ts.forEachChild(node, child => visitReactiveCalls(child, calls));
|
|
25
19
|
}
|
|
26
20
|
function visitTemplates(node, depth, templates) {
|
|
27
|
-
let nextDepth =
|
|
21
|
+
let nextDepth = (ts.isArrowFunction(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isMethodDeclaration(node))
|
|
22
|
+
? depth + 1
|
|
23
|
+
: depth;
|
|
28
24
|
if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && node.tag.text === COMPILER_ENTRYPOINT) {
|
|
29
25
|
let expressions = [], literals = [], template = node.template;
|
|
30
26
|
if (ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
@@ -2,5 +2,5 @@ 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
4
|
declare const generateAttributeBinding: (elementVar: string, name: string, expr: string, staticValue: string, ns: string) => string;
|
|
5
|
-
declare const generateSpreadBindings: (expr: ts.Expression, exprCode: string, elementVar: string,
|
|
5
|
+
declare const generateSpreadBindings: (expr: ts.Expression, exprCode: string, elementVar: string, checker: ts.TypeChecker | undefined, ns: string) => string[];
|
|
6
6
|
export { analyzeExpression, generateAttributeBinding, generateSpreadBindings };
|
|
@@ -24,28 +24,23 @@ function analyzeSpread(expr, checker) {
|
|
|
24
24
|
}
|
|
25
25
|
if (checker && (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr))) {
|
|
26
26
|
try {
|
|
27
|
-
let keys =
|
|
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
|
+
}
|
|
28
35
|
if (keys.length > 0) {
|
|
29
36
|
return { canUnpack: true, keys };
|
|
30
37
|
}
|
|
31
38
|
}
|
|
32
|
-
catch {
|
|
33
|
-
}
|
|
39
|
+
catch { }
|
|
34
40
|
}
|
|
35
41
|
return { canUnpack: false, keys: [] };
|
|
36
42
|
}
|
|
37
|
-
function
|
|
38
|
-
let keys = [], props = type.getProperties();
|
|
39
|
-
for (let i = 0, n = props.length; i < n; i++) {
|
|
40
|
-
let name = props[i].getName();
|
|
41
|
-
if (name.startsWith('__') || name.startsWith('[')) {
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
keys.push(name);
|
|
45
|
-
}
|
|
46
|
-
return keys;
|
|
47
|
-
}
|
|
48
|
-
function inferCOMPILER_TYPES(expr, ctx) {
|
|
43
|
+
function inferCOMPILER_TYPES(expr, checker) {
|
|
49
44
|
while (ts.isParenthesizedExpression(expr)) {
|
|
50
45
|
expr = expr.expression;
|
|
51
46
|
}
|
|
@@ -78,7 +73,7 @@ function inferCOMPILER_TYPES(expr, ctx) {
|
|
|
78
73
|
return COMPILER_TYPES.Primitive;
|
|
79
74
|
}
|
|
80
75
|
if (ts.isConditionalExpression(expr)) {
|
|
81
|
-
let whenFalse = inferCOMPILER_TYPES(expr.whenFalse,
|
|
76
|
+
let whenFalse = inferCOMPILER_TYPES(expr.whenFalse, checker), whenTrue = inferCOMPILER_TYPES(expr.whenTrue, checker);
|
|
82
77
|
if (whenTrue === whenFalse) {
|
|
83
78
|
return whenTrue;
|
|
84
79
|
}
|
|
@@ -87,46 +82,17 @@ function inferCOMPILER_TYPES(expr, ctx) {
|
|
|
87
82
|
}
|
|
88
83
|
return COMPILER_TYPES.Unknown;
|
|
89
84
|
}
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (isTypeFunction(type, checker)) {
|
|
96
|
-
return COMPILER_TYPES.Effect;
|
|
97
|
-
}
|
|
98
|
-
if (isTypeArray(type, checker)) {
|
|
99
|
-
return COMPILER_TYPES.ArraySlot;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (ts.isPropertyAccessExpression(expr)) {
|
|
106
|
-
try {
|
|
107
|
-
let type = checker.getTypeAtLocation(expr);
|
|
108
|
-
if (isTypeFunction(type, checker)) {
|
|
109
|
-
return COMPILER_TYPES.Effect;
|
|
110
|
-
}
|
|
111
|
-
if (isTypeArray(type, checker)) {
|
|
112
|
-
return COMPILER_TYPES.ArraySlot;
|
|
113
|
-
}
|
|
85
|
+
if (checker && (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr) || ts.isCallExpression(expr))) {
|
|
86
|
+
try {
|
|
87
|
+
let type = checker.getTypeAtLocation(expr);
|
|
88
|
+
if (isTypeFunction(type, checker)) {
|
|
89
|
+
return COMPILER_TYPES.Effect;
|
|
114
90
|
}
|
|
115
|
-
|
|
91
|
+
if (isTypeArray(type, checker)) {
|
|
92
|
+
return COMPILER_TYPES.ArraySlot;
|
|
116
93
|
}
|
|
117
94
|
}
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
let type = checker.getTypeAtLocation(expr);
|
|
121
|
-
if (isTypeFunction(type, checker)) {
|
|
122
|
-
return COMPILER_TYPES.Effect;
|
|
123
|
-
}
|
|
124
|
-
if (isTypeArray(type, checker)) {
|
|
125
|
-
return COMPILER_TYPES.ArraySlot;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
}
|
|
95
|
+
catch {
|
|
130
96
|
}
|
|
131
97
|
}
|
|
132
98
|
return COMPILER_TYPES.Unknown;
|
|
@@ -135,8 +101,7 @@ function isTypeArray(type, checker) {
|
|
|
135
101
|
if (checker.isArrayType(type)) {
|
|
136
102
|
return true;
|
|
137
103
|
}
|
|
138
|
-
|
|
139
|
-
return symbol?.getName() === 'ReactiveArray';
|
|
104
|
+
return type.getSymbol()?.getName() === 'ReactiveArray';
|
|
140
105
|
}
|
|
141
106
|
function isTypeFunction(type, checker) {
|
|
142
107
|
if (type.getCallSignatures().length > 0) {
|
|
@@ -152,7 +117,7 @@ function isTypeFunction(type, checker) {
|
|
|
152
117
|
return false;
|
|
153
118
|
}
|
|
154
119
|
const analyzeExpression = (expr, checker) => {
|
|
155
|
-
return inferCOMPILER_TYPES(expr, checker
|
|
120
|
+
return inferCOMPILER_TYPES(expr, checker);
|
|
156
121
|
};
|
|
157
122
|
const generateAttributeBinding = (elementVar, name, expr, staticValue, ns) => {
|
|
158
123
|
if (name.startsWith('on') && name.length > 2) {
|
|
@@ -176,7 +141,7 @@ const generateAttributeBinding = (elementVar, name, expr, staticValue, ns) => {
|
|
|
176
141
|
}
|
|
177
142
|
return `${ns}.attributes.setProperty(${elementVar}, '${name}', ${expr});`;
|
|
178
143
|
};
|
|
179
|
-
const generateSpreadBindings = (expr, exprCode, elementVar,
|
|
144
|
+
const generateSpreadBindings = (expr, exprCode, elementVar, checker, ns) => {
|
|
180
145
|
while (ts.isParenthesizedExpression(expr)) {
|
|
181
146
|
expr = expr.expression;
|
|
182
147
|
}
|
|
@@ -191,11 +156,11 @@ const generateSpreadBindings = (expr, exprCode, elementVar, sourceFile, checker,
|
|
|
191
156
|
for (let j = 0, m = expr.properties.length; j < m; j++) {
|
|
192
157
|
let prop = expr.properties[j];
|
|
193
158
|
if (ts.isPropertyAssignment(prop)) {
|
|
194
|
-
let
|
|
159
|
+
let text = ts.isIdentifier(prop.name)
|
|
195
160
|
? prop.name.text
|
|
196
161
|
: ts.isStringLiteral(prop.name) ? prop.name.text : null;
|
|
197
|
-
if (
|
|
198
|
-
value = prop.initializer.getText(
|
|
162
|
+
if (text === key) {
|
|
163
|
+
value = prop.initializer.getText();
|
|
199
164
|
break;
|
|
200
165
|
}
|
|
201
166
|
}
|
package/package.json
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"author": "ICJR",
|
|
3
3
|
"dependencies": {
|
|
4
4
|
"@esportsplus/queue": "^0.2.0",
|
|
5
|
-
"@esportsplus/reactivity": "^0.25.
|
|
5
|
+
"@esportsplus/reactivity": "^0.25.12",
|
|
6
6
|
"@esportsplus/utilities": "^0.27.2",
|
|
7
7
|
"serve": "^14.2.5"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
|
-
"@esportsplus/typescript": "^0.
|
|
10
|
+
"@esportsplus/typescript": "^0.18.0",
|
|
11
11
|
"@types/node": "^25.0.3",
|
|
12
12
|
"vite": "^7.3.0",
|
|
13
13
|
"vite-tsconfig-paths": "^6.0.3"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"type": "module",
|
|
42
42
|
"types": "./build/index.d.ts",
|
|
43
|
-
"version": "0.32.
|
|
43
|
+
"version": "0.32.2",
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsc",
|
|
46
46
|
"build:test": "vite build --config test/vite.config.ts",
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import './runtime';
|
|
2
|
-
export { default as attributes } from './attributes';
|
|
3
|
-
export { default as event } from './event';
|
|
4
2
|
export { default as html } from './html';
|
|
5
3
|
export { default as render } from './render';
|
|
6
|
-
export { default as slot } from './slot';
|
|
7
4
|
export { default as svg } from './svg';
|
|
5
|
+
|
|
6
|
+
// Must be exported for compilation even if not used directly
|
|
7
|
+
export { default as attributes } from './attributes';
|
|
8
|
+
export { default as event } from './event';
|
|
9
|
+
export { default as slot } from './slot';
|
|
8
10
|
export { ArraySlot } from './slot/array';
|
|
9
11
|
export { EffectSlot } from './slot/effect';
|
|
10
12
|
export type { Attributes, Element, Renderable } from './types';
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type
|
|
1
|
+
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { code as c, uid, type Replacement } from '@esportsplus/typescript/transformer';
|
|
3
3
|
import type { ReactiveCallInfo, TemplateInfo } from './ts-parser';
|
|
4
4
|
import { analyzeExpression, generateAttributeBinding, generateSpreadBindings } from './type-analyzer';
|
|
5
|
-
import { ts } from '@esportsplus/typescript';
|
|
6
5
|
import {
|
|
7
|
-
COMPILER_ENTRYPOINT,
|
|
8
|
-
COMPILER_NAMESPACE,
|
|
9
|
-
COMPILER_TYPES,
|
|
6
|
+
COMPILER_ENTRYPOINT, COMPILER_NAMESPACE, COMPILER_TYPES,
|
|
10
7
|
PACKAGE
|
|
11
|
-
} from '
|
|
8
|
+
} from '~/constants';
|
|
12
9
|
import parser from './parser';
|
|
13
10
|
|
|
14
11
|
|
|
@@ -47,6 +44,7 @@ type ParseResult = {
|
|
|
47
44
|
|
|
48
45
|
const ARROW_EMPTY_PARAMS = /\(\s*\)\s*=>\s*$/;
|
|
49
46
|
|
|
47
|
+
|
|
50
48
|
let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
51
49
|
|
|
52
50
|
|
|
@@ -192,15 +190,16 @@ function generateTemplateCode(
|
|
|
192
190
|
slot = slots[i];
|
|
193
191
|
|
|
194
192
|
if (slot.type === COMPILER_TYPES.AttributeSlot) {
|
|
195
|
-
|
|
196
|
-
|
|
193
|
+
let names = slot.attributes.names;
|
|
194
|
+
|
|
195
|
+
for (let j = 0, m = names.length; j < m; j++) {
|
|
196
|
+
let name = names[j];
|
|
197
197
|
|
|
198
198
|
if (name === 'spread') {
|
|
199
199
|
let bindings = generateSpreadBindings(
|
|
200
200
|
exprNodes[index],
|
|
201
201
|
exprTexts[index] || 'undefined',
|
|
202
202
|
elementVar,
|
|
203
|
-
ctx.sourceFile,
|
|
204
203
|
ctx.checker,
|
|
205
204
|
COMPILER_NAMESPACE
|
|
206
205
|
);
|
|
@@ -212,20 +211,22 @@ function generateTemplateCode(
|
|
|
212
211
|
index++;
|
|
213
212
|
}
|
|
214
213
|
else {
|
|
215
|
-
|
|
214
|
+
code.push(
|
|
215
|
+
generateAttributeBinding(
|
|
216
216
|
elementVar,
|
|
217
217
|
name,
|
|
218
218
|
exprTexts[index++] || 'undefined',
|
|
219
219
|
slot.attributes.statics[name] || '',
|
|
220
220
|
COMPILER_NAMESPACE
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
code.push(binding);
|
|
221
|
+
)
|
|
222
|
+
);
|
|
224
223
|
}
|
|
225
224
|
}
|
|
226
225
|
}
|
|
227
226
|
else {
|
|
228
|
-
code.push(
|
|
227
|
+
code.push(
|
|
228
|
+
generateNodeBinding(ctx, elementVar, exprTexts[index] || 'undefined', exprNodes[index])
|
|
229
|
+
);
|
|
229
230
|
index++;
|
|
230
231
|
}
|
|
231
232
|
}
|
|
@@ -288,18 +289,6 @@ function hasMatch(node: ts.Node, predicate: (n: ts.Node) => boolean): boolean {
|
|
|
288
289
|
return found;
|
|
289
290
|
}
|
|
290
291
|
|
|
291
|
-
function hasArraySlotUsage(node: ts.Node): boolean {
|
|
292
|
-
return hasMatch(node, n =>
|
|
293
|
-
ts.isNewExpression(n) &&
|
|
294
|
-
ts.isPropertyAccessExpression(n.expression) &&
|
|
295
|
-
n.expression.name.text === 'ArraySlot'
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function hasNestedTemplates(node: ts.Node): boolean {
|
|
300
|
-
return hasMatch(node, n => isNestedHtmlTemplate(n as ts.Expression));
|
|
301
|
-
}
|
|
302
|
-
|
|
303
292
|
function isNestedHtmlTemplate(expr: ts.Expression): expr is ts.TaggedTemplateExpression {
|
|
304
293
|
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
|
|
305
294
|
}
|
|
@@ -321,14 +310,13 @@ function rewriteExpression(ctx: CodegenContext, expr: ts.Expression): string {
|
|
|
321
310
|
return generateNestedTemplateCode(ctx, expr);
|
|
322
311
|
}
|
|
323
312
|
|
|
324
|
-
if (!
|
|
313
|
+
if (!hasMatch(expr, n => isNestedHtmlTemplate(n as ts.Expression))) {
|
|
325
314
|
return ctx.printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
|
|
326
315
|
}
|
|
327
316
|
|
|
328
|
-
let
|
|
329
|
-
replacements: Replacement[] = [];
|
|
317
|
+
let replacements: Replacement[] = [];
|
|
330
318
|
|
|
331
|
-
collectNestedTemplateReplacements(ctx, expr,
|
|
319
|
+
collectNestedTemplateReplacements(ctx, expr, expr.getStart(), replacements);
|
|
332
320
|
|
|
333
321
|
return c.replaceReverse(expr.getText(ctx.sourceFile), replacements);
|
|
334
322
|
}
|
|
@@ -344,17 +332,17 @@ const generateCode = (templates: TemplateInfo[], originalCode: string, sourceFil
|
|
|
344
332
|
}
|
|
345
333
|
|
|
346
334
|
// Precompute expression ranges for nested template detection
|
|
347
|
-
let
|
|
335
|
+
let ranges: { end: number; start: number }[] = [];
|
|
348
336
|
|
|
349
337
|
for (let i = 0, n = templates.length; i < n; i++) {
|
|
350
338
|
let exprs = templates[i].expressions;
|
|
351
339
|
|
|
352
340
|
for (let j = 0, m = exprs.length; j < m; j++) {
|
|
353
|
-
|
|
341
|
+
ranges.push({ end: exprs[j].end, start: exprs[j].getStart() });
|
|
354
342
|
}
|
|
355
343
|
}
|
|
356
344
|
|
|
357
|
-
let rootTemplates = templates.filter(t => !isNestedTemplate(t,
|
|
345
|
+
let rootTemplates = templates.filter(t => !isNestedTemplate(t, ranges));
|
|
358
346
|
|
|
359
347
|
if (rootTemplates.length === 0) {
|
|
360
348
|
return { changed: false, code: originalCode };
|
|
@@ -448,7 +436,11 @@ const generateReactiveInlining = (calls: ReactiveCallInfo[], code: string, sourc
|
|
|
448
436
|
};
|
|
449
437
|
|
|
450
438
|
const needsArraySlotImport = (sourceFile: ts.SourceFile): boolean => {
|
|
451
|
-
return
|
|
439
|
+
return hasMatch(sourceFile, n =>
|
|
440
|
+
ts.isNewExpression(n) &&
|
|
441
|
+
ts.isPropertyAccessExpression(n.expression) &&
|
|
442
|
+
n.expression.name.text === 'ArraySlot'
|
|
443
|
+
) && !hasArraySlotImport(sourceFile);
|
|
452
444
|
};
|
|
453
445
|
|
|
454
446
|
|
package/src/transformer/index.ts
CHANGED
|
@@ -14,28 +14,9 @@ type TransformResult = {
|
|
|
14
14
|
|
|
15
15
|
const PATTERNS = [`${COMPILER_ENTRYPOINT}\``, `${COMPILER_ENTRYPOINT}.${COMPILER_ENTRYPOINT_REACTIVITY}`];
|
|
16
16
|
|
|
17
|
+
const REGEX_BACKSLASH = /\\/g;
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
let typeChecker = program.getTypeChecker();
|
|
20
|
-
|
|
21
|
-
return (_context: ts.TransformationContext) => {
|
|
22
|
-
return (sourceFile: ts.SourceFile): ts.SourceFile => {
|
|
23
|
-
let code = sourceFile.getFullText();
|
|
24
|
-
|
|
25
|
-
if (!c.contains(code, { patterns: PATTERNS })) {
|
|
26
|
-
return sourceFile;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let result = transformCode(code, sourceFile, typeChecker);
|
|
30
|
-
|
|
31
|
-
if (!result.changed) {
|
|
32
|
-
return sourceFile;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return result.sourceFile;
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
}
|
|
19
|
+
const REGEX_FORWARD_SLASH = /\//g;
|
|
39
20
|
|
|
40
21
|
|
|
41
22
|
const transform = (sourceFile: ts.SourceFile, program: ts.Program): TransformResult => {
|
|
@@ -48,30 +29,26 @@ const transform = (sourceFile: ts.SourceFile, program: ts.Program): TransformRes
|
|
|
48
29
|
let checker: ts.TypeChecker | undefined,
|
|
49
30
|
fileName = sourceFile.fileName,
|
|
50
31
|
programSourceFile = program.getSourceFile(fileName)
|
|
51
|
-
|| program.getSourceFile(fileName.replace(
|
|
52
|
-
|| program.getSourceFile(fileName.replace(
|
|
32
|
+
|| program.getSourceFile(fileName.replace(REGEX_BACKSLASH, '/'))
|
|
33
|
+
|| program.getSourceFile(fileName.replace(REGEX_FORWARD_SLASH, '\\'));
|
|
53
34
|
|
|
54
35
|
if (programSourceFile) {
|
|
55
36
|
checker = program.getTypeChecker();
|
|
56
37
|
sourceFile = programSourceFile;
|
|
57
38
|
}
|
|
58
39
|
|
|
59
|
-
return transformCode(code, sourceFile, checker);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const transformCode = (code: string, sourceFile: ts.SourceFile, checker?: ts.TypeChecker): TransformResult => {
|
|
63
40
|
let changed = false,
|
|
64
41
|
codegenChanged = false,
|
|
65
42
|
needsImport = false,
|
|
66
|
-
|
|
67
|
-
|
|
43
|
+
reactiveCalls = findReactiveCalls(sourceFile),
|
|
44
|
+
result = code;
|
|
68
45
|
|
|
69
46
|
if (reactiveCalls.length > 0) {
|
|
70
|
-
result = generateReactiveInlining(reactiveCalls, result, sourceFile);
|
|
71
47
|
changed = true;
|
|
48
|
+
checker = undefined;
|
|
49
|
+
result = generateReactiveInlining(reactiveCalls, result, sourceFile);
|
|
72
50
|
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
73
51
|
needsImport = needsArraySlotImport(sourceFile);
|
|
74
|
-
checker = undefined;
|
|
75
52
|
}
|
|
76
53
|
|
|
77
54
|
let templates = findHtmlTemplates(sourceFile);
|
|
@@ -98,4 +75,4 @@ const transformCode = (code: string, sourceFile: ts.SourceFile, checker?: ts.Typ
|
|
|
98
75
|
};
|
|
99
76
|
|
|
100
77
|
|
|
101
|
-
export {
|
|
78
|
+
export { transform };
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { plugin } from '@esportsplus/typescript/transformer';
|
|
2
|
+
import { transform } from '..';
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
export default (
|
|
6
|
-
let typeChecker = program.getTypeChecker();
|
|
7
|
-
|
|
8
|
-
return () => {
|
|
9
|
-
return (sourceFile: ts.SourceFile): ts.SourceFile => {
|
|
10
|
-
let result = transformCode(sourceFile.getFullText(), sourceFile, typeChecker);
|
|
11
|
-
|
|
12
|
-
return result.changed ? result.sourceFile : sourceFile;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
};
|
|
5
|
+
export default plugin.tsc(transform) as ReturnType<typeof plugin.tsc>;
|
|
@@ -1,45 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { PACKAGE } from '~/constants';
|
|
5
|
-
import { transform } from '~/transformer';
|
|
1
|
+
import { plugin } from '@esportsplus/typescript/transformer';
|
|
2
|
+
import { PACKAGE } from '../../constants';
|
|
3
|
+
import { transform } from '..';
|
|
6
4
|
|
|
7
5
|
|
|
8
|
-
export default (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
configResolved(config: ResolvedConfig) {
|
|
13
|
-
root = options?.root ?? config.root;
|
|
14
|
-
},
|
|
15
|
-
enforce: 'pre',
|
|
16
|
-
name: `${PACKAGE}/plugin-vite`,
|
|
17
|
-
transform(code: string, id: string) {
|
|
18
|
-
if (!TRANSFORM_PATTERN.test(id) || id.includes('node_modules')) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
let result = transform(
|
|
24
|
-
ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true),
|
|
25
|
-
program.get(root)
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
if (!result.changed) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return { code: result.code, map: null };
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
console.error(`${PACKAGE}: Error transforming ${id}:`, error);
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
watchChange(id: string) {
|
|
40
|
-
if (TRANSFORM_PATTERN.test(id)) {
|
|
41
|
-
program.delete(root);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
};
|
|
6
|
+
export default plugin.vite({
|
|
7
|
+
name: PACKAGE,
|
|
8
|
+
transform
|
|
9
|
+
});
|
|
@@ -20,15 +20,6 @@ type TemplateInfo = {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
function isFunctionNode(node: ts.Node): boolean {
|
|
24
|
-
return (
|
|
25
|
-
ts.isArrowFunction(node) ||
|
|
26
|
-
ts.isFunctionDeclaration(node) ||
|
|
27
|
-
ts.isFunctionExpression(node) ||
|
|
28
|
-
ts.isMethodDeclaration(node)
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
23
|
function visitReactiveCalls(node: ts.Node, calls: ReactiveCallInfo[]): void {
|
|
33
24
|
if (
|
|
34
25
|
ts.isCallExpression(node) &&
|
|
@@ -51,7 +42,9 @@ function visitReactiveCalls(node: ts.Node, calls: ReactiveCallInfo[]): void {
|
|
|
51
42
|
}
|
|
52
43
|
|
|
53
44
|
function visitTemplates(node: ts.Node, depth: number, templates: TemplateInfo[]): void {
|
|
54
|
-
let nextDepth =
|
|
45
|
+
let nextDepth = (ts.isArrowFunction(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isMethodDeclaration(node))
|
|
46
|
+
? depth + 1
|
|
47
|
+
: depth;
|
|
55
48
|
|
|
56
49
|
if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && node.tag.text === COMPILER_ENTRYPOINT) {
|
|
57
50
|
let expressions: ts.Expression[] = [],
|
|
@@ -8,10 +8,6 @@ import {
|
|
|
8
8
|
import { ts } from '@esportsplus/typescript';
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
type AnalyzerContext = {
|
|
12
|
-
checker?: ts.TypeChecker;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
11
|
type SpreadAnalysis = {
|
|
16
12
|
canUnpack: boolean;
|
|
17
13
|
keys: string[];
|
|
@@ -47,37 +43,30 @@ function analyzeSpread(expr: ts.Expression, checker?: ts.TypeChecker): SpreadAna
|
|
|
47
43
|
|
|
48
44
|
if (checker && (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr))) {
|
|
49
45
|
try {
|
|
50
|
-
let keys =
|
|
46
|
+
let keys: string[] = [],
|
|
47
|
+
props = checker.getTypeAtLocation(expr).getProperties();
|
|
48
|
+
|
|
49
|
+
for (let i = 0, n = props.length; i < n; i++) {
|
|
50
|
+
let name = props[i].getName();
|
|
51
|
+
|
|
52
|
+
if (name.startsWith('__') || name.startsWith('[')) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
keys.push(name);
|
|
57
|
+
}
|
|
51
58
|
|
|
52
59
|
if (keys.length > 0) {
|
|
53
60
|
return { canUnpack: true, keys };
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
|
-
catch {
|
|
57
|
-
}
|
|
63
|
+
catch { }
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
return { canUnpack: false, keys: [] };
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
function
|
|
64
|
-
let keys: string[] = [],
|
|
65
|
-
props = type.getProperties();
|
|
66
|
-
|
|
67
|
-
for (let i = 0, n = props.length; i < n; i++) {
|
|
68
|
-
let name = props[i].getName();
|
|
69
|
-
|
|
70
|
-
if (name.startsWith('__') || name.startsWith('[')) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
keys.push(name);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return keys;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function inferCOMPILER_TYPES(expr: ts.Expression, ctx?: AnalyzerContext): COMPILER_TYPES {
|
|
69
|
+
function inferCOMPILER_TYPES(expr: ts.Expression, checker?: ts.TypeChecker): COMPILER_TYPES {
|
|
81
70
|
while (ts.isParenthesizedExpression(expr)) {
|
|
82
71
|
expr = expr.expression;
|
|
83
72
|
}
|
|
@@ -121,8 +110,8 @@ function inferCOMPILER_TYPES(expr: ts.Expression, ctx?: AnalyzerContext): COMPIL
|
|
|
121
110
|
}
|
|
122
111
|
|
|
123
112
|
if (ts.isConditionalExpression(expr)) {
|
|
124
|
-
let whenFalse = inferCOMPILER_TYPES(expr.whenFalse,
|
|
125
|
-
whenTrue = inferCOMPILER_TYPES(expr.whenTrue,
|
|
113
|
+
let whenFalse = inferCOMPILER_TYPES(expr.whenFalse, checker),
|
|
114
|
+
whenTrue = inferCOMPILER_TYPES(expr.whenTrue, checker);
|
|
126
115
|
|
|
127
116
|
if (whenTrue === whenFalse) {
|
|
128
117
|
return whenTrue;
|
|
@@ -135,55 +124,19 @@ function inferCOMPILER_TYPES(expr: ts.Expression, ctx?: AnalyzerContext): COMPIL
|
|
|
135
124
|
return COMPILER_TYPES.Unknown;
|
|
136
125
|
}
|
|
137
126
|
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (ts.isIdentifier(expr)) {
|
|
142
|
-
try {
|
|
143
|
-
let type = checker.getTypeAtLocation(expr);
|
|
144
|
-
|
|
145
|
-
if (isTypeFunction(type, checker)) {
|
|
146
|
-
return COMPILER_TYPES.Effect;
|
|
147
|
-
}
|
|
127
|
+
if (checker && (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr) || ts.isCallExpression(expr))) {
|
|
128
|
+
try {
|
|
129
|
+
let type = checker.getTypeAtLocation(expr);
|
|
148
130
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
131
|
+
if (isTypeFunction(type, checker)) {
|
|
132
|
+
return COMPILER_TYPES.Effect;
|
|
152
133
|
}
|
|
153
|
-
catch {
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
134
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
let type = checker.getTypeAtLocation(expr);
|
|
160
|
-
|
|
161
|
-
if (isTypeFunction(type, checker)) {
|
|
162
|
-
return COMPILER_TYPES.Effect;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (isTypeArray(type, checker)) {
|
|
166
|
-
return COMPILER_TYPES.ArraySlot;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
135
|
+
if (isTypeArray(type, checker)) {
|
|
136
|
+
return COMPILER_TYPES.ArraySlot;
|
|
170
137
|
}
|
|
171
138
|
}
|
|
172
|
-
|
|
173
|
-
if (ts.isCallExpression(expr)) {
|
|
174
|
-
try {
|
|
175
|
-
let type = checker.getTypeAtLocation(expr);
|
|
176
|
-
|
|
177
|
-
if (isTypeFunction(type, checker)) {
|
|
178
|
-
return COMPILER_TYPES.Effect;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (isTypeArray(type, checker)) {
|
|
182
|
-
return COMPILER_TYPES.ArraySlot;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
catch {
|
|
186
|
-
}
|
|
139
|
+
catch {
|
|
187
140
|
}
|
|
188
141
|
}
|
|
189
142
|
|
|
@@ -195,9 +148,7 @@ function isTypeArray(type: ts.Type, checker: ts.TypeChecker): boolean {
|
|
|
195
148
|
return true;
|
|
196
149
|
}
|
|
197
150
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return symbol?.getName() === 'ReactiveArray';
|
|
151
|
+
return type.getSymbol()?.getName() === 'ReactiveArray';
|
|
201
152
|
}
|
|
202
153
|
|
|
203
154
|
function isTypeFunction(type: ts.Type, checker: ts.TypeChecker): boolean {
|
|
@@ -218,7 +169,7 @@ function isTypeFunction(type: ts.Type, checker: ts.TypeChecker): boolean {
|
|
|
218
169
|
|
|
219
170
|
|
|
220
171
|
const analyzeExpression = (expr: ts.Expression, checker?: ts.TypeChecker): COMPILER_TYPES => {
|
|
221
|
-
return inferCOMPILER_TYPES(expr, checker
|
|
172
|
+
return inferCOMPILER_TYPES(expr, checker);
|
|
222
173
|
};
|
|
223
174
|
|
|
224
175
|
const generateAttributeBinding = (elementVar: string, name: string, expr: string, staticValue: string, ns: string): string => {
|
|
@@ -256,7 +207,6 @@ const generateSpreadBindings = (
|
|
|
256
207
|
expr: ts.Expression,
|
|
257
208
|
exprCode: string,
|
|
258
209
|
elementVar: string,
|
|
259
|
-
sourceFile: ts.SourceFile,
|
|
260
210
|
checker: ts.TypeChecker | undefined,
|
|
261
211
|
ns: string
|
|
262
212
|
): string[] => {
|
|
@@ -281,12 +231,12 @@ const generateSpreadBindings = (
|
|
|
281
231
|
let prop = expr.properties[j];
|
|
282
232
|
|
|
283
233
|
if (ts.isPropertyAssignment(prop)) {
|
|
284
|
-
let
|
|
285
|
-
|
|
286
|
-
|
|
234
|
+
let text = ts.isIdentifier(prop.name)
|
|
235
|
+
? prop.name.text
|
|
236
|
+
: ts.isStringLiteral(prop.name) ? prop.name.text : null;
|
|
287
237
|
|
|
288
|
-
if (
|
|
289
|
-
value = prop.initializer.getText(
|
|
238
|
+
if (text === key) {
|
|
239
|
+
value = prop.initializer.getText();
|
|
290
240
|
break;
|
|
291
241
|
}
|
|
292
242
|
}
|