@esportsplus/template 0.39.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -26
- package/build/compiler/codegen.d.ts +1 -1
- package/build/compiler/codegen.js +100 -141
- package/build/compiler/constants.d.ts +16 -0
- package/build/compiler/constants.js +19 -0
- package/build/compiler/index.d.ts +6 -3
- package/build/compiler/index.js +22 -23
- package/build/compiler/parser.d.ts +3 -3
- package/build/compiler/parser.js +5 -4
- package/build/compiler/plugins/tsc.d.ts +3 -2
- package/build/compiler/plugins/tsc.js +4 -2
- package/build/compiler/plugins/vite.js +4 -3
- package/build/compiler/ts-analyzer.d.ts +2 -2
- package/build/compiler/ts-analyzer.js +15 -16
- package/build/compiler/ts-parser.js +6 -6
- package/build/constants.d.ts +1 -16
- package/build/constants.js +1 -19
- package/package.json +7 -3
- package/src/compiler/codegen.ts +126 -192
- package/src/compiler/constants.ts +26 -0
- package/src/compiler/index.ts +24 -35
- package/src/compiler/parser.ts +7 -6
- package/src/compiler/plugins/tsc.ts +4 -2
- package/src/compiler/plugins/vite.ts +4 -3
- package/src/compiler/ts-analyzer.ts +16 -18
- package/src/compiler/ts-parser.ts +6 -7
- package/src/constants.ts +0 -25
- package/test/counter.ts +113 -0
- package/test/effects.ts +1 -1
- package/test/events.ts +1 -1
- package/test/imported-values.ts +1 -1
- package/test/integration/tsconfig.json +0 -1
- package/test/nested.ts +20 -1
- package/test/slots.ts +1 -1
- package/test/spread.ts +1 -1
- package/test/static.ts +1 -1
- package/test/templates.ts +1 -1
- package/test/vite.config.ts +2 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { uid } from '@esportsplus/typescript/compiler';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const ENTRYPOINT = 'html';
|
|
5
|
+
|
|
6
|
+
const ENTRYPOINT_REACTIVITY = 'reactive';
|
|
7
|
+
|
|
8
|
+
const NAMESPACE = uid('template');
|
|
9
|
+
|
|
10
|
+
const PACKAGE = '@esportsplus/template';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const enum TYPES {
|
|
14
|
+
ArraySlot = 'array-slot',
|
|
15
|
+
Attributes = 'attributes',
|
|
16
|
+
Attribute = 'attribute',
|
|
17
|
+
DocumentFragment = 'document-fragment',
|
|
18
|
+
Effect = 'effect',
|
|
19
|
+
Node = 'node',
|
|
20
|
+
Primitive = 'primitive',
|
|
21
|
+
Static = 'static',
|
|
22
|
+
Unknown = 'unknown'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
export { ENTRYPOINT, ENTRYPOINT_REACTIVITY, NAMESPACE, PACKAGE, TYPES };
|
package/src/compiler/index.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
2
|
import { ast } from '@esportsplus/typescript/compiler';
|
|
3
|
-
import type { ImportIntent,
|
|
4
|
-
import {
|
|
3
|
+
import type { ImportIntent, ReplacementIntent, TransformContext } from '@esportsplus/typescript/compiler';
|
|
4
|
+
import { ENTRYPOINT, ENTRYPOINT_REACTIVITY, NAMESPACE, PACKAGE } from './constants';
|
|
5
5
|
import { generateCode, printer } from './codegen';
|
|
6
6
|
import { findHtmlTemplates, findReactiveCalls } from './ts-parser';
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
export default {
|
|
10
10
|
patterns: [
|
|
11
|
-
`${
|
|
12
|
-
`${
|
|
11
|
+
`${ENTRYPOINT}\``,
|
|
12
|
+
`${ENTRYPOINT}.${ENTRYPOINT_REACTIVITY}`
|
|
13
13
|
],
|
|
14
|
-
|
|
15
14
|
transform: (ctx: TransformContext) => {
|
|
16
|
-
let
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
let intents = {
|
|
16
|
+
imports: [] as ImportIntent[],
|
|
17
|
+
prepend: [] as string[],
|
|
18
|
+
replacements: [] as ReplacementIntent[]
|
|
19
|
+
},
|
|
19
20
|
ranges: { end: number; start: number }[] = [],
|
|
20
|
-
|
|
21
|
-
removeImports: string[] = [],
|
|
22
|
-
// Find templates first to build exclusion ranges
|
|
21
|
+
remove: string[] = [],
|
|
23
22
|
templates = findHtmlTemplates(ctx.sourceFile, ctx.checker);
|
|
24
23
|
|
|
25
24
|
for (let i = 0, n = templates.length; i < n; i++) {
|
|
@@ -29,19 +28,17 @@ const plugin: Plugin = {
|
|
|
29
28
|
});
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
let reactiveCalls = findReactiveCalls(ctx.sourceFile, ctx.checker);
|
|
31
|
+
let calls = findReactiveCalls(ctx.sourceFile, ctx.checker);
|
|
34
32
|
|
|
35
|
-
for (let i = 0, n =
|
|
36
|
-
let call =
|
|
33
|
+
for (let i = 0, n = calls.length; i < n; i++) {
|
|
34
|
+
let call = calls[i];
|
|
37
35
|
|
|
38
|
-
// Skip reactive calls that are inside template expressions - handled by template codegen
|
|
39
36
|
if (ast.inRange(ranges, call.start, call.end)) {
|
|
40
37
|
continue;
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
replacements.push({
|
|
44
|
-
generate: (sourceFile) => `new ${
|
|
40
|
+
intents.replacements.push({
|
|
41
|
+
generate: (sourceFile) => `new ${NAMESPACE}.ArraySlot(
|
|
45
42
|
${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)},
|
|
46
43
|
${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)}
|
|
47
44
|
)`,
|
|
@@ -49,32 +46,24 @@ const plugin: Plugin = {
|
|
|
49
46
|
});
|
|
50
47
|
}
|
|
51
48
|
|
|
52
|
-
// Transform html`` templates
|
|
53
49
|
if (templates.length > 0) {
|
|
54
50
|
let result = generateCode(templates, ctx.sourceFile, ctx.checker);
|
|
55
51
|
|
|
56
|
-
prepend.push(...result.prepend);
|
|
57
|
-
replacements.push(...result.replacements);
|
|
58
|
-
|
|
52
|
+
intents.prepend.push(...result.prepend);
|
|
53
|
+
intents.replacements.push(...result.replacements);
|
|
54
|
+
remove.push(ENTRYPOINT);
|
|
59
55
|
}
|
|
60
56
|
|
|
61
|
-
if (replacements.length === 0 && prepend.length === 0) {
|
|
57
|
+
if (intents.replacements.length === 0 && intents.prepend.length === 0) {
|
|
62
58
|
return {};
|
|
63
59
|
}
|
|
64
60
|
|
|
65
|
-
|
|
66
|
-
namespace:
|
|
61
|
+
intents.imports.push({
|
|
62
|
+
namespace: NAMESPACE,
|
|
67
63
|
package: PACKAGE,
|
|
68
|
-
remove:
|
|
64
|
+
remove: remove
|
|
69
65
|
});
|
|
70
66
|
|
|
71
|
-
return
|
|
72
|
-
imports: importsIntent,
|
|
73
|
-
prepend,
|
|
74
|
-
replacements
|
|
75
|
-
};
|
|
67
|
+
return intents;
|
|
76
68
|
}
|
|
77
69
|
};
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
export default plugin;
|
package/src/compiler/parser.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SLOT_HTML } from '../constants';
|
|
2
|
+
import { PACKAGE, TYPES } from './constants';
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
type NodePath = ('firstChild' | 'firstElementChild' | 'nextElementSibling' | 'nextSibling')[];
|
|
@@ -90,8 +91,8 @@ const parse = (literals: string[]) => {
|
|
|
90
91
|
parsed = html.split(SLOT_MARKER),
|
|
91
92
|
slot = 0,
|
|
92
93
|
slots: (
|
|
93
|
-
{ path: NodePath; type:
|
|
94
|
-
{ attributes: typeof attributes[string]; path: NodePath; type:
|
|
94
|
+
{ path: NodePath; type: TYPES.Node } |
|
|
95
|
+
{ attributes: typeof attributes[string]; path: NodePath; type: TYPES.Attribute }
|
|
95
96
|
)[] = [];
|
|
96
97
|
|
|
97
98
|
{
|
|
@@ -154,7 +155,7 @@ const parse = (literals: string[]) => {
|
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
else {
|
|
157
|
-
names.push(
|
|
158
|
+
names.push(TYPES.Attributes);
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
161
|
}
|
|
@@ -184,7 +185,7 @@ const parse = (literals: string[]) => {
|
|
|
184
185
|
throw new Error(`${PACKAGE}: attribute metadata could not be found for '${attr}'`);
|
|
185
186
|
}
|
|
186
187
|
|
|
187
|
-
slots.push({ attributes: attrs, path, type:
|
|
188
|
+
slots.push({ attributes: attrs, path, type: TYPES.Attribute });
|
|
188
189
|
|
|
189
190
|
for (let i = 0, n = attrs.names.length; i < n; i++) {
|
|
190
191
|
buffer += parsed[slot++];
|
|
@@ -201,7 +202,7 @@ const parse = (literals: string[]) => {
|
|
|
201
202
|
buffer += parsed[slot++] + SLOT_HTML;
|
|
202
203
|
slots.push({
|
|
203
204
|
path: methods(parent.children, parent.path, 'firstChild', 'nextSibling'),
|
|
204
|
-
type:
|
|
205
|
+
type: TYPES.Node
|
|
205
206
|
});
|
|
206
207
|
}
|
|
207
208
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import plugin from '
|
|
1
|
+
import { plugin } from '@esportsplus/typescript/compiler';
|
|
2
|
+
import reactivity from '@esportsplus/reactivity/compiler';
|
|
3
|
+
import template from '..';
|
|
2
4
|
|
|
3
5
|
|
|
4
|
-
export default plugin
|
|
6
|
+
export default plugin.tsc([reactivity, template]) as ReturnType<typeof plugin.tsc>;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { plugin } from '@esportsplus/typescript/compiler';
|
|
2
|
-
import { PACKAGE } from '
|
|
3
|
-
import
|
|
2
|
+
import { PACKAGE } from '../constants';
|
|
3
|
+
import reactivity from '@esportsplus/reactivity/compiler';
|
|
4
|
+
import template from '..';
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
export default plugin.vite({
|
|
7
8
|
name: PACKAGE,
|
|
8
|
-
plugins: [
|
|
9
|
+
plugins: [reactivity, template]
|
|
9
10
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_TYPES } from '~/constants';
|
|
2
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { ENTRYPOINT, ENTRYPOINT_REACTIVITY, TYPES } from './constants';
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
// Union types that mix functions with non-functions (e.g., Renderable)
|
|
@@ -19,13 +19,13 @@ function isTypeFunction(type: ts.Type, checker: ts.TypeChecker): boolean {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
const analyze = (expr: ts.Expression, checker?: ts.TypeChecker):
|
|
22
|
+
const analyze = (expr: ts.Expression, checker?: ts.TypeChecker): TYPES => {
|
|
23
23
|
while (ts.isParenthesizedExpression(expr)) {
|
|
24
24
|
expr = expr.expression;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
if (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr)) {
|
|
28
|
-
return
|
|
28
|
+
return TYPES.Effect;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// Only html.reactive() calls become ArraySlot - handled by generateReactiveInlining
|
|
@@ -33,14 +33,14 @@ const analyze = (expr: ts.Expression, checker?: ts.TypeChecker): COMPILER_TYPES
|
|
|
33
33
|
ts.isCallExpression(expr) &&
|
|
34
34
|
ts.isPropertyAccessExpression(expr.expression) &&
|
|
35
35
|
ts.isIdentifier(expr.expression.expression) &&
|
|
36
|
-
expr.expression.expression.text ===
|
|
37
|
-
expr.expression.name.text ===
|
|
36
|
+
expr.expression.expression.text === ENTRYPOINT &&
|
|
37
|
+
expr.expression.name.text === ENTRYPOINT_REACTIVITY
|
|
38
38
|
) {
|
|
39
|
-
return
|
|
39
|
+
return TYPES.ArraySlot;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
if (ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text ===
|
|
43
|
-
return
|
|
42
|
+
if (ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === ENTRYPOINT) {
|
|
43
|
+
return TYPES.DocumentFragment;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
if (
|
|
@@ -52,11 +52,11 @@ const analyze = (expr: ts.Expression, checker?: ts.TypeChecker): COMPILER_TYPES
|
|
|
52
52
|
expr.kind === ts.SyntaxKind.NullKeyword ||
|
|
53
53
|
expr.kind === ts.SyntaxKind.UndefinedKeyword
|
|
54
54
|
) {
|
|
55
|
-
return
|
|
55
|
+
return TYPES.Static;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
if (ts.isTemplateExpression(expr)) {
|
|
59
|
-
return
|
|
59
|
+
return TYPES.Primitive;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
if (ts.isConditionalExpression(expr)) {
|
|
@@ -67,25 +67,23 @@ const analyze = (expr: ts.Expression, checker?: ts.TypeChecker): COMPILER_TYPES
|
|
|
67
67
|
return whenTrue;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
if (whenTrue ===
|
|
71
|
-
return
|
|
70
|
+
if (whenTrue === TYPES.Effect || whenFalse === TYPES.Effect) {
|
|
71
|
+
return TYPES.Effect;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
return
|
|
74
|
+
return TYPES.Unknown;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
if (checker && (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr) || ts.isCallExpression(expr))) {
|
|
78
78
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (isTypeFunction(type, checker)) {
|
|
82
|
-
return COMPILER_TYPES.Effect;
|
|
79
|
+
if (isTypeFunction(checker.getTypeAtLocation(expr), checker)) {
|
|
80
|
+
return TYPES.Effect;
|
|
83
81
|
}
|
|
84
82
|
}
|
|
85
83
|
catch {}
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
return
|
|
86
|
+
return TYPES.Unknown;
|
|
89
87
|
};
|
|
90
88
|
|
|
91
89
|
export { analyze };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
2
|
import { imports } from '@esportsplus/typescript/compiler';
|
|
3
|
-
import {
|
|
3
|
+
import { ENTRYPOINT, ENTRYPOINT_REACTIVITY, PACKAGE } from './constants';
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
type ReactiveCallInfo = {
|
|
@@ -26,10 +26,10 @@ function visitReactiveCalls(node: ts.Node, calls: ReactiveCallInfo[], checker: t
|
|
|
26
26
|
ts.isCallExpression(node) &&
|
|
27
27
|
ts.isPropertyAccessExpression(node.expression) &&
|
|
28
28
|
ts.isIdentifier(node.expression.expression) &&
|
|
29
|
-
node.expression.name.text ===
|
|
29
|
+
node.expression.name.text === ENTRYPOINT_REACTIVITY &&
|
|
30
30
|
node.arguments.length === 2 &&
|
|
31
|
-
node.expression.expression.text ===
|
|
32
|
-
(!checker || imports.
|
|
31
|
+
node.expression.expression.text === ENTRYPOINT &&
|
|
32
|
+
(!checker || imports.includes(checker, node.expression.expression, PACKAGE, ENTRYPOINT))
|
|
33
33
|
) {
|
|
34
34
|
calls.push({
|
|
35
35
|
arrayArg: node.arguments[0],
|
|
@@ -51,8 +51,8 @@ function visitTemplates(node: ts.Node, depth: number, templates: TemplateInfo[],
|
|
|
51
51
|
if (
|
|
52
52
|
ts.isTaggedTemplateExpression(node) &&
|
|
53
53
|
ts.isIdentifier(node.tag) &&
|
|
54
|
-
node.tag.text ===
|
|
55
|
-
(!checker || imports.
|
|
54
|
+
node.tag.text === ENTRYPOINT &&
|
|
55
|
+
(!checker || imports.includes(checker, node.tag, PACKAGE, ENTRYPOINT))
|
|
56
56
|
) {
|
|
57
57
|
let { expressions, literals } = extractTemplateParts(node.template);
|
|
58
58
|
|
|
@@ -96,7 +96,6 @@ const findHtmlTemplates = (sourceFile: ts.SourceFile, checker?: ts.TypeChecker):
|
|
|
96
96
|
|
|
97
97
|
visitTemplates(sourceFile, 0, templates, checker);
|
|
98
98
|
|
|
99
|
-
// Sort by depth descending (deepest first), then by position for stable ordering
|
|
100
99
|
return templates.sort((a, b) => a.depth !== b.depth ? b.depth - a.depth : a.start - b.start);
|
|
101
100
|
};
|
|
102
101
|
|
package/src/constants.ts
CHANGED
|
@@ -1,28 +1,7 @@
|
|
|
1
|
-
import { uid } from '@esportsplus/typescript/compiler';
|
|
2
|
-
|
|
3
|
-
|
|
4
1
|
const ARRAY_SLOT = Symbol('template.array.slot');
|
|
5
2
|
|
|
6
3
|
const CLEANUP = Symbol('template.cleanup');
|
|
7
4
|
|
|
8
|
-
const COMPILER_ENTRYPOINT = 'html';
|
|
9
|
-
|
|
10
|
-
const COMPILER_ENTRYPOINT_REACTIVITY = 'reactive';
|
|
11
|
-
|
|
12
|
-
const COMPILER_NAMESPACE = uid('template');
|
|
13
|
-
|
|
14
|
-
const enum COMPILER_TYPES {
|
|
15
|
-
ArraySlot = 'array-slot',
|
|
16
|
-
Attributes = 'attributes',
|
|
17
|
-
Attribute = 'attribute',
|
|
18
|
-
DocumentFragment = 'document-fragment',
|
|
19
|
-
Effect = 'effect',
|
|
20
|
-
Node = 'node',
|
|
21
|
-
Primitive = 'primitive',
|
|
22
|
-
Static = 'static',
|
|
23
|
-
Unknown = 'unknown'
|
|
24
|
-
};
|
|
25
|
-
|
|
26
5
|
const DIRECT_ATTACH_EVENTS = new Set<string>([
|
|
27
6
|
'onblur',
|
|
28
7
|
'onerror',
|
|
@@ -37,8 +16,6 @@ const LIFECYCLE_EVENTS = new Set<string>([
|
|
|
37
16
|
'onconnect', 'ondisconnect', 'onrender', 'onresize', 'ontick'
|
|
38
17
|
]);
|
|
39
18
|
|
|
40
|
-
const PACKAGE = '@esportsplus/template';
|
|
41
|
-
|
|
42
19
|
const SLOT_HTML = '<!--$-->';
|
|
43
20
|
|
|
44
21
|
const STATE_HYDRATING = 0;
|
|
@@ -53,9 +30,7 @@ const STORE = Symbol('template.store');
|
|
|
53
30
|
export {
|
|
54
31
|
ARRAY_SLOT,
|
|
55
32
|
CLEANUP,
|
|
56
|
-
COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, COMPILER_TYPES,
|
|
57
33
|
DIRECT_ATTACH_EVENTS,
|
|
58
34
|
LIFECYCLE_EVENTS,
|
|
59
|
-
PACKAGE,
|
|
60
35
|
SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE,
|
|
61
36
|
};
|
package/test/counter.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { effect, reactive } from '@esportsplus/reactivity'
|
|
2
|
+
import { html, type Attributes } from '@esportsplus/template';
|
|
3
|
+
import { omit } from '@esportsplus/utilities';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const OMIT = ['currency', 'decimals', 'delay', 'max', 'state', 'suffix', 'value'];
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
let formatters: Record<string, Intl.NumberFormat> = {};
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export default (attributes: Attributes & {
|
|
13
|
+
currency?: 'IGNORE' | 'EUR' | 'GBP' | 'USD';
|
|
14
|
+
decimals?: number;
|
|
15
|
+
delay?: number;
|
|
16
|
+
max?: number;
|
|
17
|
+
state?: { value: number },
|
|
18
|
+
suffix?: string;
|
|
19
|
+
value: number;
|
|
20
|
+
}) => {
|
|
21
|
+
let { currency, decimals, delay, max, suffix, value } = attributes,
|
|
22
|
+
api = attributes.state || reactive({ value: -1 }),
|
|
23
|
+
formatter = currency === 'IGNORE'
|
|
24
|
+
? undefined
|
|
25
|
+
: formatters[currency || 'USD'] ??= new Intl.NumberFormat('en-US', {
|
|
26
|
+
style: 'currency',
|
|
27
|
+
currency: currency || 'USD'
|
|
28
|
+
}),
|
|
29
|
+
rendering = true,
|
|
30
|
+
state = reactive({
|
|
31
|
+
length: 0,
|
|
32
|
+
test: () => 'sds',
|
|
33
|
+
render: [] as string[]
|
|
34
|
+
}),
|
|
35
|
+
render = reactive([] as string[]);
|
|
36
|
+
|
|
37
|
+
decimals ??= 2;
|
|
38
|
+
|
|
39
|
+
effect(() => {
|
|
40
|
+
if (api.value !== -1) {
|
|
41
|
+
value = api.value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let padding = (max || value).toFixed(decimals).length - value.toFixed(decimals).length,
|
|
45
|
+
values = value.toString().padStart( value.toString().length + padding, '1') as any;
|
|
46
|
+
|
|
47
|
+
if (formatter) {
|
|
48
|
+
values = formatter.format(values);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
values = Number(values).toLocaleString([], {
|
|
52
|
+
minimumFractionDigits: 0,
|
|
53
|
+
maximumFractionDigits: decimals
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
values = values.split('');
|
|
58
|
+
|
|
59
|
+
if (suffix) {
|
|
60
|
+
values.push(' ', ...suffix.split(''));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
state.length = values.length;
|
|
64
|
+
|
|
65
|
+
for (let i = 0, n = values.length; i < n; i++) {
|
|
66
|
+
let value = values[i];
|
|
67
|
+
|
|
68
|
+
if (!isNaN(parseInt(value, 10)) && (rendering === true || padding > 0)) {
|
|
69
|
+
padding--;
|
|
70
|
+
value = '0';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
render[i] = value;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (rendering === true) {
|
|
77
|
+
rendering = false;
|
|
78
|
+
setTimeout(() => api.value = value, delay || 1000);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return html`
|
|
83
|
+
<div class='counter' ${omit(attributes, OMIT)}>
|
|
84
|
+
${() => {
|
|
85
|
+
let n = state.length;
|
|
86
|
+
|
|
87
|
+
if (n === 0) {
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return html.reactive(render, function (value) {
|
|
92
|
+
if (isNaN(parseInt(value, 10))) {
|
|
93
|
+
return html`
|
|
94
|
+
<span class='counter-character counter-character--symbol'>
|
|
95
|
+
${value}
|
|
96
|
+
</span>
|
|
97
|
+
`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return html`
|
|
101
|
+
<div class=' counter-character'>
|
|
102
|
+
<div class='counter-character-track' style='${`--value: ${value}`}'>
|
|
103
|
+
<span>9</span>
|
|
104
|
+
${[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((value) => html`<span>${value}</span>`)}
|
|
105
|
+
<span>0</span>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
`;
|
|
109
|
+
});
|
|
110
|
+
}}
|
|
111
|
+
</div>
|
|
112
|
+
`;
|
|
113
|
+
};
|
package/test/effects.ts
CHANGED
package/test/events.ts
CHANGED
package/test/imported-values.ts
CHANGED
package/test/nested.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Tests nested html`` calls, array mapping, and conditional templates
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
import { html } from '
|
|
5
|
+
import { html } from '@esportsplus/template';
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
// =============================================================================
|
|
@@ -273,6 +273,25 @@ export const stressList50 = (items: string[]) =>
|
|
|
273
273
|
export const stressList100 = (items: string[]) =>
|
|
274
274
|
html`<ul>${items.slice(0, 100).map(item => html`<li>${item}</li>`)}</ul>`;
|
|
275
275
|
|
|
276
|
+
// Block body arrow function with nested template
|
|
277
|
+
export const blockBodyNested = (items: { name: string; active: boolean }[]) =>
|
|
278
|
+
html`<ul>${items.map((item) => {
|
|
279
|
+
let cls = item.active ? 'active' : 'inactive';
|
|
280
|
+
return html`<li class="${cls}">${item.name}</li>`;
|
|
281
|
+
})}</ul>`;
|
|
282
|
+
|
|
283
|
+
// Block body with conditional
|
|
284
|
+
export const blockBodyConditional = (data: { show: boolean; items: string[] }) =>
|
|
285
|
+
html`<div>${() => {
|
|
286
|
+
if (!data.show) {
|
|
287
|
+
return '';
|
|
288
|
+
}
|
|
289
|
+
return html`<ul>${data.items.map((item) => {
|
|
290
|
+
let processed = item.toUpperCase();
|
|
291
|
+
return html`<li>${processed}</li>`;
|
|
292
|
+
})}</ul>`;
|
|
293
|
+
}}</div>`;
|
|
294
|
+
|
|
276
295
|
// Complex nested structure
|
|
277
296
|
export const stressComplex = (data: {
|
|
278
297
|
header: { title: string; subtitle: string };
|
package/test/slots.ts
CHANGED
package/test/spread.ts
CHANGED
package/test/static.ts
CHANGED
package/test/templates.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Test Templates for Compilation Benchmark
|
|
2
2
|
// Wide variety of template patterns for performance testing
|
|
3
3
|
|
|
4
|
-
import { html } from '
|
|
4
|
+
import { html } from '@esportsplus/template';
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
// =============================================================================
|
package/test/vite.config.ts
CHANGED
|
@@ -8,6 +8,7 @@ export default defineConfig({
|
|
|
8
8
|
build: {
|
|
9
9
|
lib: {
|
|
10
10
|
entry: {
|
|
11
|
+
'counter': resolve(__dirname, 'counter.ts'),
|
|
11
12
|
'constants': resolve(__dirname, 'constants.ts'),
|
|
12
13
|
'effects': resolve(__dirname, 'effects.ts'),
|
|
13
14
|
'events': resolve(__dirname, 'events.ts'),
|
|
@@ -27,7 +28,6 @@ export default defineConfig({
|
|
|
27
28
|
rollupOptions: {
|
|
28
29
|
external: [
|
|
29
30
|
/^~\//,
|
|
30
|
-
/^\.\.\/src/,
|
|
31
31
|
'@esportsplus/reactivity',
|
|
32
32
|
'@esportsplus/template',
|
|
33
33
|
'@esportsplus/utilities'
|
|
@@ -44,6 +44,7 @@ export default defineConfig({
|
|
|
44
44
|
],
|
|
45
45
|
resolve: {
|
|
46
46
|
alias: {
|
|
47
|
+
'@esportsplus/template': resolve(__dirname, '../src'),
|
|
47
48
|
'~': resolve(__dirname, '../src')
|
|
48
49
|
}
|
|
49
50
|
}
|