@esportsplus/template 0.32.0 → 0.32.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.js +1 -2
- package/build/constants.d.ts +18 -3
- package/build/constants.js +31 -4
- package/build/html.d.ts +3 -3
- package/build/slot/array.d.ts +2 -2
- package/build/slot/array.js +5 -4
- package/build/slot/render.js +3 -2
- package/build/transformer/codegen.d.ts +2 -8
- package/build/transformer/codegen.js +87 -138
- package/build/transformer/index.d.ts +2 -3
- package/build/transformer/index.js +32 -31
- package/build/transformer/parser.d.ts +3 -2
- package/build/transformer/parser.js +4 -4
- package/build/transformer/plugins/tsc.js +8 -2
- package/build/transformer/plugins/vite.js +7 -9
- package/build/transformer/ts-parser.d.ts +1 -2
- package/build/transformer/ts-parser.js +25 -34
- package/build/transformer/type-analyzer.d.ts +4 -5
- package/build/transformer/type-analyzer.js +61 -71
- package/build/types.d.ts +1 -1
- package/package.json +7 -7
- package/src/attributes.ts +1 -4
- package/src/constants.ts +42 -6
- package/src/html.ts +3 -3
- package/src/slot/array.ts +9 -6
- package/src/slot/render.ts +5 -2
- package/src/transformer/codegen.ts +113 -175
- package/src/transformer/index.ts +43 -40
- package/src/transformer/parser.ts +10 -7
- package/src/transformer/plugins/tsc.ts +10 -2
- package/src/transformer/plugins/vite.ts +10 -14
- package/src/transformer/ts-parser.ts +31 -44
- package/src/transformer/type-analyzer.ts +75 -93
- package/src/types.ts +1 -1
- package/test/vite.config.ts +1 -1
- package/build/event/constants.d.ts +0 -3
- package/build/event/constants.js +0 -13
- package/src/event/constants.ts +0 -16
- package/storage/rewrite-analysis-2026-01-04.md +0 -439
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { COMPILER_TYPES } from '../constants.js';
|
|
1
2
|
type NodePath = ('firstChild' | 'firstElementChild' | 'nextElementSibling' | 'nextSibling')[];
|
|
2
3
|
declare const _default: {
|
|
3
4
|
parse: (literals: string[]) => {
|
|
4
5
|
html: string;
|
|
5
6
|
slots: ({
|
|
6
7
|
path: NodePath;
|
|
7
|
-
type:
|
|
8
|
+
type: COMPILER_TYPES.NodeSlot;
|
|
8
9
|
} | {
|
|
9
10
|
attributes: {
|
|
10
11
|
names: string[];
|
|
11
12
|
statics: Record<string, string>;
|
|
12
13
|
};
|
|
13
14
|
path: NodePath;
|
|
14
|
-
type:
|
|
15
|
+
type: COMPILER_TYPES.AttributeSlot;
|
|
15
16
|
})[] | null;
|
|
16
17
|
};
|
|
17
18
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { COMPILER_TYPES, PACKAGE, SLOT_HTML } from '../constants.js';
|
|
2
2
|
const ATTRIBUTE_DELIMITERS = {
|
|
3
3
|
class: ' ',
|
|
4
4
|
style: ';'
|
|
@@ -125,9 +125,9 @@ const parse = (literals) => {
|
|
|
125
125
|
if (attr) {
|
|
126
126
|
let attrs = attributes[attr];
|
|
127
127
|
if (!attrs) {
|
|
128
|
-
throw new Error(
|
|
128
|
+
throw new Error(`${PACKAGE}: attribute metadata could not be found for '${attr}'`);
|
|
129
129
|
}
|
|
130
|
-
slots.push({ attributes: attrs, path, type:
|
|
130
|
+
slots.push({ attributes: attrs, path, type: COMPILER_TYPES.AttributeSlot });
|
|
131
131
|
for (let i = 0, n = attrs.names.length; i < n; i++) {
|
|
132
132
|
buffer += parsed[slot++];
|
|
133
133
|
}
|
|
@@ -139,7 +139,7 @@ const parse = (literals) => {
|
|
|
139
139
|
}
|
|
140
140
|
else if (type === NODE_SLOT) {
|
|
141
141
|
buffer += parsed[slot++] + SLOT_HTML;
|
|
142
|
-
slots.push({ path: methods(parent.children, parent.path, 'firstChild', 'nextSibling'), type:
|
|
142
|
+
slots.push({ path: methods(parent.children, parent.path, 'firstChild', 'nextSibling'), type: COMPILER_TYPES.NodeSlot });
|
|
143
143
|
}
|
|
144
144
|
if (n === slot) {
|
|
145
145
|
buffer += parsed[slot];
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { transformCode } from '../../transformer/index.js';
|
|
2
2
|
export default (program) => {
|
|
3
|
-
|
|
3
|
+
let typeChecker = program.getTypeChecker();
|
|
4
|
+
return () => {
|
|
5
|
+
return (sourceFile) => {
|
|
6
|
+
let result = transformCode(sourceFile.getFullText(), sourceFile, typeChecker);
|
|
7
|
+
return result.changed ? result.sourceFile : sourceFile;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
4
10
|
};
|
|
@@ -1,30 +1,28 @@
|
|
|
1
|
-
import { mightNeedTransform, PATTERNS, transform } from '../index.js';
|
|
2
|
-
import { program, TRANSFORM_PATTERN } from '@esportsplus/typescript/transformer';
|
|
3
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { program, TRANSFORM_PATTERN } from '@esportsplus/typescript/transformer';
|
|
3
|
+
import { PACKAGE } from '../../constants.js';
|
|
4
|
+
import { transform } from '../../transformer/index.js';
|
|
4
5
|
export default (options) => {
|
|
5
6
|
let root;
|
|
6
7
|
return {
|
|
7
|
-
enforce: 'pre',
|
|
8
|
-
name: '@esportsplus/template/plugin-vite',
|
|
9
8
|
configResolved(config) {
|
|
10
9
|
root = options?.root ?? config.root;
|
|
11
10
|
},
|
|
11
|
+
enforce: 'pre',
|
|
12
|
+
name: `${PACKAGE}/plugin-vite`,
|
|
12
13
|
transform(code, id) {
|
|
13
14
|
if (!TRANSFORM_PATTERN.test(id) || id.includes('node_modules')) {
|
|
14
15
|
return null;
|
|
15
16
|
}
|
|
16
|
-
if (!mightNeedTransform(code, { patterns: PATTERNS })) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
17
|
try {
|
|
20
|
-
let
|
|
18
|
+
let result = transform(ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true), program.get(root));
|
|
21
19
|
if (!result.changed) {
|
|
22
20
|
return null;
|
|
23
21
|
}
|
|
24
22
|
return { code: result.code, map: null };
|
|
25
23
|
}
|
|
26
24
|
catch (error) {
|
|
27
|
-
console.error(
|
|
25
|
+
console.error(`${PACKAGE}: Error transforming ${id}:`, error);
|
|
28
26
|
return null;
|
|
29
27
|
}
|
|
30
28
|
},
|
|
@@ -16,6 +16,5 @@ type TemplateInfo = {
|
|
|
16
16
|
};
|
|
17
17
|
declare const findHtmlTemplates: (sourceFile: ts.SourceFile) => TemplateInfo[];
|
|
18
18
|
declare const findReactiveCalls: (sourceFile: ts.SourceFile) => ReactiveCallInfo[];
|
|
19
|
-
|
|
20
|
-
export { findHtmlTemplates, findReactiveCalls, getTemplateExpressions };
|
|
19
|
+
export { findHtmlTemplates, findReactiveCalls };
|
|
21
20
|
export type { ReactiveCallInfo, TemplateInfo };
|
|
@@ -1,26 +1,5 @@
|
|
|
1
|
+
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY } from '../constants.js';
|
|
1
2
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
function extractTemplateInfo(node, depth) {
|
|
3
|
-
let expressions = [], literals = [], template = node.template;
|
|
4
|
-
if (ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
5
|
-
literals.push(template.text);
|
|
6
|
-
}
|
|
7
|
-
else if (ts.isTemplateExpression(template)) {
|
|
8
|
-
literals.push(template.head.text);
|
|
9
|
-
for (let i = 0, n = template.templateSpans.length; i < n; i++) {
|
|
10
|
-
let span = template.templateSpans[i];
|
|
11
|
-
expressions.push(span.expression);
|
|
12
|
-
literals.push(span.literal.text);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
return {
|
|
16
|
-
depth,
|
|
17
|
-
end: node.end,
|
|
18
|
-
expressions,
|
|
19
|
-
literals,
|
|
20
|
-
node,
|
|
21
|
-
start: node.getStart()
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
3
|
function isFunctionNode(node) {
|
|
25
4
|
return (ts.isArrowFunction(node) ||
|
|
26
5
|
ts.isFunctionDeclaration(node) ||
|
|
@@ -31,8 +10,8 @@ function visitReactiveCalls(node, calls) {
|
|
|
31
10
|
if (ts.isCallExpression(node) &&
|
|
32
11
|
ts.isPropertyAccessExpression(node.expression) &&
|
|
33
12
|
ts.isIdentifier(node.expression.expression) &&
|
|
34
|
-
node.expression.expression.text ===
|
|
35
|
-
node.expression.name.text ===
|
|
13
|
+
node.expression.expression.text === COMPILER_ENTRYPOINT &&
|
|
14
|
+
node.expression.name.text === COMPILER_ENTRYPOINT_REACTIVITY &&
|
|
36
15
|
node.arguments.length === 2) {
|
|
37
16
|
calls.push({
|
|
38
17
|
arrayArg: node.arguments[0],
|
|
@@ -46,8 +25,27 @@ function visitReactiveCalls(node, calls) {
|
|
|
46
25
|
}
|
|
47
26
|
function visitTemplates(node, depth, templates) {
|
|
48
27
|
let nextDepth = isFunctionNode(node) ? depth + 1 : depth;
|
|
49
|
-
if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && node.tag.text ===
|
|
50
|
-
|
|
28
|
+
if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && node.tag.text === COMPILER_ENTRYPOINT) {
|
|
29
|
+
let expressions = [], literals = [], template = node.template;
|
|
30
|
+
if (ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
31
|
+
literals.push(template.text);
|
|
32
|
+
}
|
|
33
|
+
else if (ts.isTemplateExpression(template)) {
|
|
34
|
+
literals.push(template.head.text);
|
|
35
|
+
for (let i = 0, n = template.templateSpans.length; i < n; i++) {
|
|
36
|
+
let span = template.templateSpans[i];
|
|
37
|
+
expressions.push(span.expression);
|
|
38
|
+
literals.push(span.literal.text);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
templates.push({
|
|
42
|
+
depth,
|
|
43
|
+
end: node.end,
|
|
44
|
+
expressions,
|
|
45
|
+
literals,
|
|
46
|
+
node,
|
|
47
|
+
start: node.getStart()
|
|
48
|
+
});
|
|
51
49
|
}
|
|
52
50
|
ts.forEachChild(node, child => visitTemplates(child, nextDepth, templates));
|
|
53
51
|
}
|
|
@@ -62,11 +60,4 @@ const findReactiveCalls = (sourceFile) => {
|
|
|
62
60
|
visitReactiveCalls(sourceFile, calls);
|
|
63
61
|
return calls;
|
|
64
62
|
};
|
|
65
|
-
|
|
66
|
-
let exprs = [];
|
|
67
|
-
for (let i = 0, n = info.expressions.length; i < n; i++) {
|
|
68
|
-
exprs.push(info.expressions[i].getText(sourceFile));
|
|
69
|
-
}
|
|
70
|
-
return exprs;
|
|
71
|
-
};
|
|
72
|
-
export { findHtmlTemplates, findReactiveCalls, getTemplateExpressions };
|
|
63
|
+
export { findHtmlTemplates, findReactiveCalls };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import { COMPILER_TYPES } from '../constants.js';
|
|
1
2
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
|
|
3
|
-
declare const
|
|
4
|
-
declare const
|
|
5
|
-
declare const generateSpreadBindings: (expr: ts.Expression, exprCode: string, elementVar: string, sourceFile: ts.SourceFile, checker?: ts.TypeChecker) => string[];
|
|
3
|
+
declare const analyzeExpression: (expr: ts.Expression, checker?: ts.TypeChecker) => COMPILER_TYPES;
|
|
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, sourceFile: ts.SourceFile, checker: ts.TypeChecker | undefined, ns: string) => string[];
|
|
6
6
|
export { analyzeExpression, generateAttributeBinding, generateSpreadBindings };
|
|
7
|
-
export type { SlotType };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../event/constants.js';
|
|
1
|
+
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../constants.js';
|
|
3
2
|
import { ts } from '@esportsplus/typescript';
|
|
4
3
|
function analyzeSpread(expr, checker) {
|
|
5
4
|
while (ts.isParenthesizedExpression(expr)) {
|
|
@@ -46,67 +45,47 @@ function extractTypePropertyKeys(type) {
|
|
|
46
45
|
}
|
|
47
46
|
return keys;
|
|
48
47
|
}
|
|
49
|
-
function
|
|
50
|
-
for (let i = 0, n = expr.properties.length; i < n; i++) {
|
|
51
|
-
let prop = expr.properties[i];
|
|
52
|
-
if (ts.isPropertyAssignment(prop)) {
|
|
53
|
-
let name = ts.isIdentifier(prop.name)
|
|
54
|
-
? prop.name.text
|
|
55
|
-
: ts.isStringLiteral(prop.name) ? prop.name.text : null;
|
|
56
|
-
if (name === key) {
|
|
57
|
-
return prop.initializer.getText(sourceFile);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
else if (ts.isShorthandPropertyAssignment(prop) && prop.name.text === key) {
|
|
61
|
-
return prop.name.text;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
function inferSlotType(expr, ctx) {
|
|
48
|
+
function inferCOMPILER_TYPES(expr, ctx) {
|
|
67
49
|
while (ts.isParenthesizedExpression(expr)) {
|
|
68
50
|
expr = expr.expression;
|
|
69
51
|
}
|
|
70
52
|
if (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr)) {
|
|
71
|
-
return
|
|
53
|
+
return COMPILER_TYPES.Effect;
|
|
72
54
|
}
|
|
73
55
|
if (ts.isCallExpression(expr) &&
|
|
74
56
|
ts.isPropertyAccessExpression(expr.expression) &&
|
|
75
57
|
ts.isIdentifier(expr.expression.expression) &&
|
|
76
|
-
expr.expression.expression.text ===
|
|
77
|
-
expr.expression.name.text ===
|
|
78
|
-
return
|
|
58
|
+
expr.expression.expression.text === COMPILER_ENTRYPOINT &&
|
|
59
|
+
expr.expression.name.text === COMPILER_ENTRYPOINT_REACTIVITY) {
|
|
60
|
+
return COMPILER_TYPES.ArraySlot;
|
|
79
61
|
}
|
|
80
|
-
if (ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text ===
|
|
81
|
-
return
|
|
62
|
+
if (ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT) {
|
|
63
|
+
return COMPILER_TYPES.DocumentFragment;
|
|
82
64
|
}
|
|
83
65
|
if (ts.isArrayLiteralExpression(expr)) {
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
if (ts.isStringLiteral(expr) || ts.isNoSubstitutionTemplateLiteral(expr)) {
|
|
87
|
-
return 'static';
|
|
88
|
-
}
|
|
89
|
-
if (ts.isNumericLiteral(expr)) {
|
|
90
|
-
return 'static';
|
|
66
|
+
return COMPILER_TYPES.ArraySlot;
|
|
91
67
|
}
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
68
|
+
if (ts.isNumericLiteral(expr) ||
|
|
69
|
+
ts.isStringLiteral(expr) ||
|
|
70
|
+
ts.isNoSubstitutionTemplateLiteral(expr) ||
|
|
71
|
+
expr.kind === ts.SyntaxKind.TrueKeyword ||
|
|
72
|
+
expr.kind === ts.SyntaxKind.FalseKeyword ||
|
|
73
|
+
expr.kind === ts.SyntaxKind.NullKeyword ||
|
|
74
|
+
expr.kind === ts.SyntaxKind.UndefinedKeyword) {
|
|
75
|
+
return COMPILER_TYPES.Static;
|
|
97
76
|
}
|
|
98
77
|
if (ts.isTemplateExpression(expr)) {
|
|
99
|
-
return
|
|
78
|
+
return COMPILER_TYPES.Primitive;
|
|
100
79
|
}
|
|
101
80
|
if (ts.isConditionalExpression(expr)) {
|
|
102
|
-
let whenFalse =
|
|
81
|
+
let whenFalse = inferCOMPILER_TYPES(expr.whenFalse, ctx), whenTrue = inferCOMPILER_TYPES(expr.whenTrue, ctx);
|
|
103
82
|
if (whenTrue === whenFalse) {
|
|
104
83
|
return whenTrue;
|
|
105
84
|
}
|
|
106
|
-
if (whenTrue ===
|
|
107
|
-
return
|
|
85
|
+
if (whenTrue === COMPILER_TYPES.Effect || whenFalse === COMPILER_TYPES.Effect) {
|
|
86
|
+
return COMPILER_TYPES.Effect;
|
|
108
87
|
}
|
|
109
|
-
return
|
|
88
|
+
return COMPILER_TYPES.Unknown;
|
|
110
89
|
}
|
|
111
90
|
if (ctx?.checker) {
|
|
112
91
|
let checker = ctx.checker;
|
|
@@ -114,10 +93,10 @@ function inferSlotType(expr, ctx) {
|
|
|
114
93
|
try {
|
|
115
94
|
let type = checker.getTypeAtLocation(expr);
|
|
116
95
|
if (isTypeFunction(type, checker)) {
|
|
117
|
-
return
|
|
96
|
+
return COMPILER_TYPES.Effect;
|
|
118
97
|
}
|
|
119
98
|
if (isTypeArray(type, checker)) {
|
|
120
|
-
return
|
|
99
|
+
return COMPILER_TYPES.ArraySlot;
|
|
121
100
|
}
|
|
122
101
|
}
|
|
123
102
|
catch {
|
|
@@ -127,10 +106,10 @@ function inferSlotType(expr, ctx) {
|
|
|
127
106
|
try {
|
|
128
107
|
let type = checker.getTypeAtLocation(expr);
|
|
129
108
|
if (isTypeFunction(type, checker)) {
|
|
130
|
-
return
|
|
109
|
+
return COMPILER_TYPES.Effect;
|
|
131
110
|
}
|
|
132
111
|
if (isTypeArray(type, checker)) {
|
|
133
|
-
return
|
|
112
|
+
return COMPILER_TYPES.ArraySlot;
|
|
134
113
|
}
|
|
135
114
|
}
|
|
136
115
|
catch {
|
|
@@ -140,28 +119,24 @@ function inferSlotType(expr, ctx) {
|
|
|
140
119
|
try {
|
|
141
120
|
let type = checker.getTypeAtLocation(expr);
|
|
142
121
|
if (isTypeFunction(type, checker)) {
|
|
143
|
-
return
|
|
122
|
+
return COMPILER_TYPES.Effect;
|
|
144
123
|
}
|
|
145
124
|
if (isTypeArray(type, checker)) {
|
|
146
|
-
return
|
|
125
|
+
return COMPILER_TYPES.ArraySlot;
|
|
147
126
|
}
|
|
148
127
|
}
|
|
149
128
|
catch {
|
|
150
129
|
}
|
|
151
130
|
}
|
|
152
131
|
}
|
|
153
|
-
return
|
|
132
|
+
return COMPILER_TYPES.Unknown;
|
|
154
133
|
}
|
|
155
134
|
function isTypeArray(type, checker) {
|
|
156
|
-
|
|
157
|
-
if (typeStr.endsWith('[]') || typeStr.startsWith('Array<') || typeStr.startsWith('ReactiveArray<')) {
|
|
135
|
+
if (checker.isArrayType(type)) {
|
|
158
136
|
return true;
|
|
159
137
|
}
|
|
160
138
|
let symbol = type.getSymbol();
|
|
161
|
-
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
return false;
|
|
139
|
+
return symbol?.getName() === 'ReactiveArray';
|
|
165
140
|
}
|
|
166
141
|
function isTypeFunction(type, checker) {
|
|
167
142
|
if (type.getCallSignatures().length > 0) {
|
|
@@ -177,52 +152,67 @@ function isTypeFunction(type, checker) {
|
|
|
177
152
|
return false;
|
|
178
153
|
}
|
|
179
154
|
const analyzeExpression = (expr, checker) => {
|
|
180
|
-
return
|
|
155
|
+
return inferCOMPILER_TYPES(expr, checker ? { checker } : undefined);
|
|
181
156
|
};
|
|
182
|
-
const generateAttributeBinding = (elementVar, name, expr, staticValue) => {
|
|
183
|
-
let n = getNames();
|
|
157
|
+
const generateAttributeBinding = (elementVar, name, expr, staticValue, ns) => {
|
|
184
158
|
if (name.startsWith('on') && name.length > 2) {
|
|
185
159
|
let event = name.slice(2).toLowerCase(), key = name.toLowerCase();
|
|
186
160
|
if (LIFECYCLE_EVENTS.has(key)) {
|
|
187
|
-
return `${
|
|
161
|
+
return `${ns}.event.${key}(${elementVar}, ${expr});`;
|
|
188
162
|
}
|
|
189
163
|
if (DIRECT_ATTACH_EVENTS.has(key)) {
|
|
190
|
-
return `${
|
|
164
|
+
return `${ns}.event.direct(${elementVar}, '${event}', ${expr});`;
|
|
191
165
|
}
|
|
192
|
-
return `${
|
|
166
|
+
return `${ns}.event.delegate(${elementVar}, '${event}', ${expr});`;
|
|
193
167
|
}
|
|
194
168
|
if (name === 'class') {
|
|
195
|
-
return `${
|
|
169
|
+
return `${ns}.attributes.setClass(${elementVar}, '${staticValue}', ${expr});`;
|
|
196
170
|
}
|
|
197
171
|
if (name === 'spread') {
|
|
198
|
-
return `${
|
|
172
|
+
return `${ns}.attributes.spread(${elementVar}, ${expr});`;
|
|
199
173
|
}
|
|
200
174
|
if (name === 'style') {
|
|
201
|
-
return `${
|
|
175
|
+
return `${ns}.attributes.setStyle(${elementVar}, '${staticValue}', ${expr});`;
|
|
202
176
|
}
|
|
203
|
-
return `${
|
|
177
|
+
return `${ns}.attributes.setProperty(${elementVar}, '${name}', ${expr});`;
|
|
204
178
|
};
|
|
205
|
-
const generateSpreadBindings = (expr, exprCode, elementVar, sourceFile, checker) => {
|
|
179
|
+
const generateSpreadBindings = (expr, exprCode, elementVar, sourceFile, checker, ns) => {
|
|
206
180
|
while (ts.isParenthesizedExpression(expr)) {
|
|
207
181
|
expr = expr.expression;
|
|
208
182
|
}
|
|
209
183
|
let analysis = analyzeSpread(expr, checker);
|
|
210
184
|
if (!analysis.canUnpack) {
|
|
211
|
-
return [`${
|
|
185
|
+
return [`${ns}.attributes.spread(${elementVar}, ${exprCode});`];
|
|
212
186
|
}
|
|
213
187
|
let lines = [];
|
|
214
188
|
if (ts.isObjectLiteralExpression(expr)) {
|
|
215
189
|
for (let i = 0, n = analysis.keys.length; i < n; i++) {
|
|
216
|
-
let key = analysis.keys[i], value =
|
|
190
|
+
let key = analysis.keys[i], value = null;
|
|
191
|
+
for (let j = 0, m = expr.properties.length; j < m; j++) {
|
|
192
|
+
let prop = expr.properties[j];
|
|
193
|
+
if (ts.isPropertyAssignment(prop)) {
|
|
194
|
+
let name = ts.isIdentifier(prop.name)
|
|
195
|
+
? prop.name.text
|
|
196
|
+
: ts.isStringLiteral(prop.name) ? prop.name.text : null;
|
|
197
|
+
if (name === key) {
|
|
198
|
+
value = prop.initializer.getText(sourceFile);
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (ts.isShorthandPropertyAssignment(prop) && prop.name.text === key) {
|
|
203
|
+
value = prop.name.text;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
217
207
|
if (value !== null) {
|
|
218
|
-
lines.push(generateAttributeBinding(elementVar, key, value, ''));
|
|
208
|
+
lines.push(generateAttributeBinding(elementVar, key, value, '', ns));
|
|
219
209
|
}
|
|
220
210
|
}
|
|
221
211
|
}
|
|
222
212
|
else {
|
|
223
213
|
for (let i = 0, n = analysis.keys.length; i < n; i++) {
|
|
224
214
|
let key = analysis.keys[i];
|
|
225
|
-
lines.push(generateAttributeBinding(elementVar, key, `${exprCode}.${key}`, ''));
|
|
215
|
+
lines.push(generateAttributeBinding(elementVar, key, `${exprCode}.${key}`, '', ns));
|
|
226
216
|
}
|
|
227
217
|
}
|
|
228
218
|
return lines;
|
package/build/types.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ type Attributes<T extends HTMLElement = Element> = {
|
|
|
17
17
|
type Effect<T> = () => T extends [] ? Renderable<T>[] : Renderable<T>;
|
|
18
18
|
type Element = HTMLElement & Attributes<any>;
|
|
19
19
|
type Primitive = bigint | boolean | null | number | string | undefined;
|
|
20
|
-
type Renderable<T> =
|
|
20
|
+
type Renderable<T> = ArraySlot<T> | DocumentFragment | Effect<T> | Node | NodeList | Primitive | Renderable<T>[];
|
|
21
21
|
type SlotGroup = {
|
|
22
22
|
head: Element;
|
|
23
23
|
tail: Element;
|
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.7",
|
|
6
6
|
"@esportsplus/utilities": "^0.27.2",
|
|
7
7
|
"serve": "^14.2.5"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
|
-
"@esportsplus/typescript": "^0.17.
|
|
10
|
+
"@esportsplus/typescript": "^0.17.5",
|
|
11
11
|
"@types/node": "^25.0.3",
|
|
12
12
|
"vite": "^7.3.0",
|
|
13
13
|
"vite-tsconfig-paths": "^6.0.3"
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"types": "./build/constants.d.ts"
|
|
23
23
|
},
|
|
24
24
|
"./plugins/tsc": {
|
|
25
|
+
"types": "./build/transformer/plugins/tsc.d.ts",
|
|
25
26
|
"import": "./build/transformer/plugins/tsc.js",
|
|
26
|
-
"require": "./build/transformer/plugins/tsc.js"
|
|
27
|
-
"types": "./build/transformer/plugins/tsc.d.ts"
|
|
27
|
+
"require": "./build/transformer/plugins/tsc.js"
|
|
28
28
|
},
|
|
29
29
|
"./plugins/vite": {
|
|
30
|
-
"
|
|
31
|
-
"
|
|
30
|
+
"types": "./build/transformer/plugins/vite.d.ts",
|
|
31
|
+
"import": "./build/transformer/plugins/vite.js"
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"main": "./build/index.js",
|
|
@@ -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.1",
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsc",
|
|
46
46
|
"build:test": "vite build --config test/vite.config.ts",
|
package/src/attributes.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { effect } from '@esportsplus/reactivity';
|
|
2
2
|
import { isArray, isObject } from '@esportsplus/utilities';
|
|
3
|
-
import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, STATE_HYDRATING, STATE_NONE, STATE_WAITING } from './constants';
|
|
3
|
+
import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE } from './constants';
|
|
4
4
|
import { Attributes, Element } from './types';
|
|
5
5
|
import { raf } from './utilities';
|
|
6
6
|
import q from '@esportsplus/queue';
|
|
7
7
|
import event from './event';
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
const STORE = Symbol();
|
|
11
|
-
|
|
12
|
-
|
|
13
10
|
type Context = {
|
|
14
11
|
effect?: 0,
|
|
15
12
|
element: Element;
|
package/src/constants.ts
CHANGED
|
@@ -1,11 +1,45 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { uid } from '@esportsplus/typescript/transformer';
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
const ARRAY_SLOT = Symbol('template.array.slot');
|
|
5
5
|
|
|
6
6
|
const CLEANUP = Symbol('template.cleanup');
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
const COMPILER_ENTRYPOINT = 'html';
|
|
10
|
+
|
|
11
|
+
const COMPILER_ENTRYPOINT_REACTIVITY = 'reactive';
|
|
12
|
+
|
|
13
|
+
const COMPILER_NAMESPACE = uid('template');
|
|
14
|
+
|
|
15
|
+
const enum COMPILER_TYPES {
|
|
16
|
+
ArraySlot,
|
|
17
|
+
AttributeSlot,
|
|
18
|
+
AttributeSpreadSlot,
|
|
19
|
+
DocumentFragment,
|
|
20
|
+
Effect,
|
|
21
|
+
NodeSlot,
|
|
22
|
+
Primitive,
|
|
23
|
+
Static,
|
|
24
|
+
Unknown
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const DIRECT_ATTACH_EVENTS = new Set<string>([
|
|
28
|
+
'onblur',
|
|
29
|
+
'onerror',
|
|
30
|
+
'onfocus', 'onfocusin', 'onfocusout',
|
|
31
|
+
'onload',
|
|
32
|
+
'onplay', 'onpause', 'onended', 'ontimeupdate',
|
|
33
|
+
'onreset',
|
|
34
|
+
'onscroll', 'onsubmit'
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const LIFECYCLE_EVENTS = new Set<string>([
|
|
38
|
+
'onconnect', 'ondisconnect', 'onrender', 'onresize', 'ontick'
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
const PACKAGE = '@esportsplus/template';
|
|
42
|
+
|
|
9
43
|
|
|
10
44
|
const SLOT_HTML = '<!--$-->';
|
|
11
45
|
|
|
@@ -21,7 +55,9 @@ const STORE = Symbol('template.store');
|
|
|
21
55
|
export {
|
|
22
56
|
ARRAY_SLOT,
|
|
23
57
|
CLEANUP,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
58
|
+
COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, COMPILER_TYPES,
|
|
59
|
+
DIRECT_ATTACH_EVENTS,
|
|
60
|
+
LIFECYCLE_EVENTS,
|
|
61
|
+
PACKAGE,
|
|
62
|
+
SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE,
|
|
63
|
+
};
|
package/src/html.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Reactive } from '@esportsplus/reactivity';
|
|
2
2
|
import { Attribute, Attributes, Renderable } from '~/types';
|
|
3
3
|
import { ArraySlot } from '~/slot/array';
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
type Values<T> = Attribute | Attributes<any> |
|
|
6
|
+
type Values<T> = ArraySlot<T extends unknown[] ? T : never> | Attribute | Attributes<any> | Renderable<T>;
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
const html = <T>(_literals: TemplateStringsArray, ..._values: (Values<T> | Values<T>[])[]): DocumentFragment => {
|
|
10
10
|
throw new Error('html`` templates must be compiled. Ensure vite-plugin is configured.');
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
html.reactive = <T>(_arr:
|
|
13
|
+
html.reactive = <T>(_arr: Reactive<T[]>, _template: (value: T) => DocumentFragment): ArraySlot<T[]> => {
|
|
14
14
|
throw new Error('html.reactive() must be compiled. Ensure vite-plugin is configured.');
|
|
15
15
|
};
|
|
16
16
|
|
package/src/slot/array.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { read, root,
|
|
2
|
-
import { ARRAY_SLOT
|
|
1
|
+
import { read, root, signal, write, Reactive } from '@esportsplus/reactivity';
|
|
2
|
+
import { ARRAY_SLOT } from '~/constants';
|
|
3
3
|
import { Element, SlotGroup } from '~/types';
|
|
4
|
-
import { clone, marker, raf } from '~/utilities';
|
|
4
|
+
import { clone, fragment, marker, raf } from '~/utilities';
|
|
5
5
|
import { ondisconnect, remove } from './cleanup';
|
|
6
6
|
import html from '~/html';
|
|
7
7
|
|
|
@@ -18,18 +18,21 @@ type ArraySlotOp<T> =
|
|
|
18
18
|
| { op: 'sort'; order: number[] };
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
const EMPTY_FRAGMENT = fragment('');
|
|
22
|
+
|
|
23
|
+
|
|
21
24
|
class ArraySlot<T> {
|
|
22
25
|
private marker: Element;
|
|
23
26
|
private nodes: SlotGroup[] = [];
|
|
24
27
|
private queue: ArraySlotOp<T>[] = [];
|
|
25
28
|
private scheduled = false;
|
|
26
29
|
private signal;
|
|
27
|
-
private template: (...args: Parameters<(value: T) => ReturnType<typeof html>>) => SlotGroup;
|
|
30
|
+
private template: (...args: Parameters<(value: Reactive<T[]>[number]) => ReturnType<typeof html>>) => SlotGroup;
|
|
28
31
|
|
|
29
32
|
readonly fragment: DocumentFragment;
|
|
30
33
|
|
|
31
34
|
|
|
32
|
-
constructor(private array:
|
|
35
|
+
constructor(private array: Reactive<T[]>, template: ((value: Reactive<T[]>[number]) => ReturnType<typeof html>)) {
|
|
33
36
|
let fragment = this.fragment = clone(EMPTY_FRAGMENT);
|
|
34
37
|
|
|
35
38
|
this.marker = marker.cloneNode() as unknown as Element;
|
|
@@ -171,7 +174,7 @@ class ArraySlot<T> {
|
|
|
171
174
|
}
|
|
172
175
|
});
|
|
173
176
|
|
|
174
|
-
|
|
177
|
+
write(this.signal, this.nodes.length);
|
|
175
178
|
});
|
|
176
179
|
}
|
|
177
180
|
|
package/src/slot/render.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { isArray } from '@esportsplus/utilities';
|
|
2
|
-
import { ARRAY_SLOT
|
|
2
|
+
import { ARRAY_SLOT } from '~/constants';
|
|
3
3
|
import { Element } from '~/types';
|
|
4
|
-
import { clone, text } from '~/utilities';
|
|
4
|
+
import { clone, fragment, text } from '~/utilities';
|
|
5
5
|
import { ArraySlot } from './array';
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
const EMPTY_FRAGMENT = fragment('');
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
export default function render(anchor: Element, value: unknown): Node {
|
|
9
12
|
if (value == null || value === false || value === '') {
|
|
10
13
|
return EMPTY_FRAGMENT;
|