@esportsplus/template 0.16.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/.editorconfig +9 -0
- package/.gitattributes +2 -0
- package/.github/dependabot.yml +25 -0
- package/.github/workflows/bump.yml +9 -0
- package/.github/workflows/dependabot.yml +12 -0
- package/.github/workflows/publish.yml +16 -0
- package/README.md +385 -0
- package/build/attributes.d.ts +5 -0
- package/build/attributes.js +212 -0
- package/build/compiler/codegen.d.ts +21 -0
- package/build/compiler/codegen.js +303 -0
- package/build/compiler/constants.d.ts +16 -0
- package/build/compiler/constants.js +19 -0
- package/build/compiler/index.d.ts +14 -0
- package/build/compiler/index.js +61 -0
- package/build/compiler/parser.d.ts +19 -0
- package/build/compiler/parser.js +164 -0
- package/build/compiler/plugins/tsc.d.ts +3 -0
- package/build/compiler/plugins/tsc.js +4 -0
- package/build/compiler/plugins/vite.d.ts +13 -0
- package/build/compiler/plugins/vite.js +8 -0
- package/build/compiler/ts-analyzer.d.ts +4 -0
- package/build/compiler/ts-analyzer.js +63 -0
- package/build/compiler/ts-parser.d.ts +24 -0
- package/build/compiler/ts-parser.js +67 -0
- package/build/constants.d.ts +12 -0
- package/build/constants.js +25 -0
- package/build/event/index.d.ts +10 -0
- package/build/event/index.js +90 -0
- package/build/event/onconnect.d.ts +3 -0
- package/build/event/onconnect.js +15 -0
- package/build/event/onresize.d.ts +3 -0
- package/build/event/onresize.js +26 -0
- package/build/event/ontick.d.ts +6 -0
- package/build/event/ontick.js +41 -0
- package/build/html.d.ts +9 -0
- package/build/html.js +7 -0
- package/build/index.d.ts +8 -0
- package/build/index.js +12 -0
- package/build/render.d.ts +3 -0
- package/build/render.js +8 -0
- package/build/slot/array.d.ts +25 -0
- package/build/slot/array.js +189 -0
- package/build/slot/cleanup.d.ts +4 -0
- package/build/slot/cleanup.js +23 -0
- package/build/slot/effect.d.ts +12 -0
- package/build/slot/effect.js +85 -0
- package/build/slot/index.d.ts +7 -0
- package/build/slot/index.js +14 -0
- package/build/slot/render.d.ts +2 -0
- package/build/slot/render.js +44 -0
- package/build/svg.d.ts +5 -0
- package/build/svg.js +14 -0
- package/build/types.d.ts +23 -0
- package/build/types.js +1 -0
- package/build/utilities.d.ts +7 -0
- package/build/utilities.js +31 -0
- package/package.json +43 -0
- package/src/attributes.ts +313 -0
- package/src/compiler/codegen.ts +492 -0
- package/src/compiler/constants.ts +25 -0
- package/src/compiler/index.ts +87 -0
- package/src/compiler/parser.ts +242 -0
- package/src/compiler/plugins/tsc.ts +6 -0
- package/src/compiler/plugins/vite.ts +10 -0
- package/src/compiler/ts-analyzer.ts +89 -0
- package/src/compiler/ts-parser.ts +112 -0
- package/src/constants.ts +44 -0
- package/src/event/index.ts +130 -0
- package/src/event/onconnect.ts +22 -0
- package/src/event/onresize.ts +37 -0
- package/src/event/ontick.ts +59 -0
- package/src/html.ts +18 -0
- package/src/index.ts +19 -0
- package/src/llm.txt +403 -0
- package/src/render.ts +13 -0
- package/src/slot/array.ts +257 -0
- package/src/slot/cleanup.ts +37 -0
- package/src/slot/effect.ts +114 -0
- package/src/slot/index.ts +17 -0
- package/src/slot/render.ts +61 -0
- package/src/svg.ts +27 -0
- package/src/types.ts +40 -0
- package/src/utilities.ts +53 -0
- package/storage/compiler-architecture-2026-01-13.md +420 -0
- package/test/dist/test.js +1912 -0
- package/test/dist/test.js.map +1 -0
- package/test/index.ts +648 -0
- package/test/vite.config.ts +23 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { ENTRYPOINT, ENTRYPOINT_REACTIVITY, TYPES } from './constants.js';
|
|
3
|
+
function isTypeFunction(type, checker) {
|
|
4
|
+
if (type.isUnion()) {
|
|
5
|
+
for (let i = 0, n = type.types.length; i < n; i++) {
|
|
6
|
+
if (!isTypeFunction(type.types[i], checker)) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return type.types.length > 0;
|
|
11
|
+
}
|
|
12
|
+
return type.getCallSignatures().length > 0;
|
|
13
|
+
}
|
|
14
|
+
const analyze = (expr, checker) => {
|
|
15
|
+
while (ts.isParenthesizedExpression(expr)) {
|
|
16
|
+
expr = expr.expression;
|
|
17
|
+
}
|
|
18
|
+
if (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr)) {
|
|
19
|
+
return TYPES.Effect;
|
|
20
|
+
}
|
|
21
|
+
if (ts.isCallExpression(expr) &&
|
|
22
|
+
ts.isPropertyAccessExpression(expr.expression) &&
|
|
23
|
+
ts.isIdentifier(expr.expression.expression) &&
|
|
24
|
+
expr.expression.expression.text === ENTRYPOINT &&
|
|
25
|
+
expr.expression.name.text === ENTRYPOINT_REACTIVITY) {
|
|
26
|
+
return TYPES.ArraySlot;
|
|
27
|
+
}
|
|
28
|
+
if (ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === ENTRYPOINT) {
|
|
29
|
+
return TYPES.DocumentFragment;
|
|
30
|
+
}
|
|
31
|
+
if (ts.isNumericLiteral(expr) ||
|
|
32
|
+
ts.isStringLiteral(expr) ||
|
|
33
|
+
ts.isNoSubstitutionTemplateLiteral(expr) ||
|
|
34
|
+
expr.kind === ts.SyntaxKind.TrueKeyword ||
|
|
35
|
+
expr.kind === ts.SyntaxKind.FalseKeyword ||
|
|
36
|
+
expr.kind === ts.SyntaxKind.NullKeyword ||
|
|
37
|
+
expr.kind === ts.SyntaxKind.UndefinedKeyword) {
|
|
38
|
+
return TYPES.Static;
|
|
39
|
+
}
|
|
40
|
+
if (ts.isTemplateExpression(expr)) {
|
|
41
|
+
return TYPES.Primitive;
|
|
42
|
+
}
|
|
43
|
+
if (ts.isConditionalExpression(expr)) {
|
|
44
|
+
let whenFalse = analyze(expr.whenFalse, checker), whenTrue = analyze(expr.whenTrue, checker);
|
|
45
|
+
if (whenTrue === whenFalse) {
|
|
46
|
+
return whenTrue;
|
|
47
|
+
}
|
|
48
|
+
if (whenTrue === TYPES.Effect || whenFalse === TYPES.Effect) {
|
|
49
|
+
return TYPES.Effect;
|
|
50
|
+
}
|
|
51
|
+
return TYPES.Unknown;
|
|
52
|
+
}
|
|
53
|
+
if (checker && (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr) || ts.isCallExpression(expr))) {
|
|
54
|
+
try {
|
|
55
|
+
if (isTypeFunction(checker.getTypeAtLocation(expr), checker)) {
|
|
56
|
+
return TYPES.Effect;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch { }
|
|
60
|
+
}
|
|
61
|
+
return TYPES.Unknown;
|
|
62
|
+
};
|
|
63
|
+
export { analyze };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
type ReactiveCallInfo = {
|
|
3
|
+
arrayArg: ts.Expression;
|
|
4
|
+
callbackArg: ts.Expression;
|
|
5
|
+
end: number;
|
|
6
|
+
node: ts.CallExpression;
|
|
7
|
+
start: number;
|
|
8
|
+
};
|
|
9
|
+
type TemplateInfo = {
|
|
10
|
+
depth: number;
|
|
11
|
+
end: number;
|
|
12
|
+
expressions: ts.Expression[];
|
|
13
|
+
literals: string[];
|
|
14
|
+
node: ts.TaggedTemplateExpression;
|
|
15
|
+
start: number;
|
|
16
|
+
};
|
|
17
|
+
declare const extractTemplateParts: (template: ts.TemplateLiteral) => {
|
|
18
|
+
expressions: ts.Expression[];
|
|
19
|
+
literals: string[];
|
|
20
|
+
};
|
|
21
|
+
declare const findHtmlTemplates: (sourceFile: ts.SourceFile, checker?: ts.TypeChecker) => TemplateInfo[];
|
|
22
|
+
declare const findReactiveCalls: (sourceFile: ts.SourceFile, checker?: ts.TypeChecker) => ReactiveCallInfo[];
|
|
23
|
+
export { extractTemplateParts, findHtmlTemplates, findReactiveCalls };
|
|
24
|
+
export type { ReactiveCallInfo, TemplateInfo };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { imports } from '@esportsplus/typescript/compiler';
|
|
3
|
+
import { ENTRYPOINT, ENTRYPOINT_REACTIVITY, PACKAGE_NAME } from './constants.js';
|
|
4
|
+
function visitReactiveCalls(node, calls, checker) {
|
|
5
|
+
if (ts.isCallExpression(node) &&
|
|
6
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
7
|
+
ts.isIdentifier(node.expression.expression) &&
|
|
8
|
+
node.expression.name.text === ENTRYPOINT_REACTIVITY &&
|
|
9
|
+
node.arguments.length === 2 &&
|
|
10
|
+
node.expression.expression.text === ENTRYPOINT &&
|
|
11
|
+
(!checker || imports.includes(checker, node.expression.expression, PACKAGE_NAME, ENTRYPOINT))) {
|
|
12
|
+
calls.push({
|
|
13
|
+
arrayArg: node.arguments[0],
|
|
14
|
+
callbackArg: node.arguments[1],
|
|
15
|
+
end: node.end,
|
|
16
|
+
node,
|
|
17
|
+
start: node.getStart()
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
ts.forEachChild(node, child => visitReactiveCalls(child, calls, checker));
|
|
21
|
+
}
|
|
22
|
+
function visitTemplates(node, depth, templates, checker) {
|
|
23
|
+
let nextDepth = (ts.isArrowFunction(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isMethodDeclaration(node))
|
|
24
|
+
? depth + 1
|
|
25
|
+
: depth;
|
|
26
|
+
if (ts.isTaggedTemplateExpression(node) &&
|
|
27
|
+
ts.isIdentifier(node.tag) &&
|
|
28
|
+
node.tag.text === ENTRYPOINT &&
|
|
29
|
+
(!checker || imports.includes(checker, node.tag, PACKAGE_NAME, ENTRYPOINT))) {
|
|
30
|
+
let { expressions, literals } = extractTemplateParts(node.template);
|
|
31
|
+
templates.push({
|
|
32
|
+
depth,
|
|
33
|
+
end: node.end,
|
|
34
|
+
expressions,
|
|
35
|
+
literals,
|
|
36
|
+
node,
|
|
37
|
+
start: node.getStart()
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
ts.forEachChild(node, child => visitTemplates(child, nextDepth, templates, checker));
|
|
41
|
+
}
|
|
42
|
+
const extractTemplateParts = (template) => {
|
|
43
|
+
let expressions = [], literals = [];
|
|
44
|
+
if (ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
45
|
+
literals.push(template.text);
|
|
46
|
+
}
|
|
47
|
+
else if (ts.isTemplateExpression(template)) {
|
|
48
|
+
literals.push(template.head.text);
|
|
49
|
+
for (let i = 0, n = template.templateSpans.length; i < n; i++) {
|
|
50
|
+
let span = template.templateSpans[i];
|
|
51
|
+
expressions.push(span.expression);
|
|
52
|
+
literals.push(span.literal.text);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { expressions, literals };
|
|
56
|
+
};
|
|
57
|
+
const findHtmlTemplates = (sourceFile, checker) => {
|
|
58
|
+
let templates = [];
|
|
59
|
+
visitTemplates(sourceFile, 0, templates, checker);
|
|
60
|
+
return templates.sort((a, b) => a.depth !== b.depth ? b.depth - a.depth : a.start - b.start);
|
|
61
|
+
};
|
|
62
|
+
const findReactiveCalls = (sourceFile, checker) => {
|
|
63
|
+
let calls = [];
|
|
64
|
+
visitReactiveCalls(sourceFile, calls, checker);
|
|
65
|
+
return calls;
|
|
66
|
+
};
|
|
67
|
+
export { extractTemplateParts, findHtmlTemplates, findReactiveCalls };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare const ARRAY_SLOT: unique symbol;
|
|
2
|
+
declare const ATTRIBUTE_DELIMITERS: Record<string, string>;
|
|
3
|
+
declare const CLEANUP: unique symbol;
|
|
4
|
+
declare const DIRECT_ATTACH_EVENTS: Set<string>;
|
|
5
|
+
declare const LIFECYCLE_EVENTS: Set<string>;
|
|
6
|
+
declare const PACKAGE_NAME = "@esportsplus/template";
|
|
7
|
+
declare const SLOT_HTML = "<!--$-->";
|
|
8
|
+
declare const STATE_HYDRATING = 0;
|
|
9
|
+
declare const STATE_NONE = 1;
|
|
10
|
+
declare const STATE_WAITING = 2;
|
|
11
|
+
declare const STORE: unique symbol;
|
|
12
|
+
export { ARRAY_SLOT, ATTRIBUTE_DELIMITERS, CLEANUP, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, PACKAGE_NAME, SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE, };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const ARRAY_SLOT = Symbol('template.array.slot');
|
|
2
|
+
const ATTRIBUTE_DELIMITERS = {
|
|
3
|
+
class: ' ',
|
|
4
|
+
style: ';'
|
|
5
|
+
};
|
|
6
|
+
const CLEANUP = Symbol('template.cleanup');
|
|
7
|
+
const DIRECT_ATTACH_EVENTS = new Set([
|
|
8
|
+
'onblur',
|
|
9
|
+
'onerror',
|
|
10
|
+
'onfocus', 'onfocusin', 'onfocusout',
|
|
11
|
+
'onload',
|
|
12
|
+
'onplay', 'onpause', 'onended', 'ontimeupdate',
|
|
13
|
+
'onreset',
|
|
14
|
+
'onscroll', 'onsubmit'
|
|
15
|
+
]);
|
|
16
|
+
const LIFECYCLE_EVENTS = new Set([
|
|
17
|
+
'onconnect', 'ondisconnect', 'onrender', 'onresize', 'ontick'
|
|
18
|
+
]);
|
|
19
|
+
const PACKAGE_NAME = '@esportsplus/template';
|
|
20
|
+
const SLOT_HTML = '<!--$-->';
|
|
21
|
+
const STATE_HYDRATING = 0;
|
|
22
|
+
const STATE_NONE = 1;
|
|
23
|
+
const STATE_WAITING = 2;
|
|
24
|
+
const STORE = Symbol('template.store');
|
|
25
|
+
export { ARRAY_SLOT, ATTRIBUTE_DELIMITERS, CLEANUP, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, PACKAGE_NAME, SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE, };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Attributes, Element } from '../types.js';
|
|
2
|
+
import onconnect from './onconnect.js';
|
|
3
|
+
import onresize from './onresize.js';
|
|
4
|
+
import ontick from './ontick.js';
|
|
5
|
+
declare const delegate: <E extends string>(element: Element, event: E, listener: Attributes[`on${E}`]) => void;
|
|
6
|
+
declare const on: <E extends string>(element: Element, event: E, listener: Attributes[`on${E}`]) => void;
|
|
7
|
+
declare const ondisconnect: (element: Element, listener: NonNullable<Attributes[`ondisconnect`]>) => void;
|
|
8
|
+
declare const onrender: (element: Element, listener: NonNullable<Attributes[`onrender`]>) => void;
|
|
9
|
+
declare const runtime: <E extends `on${string}`>(element: Element, event: E, listener: Attributes[E]) => void;
|
|
10
|
+
export { delegate, on, onconnect, ondisconnect, onrender, onresize, ontick, runtime };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { root } from '@esportsplus/reactivity';
|
|
2
|
+
import { defineProperty } from '@esportsplus/utilities';
|
|
3
|
+
import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../constants.js';
|
|
4
|
+
import { ondisconnect as disconnect } from '../slot/index.js';
|
|
5
|
+
import onconnect from './onconnect.js';
|
|
6
|
+
import onresize from './onresize.js';
|
|
7
|
+
import ontick from './ontick.js';
|
|
8
|
+
let controllers = new Map(), host = window.document, keys = {}, passive = new Set([
|
|
9
|
+
'animationend', 'animationiteration', 'animationstart',
|
|
10
|
+
'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'mousewheel',
|
|
11
|
+
'pointerenter', 'pointerleave', 'pointermove', 'pointerout', 'pointerover',
|
|
12
|
+
'scroll',
|
|
13
|
+
'touchcancel', 'touchend', 'touchleave', 'touchmove', 'touchstart', 'transitionend',
|
|
14
|
+
'wheel'
|
|
15
|
+
]);
|
|
16
|
+
['mousemove', 'mousewheel', 'scroll', 'touchend', 'touchmove', 'touchstart', 'wheel'].map(event => {
|
|
17
|
+
controllers.set(event, null);
|
|
18
|
+
});
|
|
19
|
+
function register(element, event) {
|
|
20
|
+
let controller = controllers.get(event), signal;
|
|
21
|
+
if (controller === null) {
|
|
22
|
+
let { abort, signal } = new AbortController();
|
|
23
|
+
controllers.set(event, controller = {
|
|
24
|
+
abort,
|
|
25
|
+
signal,
|
|
26
|
+
listeners: 0,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (controller) {
|
|
30
|
+
controller.listeners++;
|
|
31
|
+
ondisconnect(element, () => {
|
|
32
|
+
if (--controller.listeners) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
controller.abort();
|
|
36
|
+
controllers.set(event, null);
|
|
37
|
+
});
|
|
38
|
+
signal = controller.signal;
|
|
39
|
+
}
|
|
40
|
+
let key = keys[event] = Symbol();
|
|
41
|
+
host.addEventListener(event, (e) => {
|
|
42
|
+
let fn, node = e.target;
|
|
43
|
+
while (node) {
|
|
44
|
+
fn = node[key];
|
|
45
|
+
if (typeof fn === 'function') {
|
|
46
|
+
defineProperty(e, 'currentTarget', {
|
|
47
|
+
configurable: true,
|
|
48
|
+
get() {
|
|
49
|
+
return node || window.document;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return fn.call(node, e);
|
|
53
|
+
}
|
|
54
|
+
node = node.parentElement;
|
|
55
|
+
}
|
|
56
|
+
}, {
|
|
57
|
+
passive: passive.has(event),
|
|
58
|
+
signal
|
|
59
|
+
});
|
|
60
|
+
return key;
|
|
61
|
+
}
|
|
62
|
+
const delegate = (element, event, listener) => {
|
|
63
|
+
element[keys[event] || register(element, event)] = listener;
|
|
64
|
+
};
|
|
65
|
+
const on = (element, event, listener) => {
|
|
66
|
+
let handler = (e) => listener.call(element, e);
|
|
67
|
+
element.addEventListener(event, handler, {
|
|
68
|
+
passive: passive.has(event)
|
|
69
|
+
});
|
|
70
|
+
ondisconnect(element, () => {
|
|
71
|
+
element.removeEventListener(event, handler);
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const ondisconnect = (element, listener) => {
|
|
75
|
+
disconnect(element, () => listener(element));
|
|
76
|
+
};
|
|
77
|
+
const onrender = (element, listener) => {
|
|
78
|
+
root(() => listener(element));
|
|
79
|
+
};
|
|
80
|
+
const lifecycle = { onconnect, ondisconnect, onrender, onresize, ontick };
|
|
81
|
+
const runtime = (element, event, listener) => {
|
|
82
|
+
let key = event.toLowerCase();
|
|
83
|
+
if (LIFECYCLE_EVENTS.has(key)) {
|
|
84
|
+
lifecycle[key](element, listener);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
(DIRECT_ATTACH_EVENTS.has(key) ? on : delegate)(element, key.slice(2), listener);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
export { delegate, on, onconnect, ondisconnect, onrender, onresize, ontick, runtime };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { root } from '@esportsplus/reactivity';
|
|
2
|
+
import { add, remove } from './ontick.js';
|
|
3
|
+
export default (element, listener) => {
|
|
4
|
+
let fn = () => {
|
|
5
|
+
retry--;
|
|
6
|
+
if (element.isConnected) {
|
|
7
|
+
retry = 0;
|
|
8
|
+
root(() => listener(element));
|
|
9
|
+
}
|
|
10
|
+
if (!retry) {
|
|
11
|
+
remove(fn);
|
|
12
|
+
}
|
|
13
|
+
}, retry = 60;
|
|
14
|
+
add(fn);
|
|
15
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { onCleanup } from '@esportsplus/reactivity';
|
|
2
|
+
let listeners = new Map(), registered = false;
|
|
3
|
+
function onresize() {
|
|
4
|
+
for (let [element, fn] of listeners) {
|
|
5
|
+
if (element.isConnected) {
|
|
6
|
+
fn(element);
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
listeners.delete(element);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
if (listeners.size === 0) {
|
|
13
|
+
window.removeEventListener('resize', onresize);
|
|
14
|
+
registered = false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export default (element, listener) => {
|
|
18
|
+
listeners.set(element, listener);
|
|
19
|
+
onCleanup(() => {
|
|
20
|
+
listeners.delete(element);
|
|
21
|
+
});
|
|
22
|
+
if (!registered) {
|
|
23
|
+
window.addEventListener('resize', onresize);
|
|
24
|
+
registered = true;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Attributes, Element } from '../types.js';
|
|
2
|
+
declare const add: (task: VoidFunction) => void;
|
|
3
|
+
declare const remove: (task: VoidFunction) => void;
|
|
4
|
+
declare const _default: (element: Element, listener: NonNullable<Attributes["ontick"]>) => void;
|
|
5
|
+
export default _default;
|
|
6
|
+
export { add, remove };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { raf } from '../utilities.js';
|
|
2
|
+
let tasks = Object.assign(new Set(), { running: false });
|
|
3
|
+
function tick() {
|
|
4
|
+
if (tasks.size === 0) {
|
|
5
|
+
tasks.running = false;
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
for (let task of tasks) {
|
|
9
|
+
task();
|
|
10
|
+
}
|
|
11
|
+
raf(tick);
|
|
12
|
+
}
|
|
13
|
+
const add = (task) => {
|
|
14
|
+
tasks.add(task);
|
|
15
|
+
if (!tasks.running) {
|
|
16
|
+
tasks.running = true;
|
|
17
|
+
raf(tick);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const remove = (task) => {
|
|
21
|
+
tasks.delete(task);
|
|
22
|
+
};
|
|
23
|
+
export default (element, listener) => {
|
|
24
|
+
let connected = false, fn = () => {
|
|
25
|
+
if (connected === false) {
|
|
26
|
+
if (element.isConnected) {
|
|
27
|
+
connected = true;
|
|
28
|
+
}
|
|
29
|
+
else if (retry--) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!element.isConnected) {
|
|
34
|
+
remove(fn);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
listener(() => remove(fn), element);
|
|
38
|
+
}, retry = 60;
|
|
39
|
+
add(fn);
|
|
40
|
+
};
|
|
41
|
+
export { add, remove };
|
package/build/html.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Reactive } from '@esportsplus/reactivity';
|
|
2
|
+
import { Attribute, Attributes, Renderable } from './types.js';
|
|
3
|
+
import { ArraySlot } from './slot/index.js';
|
|
4
|
+
type Values<T> = ArraySlot<T extends unknown[] ? T : never> | Attribute | Attributes<any> | Renderable<T>;
|
|
5
|
+
declare const html: {
|
|
6
|
+
<T>(_literals: TemplateStringsArray, ..._values: (Values<T> | Values<T>[])[]): DocumentFragment;
|
|
7
|
+
reactive<T>(_arr: Reactive<T[]>, _template: (value: T) => DocumentFragment): ArraySlot<T[]>;
|
|
8
|
+
};
|
|
9
|
+
export default html;
|
package/build/html.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const html = (_literals, ..._values) => {
|
|
2
|
+
throw new Error('html`` templates must be compiled. Ensure vite-plugin is configured.');
|
|
3
|
+
};
|
|
4
|
+
html.reactive = (_arr, _template) => {
|
|
5
|
+
throw new Error('html.reactive() must be compiled. Ensure vite-plugin is configured.');
|
|
6
|
+
};
|
|
7
|
+
export default html;
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './attributes.js';
|
|
2
|
+
export * from './event/index.js';
|
|
3
|
+
export * from './utilities.js';
|
|
4
|
+
export { default as html } from './html.js';
|
|
5
|
+
export { default as render } from './render.js';
|
|
6
|
+
export { default as slot, ArraySlot, EffectSlot } from './slot/index.js';
|
|
7
|
+
export { default as svg } from './svg.js';
|
|
8
|
+
export type { Attributes, Element, Renderable } from './types.js';
|
package/build/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CLEANUP, STORE } from './constants.js';
|
|
2
|
+
if (typeof Node !== 'undefined') {
|
|
3
|
+
Node.prototype[CLEANUP] = null;
|
|
4
|
+
Node.prototype[STORE] = null;
|
|
5
|
+
}
|
|
6
|
+
export * from './attributes.js';
|
|
7
|
+
export * from './event/index.js';
|
|
8
|
+
export * from './utilities.js';
|
|
9
|
+
export { default as html } from './html.js';
|
|
10
|
+
export { default as render } from './render.js';
|
|
11
|
+
export { default as slot, ArraySlot, EffectSlot } from './slot/index.js';
|
|
12
|
+
export { default as svg } from './svg.js';
|
package/build/render.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Reactive } from '@esportsplus/reactivity';
|
|
2
|
+
import html from '../html.js';
|
|
3
|
+
declare class ArraySlot<T> {
|
|
4
|
+
private array;
|
|
5
|
+
private marker;
|
|
6
|
+
private nodes;
|
|
7
|
+
private queue;
|
|
8
|
+
private scheduled;
|
|
9
|
+
private signal;
|
|
10
|
+
private template;
|
|
11
|
+
readonly fragment: DocumentFragment;
|
|
12
|
+
constructor(array: Reactive<T[]>, template: ((value: Reactive<T[]>[number]) => ReturnType<typeof html>));
|
|
13
|
+
private anchor;
|
|
14
|
+
private clear;
|
|
15
|
+
private pop;
|
|
16
|
+
private push;
|
|
17
|
+
private schedule;
|
|
18
|
+
private shift;
|
|
19
|
+
private sort;
|
|
20
|
+
private splice;
|
|
21
|
+
private sync;
|
|
22
|
+
private unshift;
|
|
23
|
+
get length(): number;
|
|
24
|
+
}
|
|
25
|
+
export { ArraySlot };
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { read, root, signal, write } from '@esportsplus/reactivity';
|
|
2
|
+
import { ARRAY_SLOT } from '../constants.js';
|
|
3
|
+
import { clone, fragment, marker, raf } from '../utilities.js';
|
|
4
|
+
import { ondisconnect, remove } from './cleanup.js';
|
|
5
|
+
const EMPTY_FRAGMENT = fragment('');
|
|
6
|
+
class ArraySlot {
|
|
7
|
+
array;
|
|
8
|
+
marker;
|
|
9
|
+
nodes = [];
|
|
10
|
+
queue = [];
|
|
11
|
+
scheduled = false;
|
|
12
|
+
signal;
|
|
13
|
+
template;
|
|
14
|
+
fragment;
|
|
15
|
+
constructor(array, template) {
|
|
16
|
+
this.array = array;
|
|
17
|
+
let fragment = this.fragment = clone(EMPTY_FRAGMENT);
|
|
18
|
+
this.marker = marker.cloneNode();
|
|
19
|
+
this.signal = signal(array.length);
|
|
20
|
+
this.template = function (data) {
|
|
21
|
+
let dispose, frag = root((d) => {
|
|
22
|
+
dispose = d;
|
|
23
|
+
return template(data);
|
|
24
|
+
}), group = {
|
|
25
|
+
head: frag.firstChild,
|
|
26
|
+
tail: frag.lastChild
|
|
27
|
+
};
|
|
28
|
+
fragment.append(frag);
|
|
29
|
+
ondisconnect(group.head, dispose);
|
|
30
|
+
return group;
|
|
31
|
+
};
|
|
32
|
+
fragment.append(this.marker);
|
|
33
|
+
if (array.length) {
|
|
34
|
+
root(() => {
|
|
35
|
+
this.nodes = array.map(this.template);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
array.on('clear', () => {
|
|
39
|
+
this.queue.length = 0;
|
|
40
|
+
this.schedule({ op: 'clear' });
|
|
41
|
+
});
|
|
42
|
+
array.on('concat', ({ items }) => {
|
|
43
|
+
this.schedule({ items, op: 'concat' });
|
|
44
|
+
});
|
|
45
|
+
array.on('pop', () => {
|
|
46
|
+
this.schedule({ op: 'pop' });
|
|
47
|
+
});
|
|
48
|
+
array.on('push', ({ items }) => {
|
|
49
|
+
this.schedule({ items, op: 'push' });
|
|
50
|
+
});
|
|
51
|
+
array.on('reverse', () => {
|
|
52
|
+
this.schedule({ op: 'reverse' });
|
|
53
|
+
});
|
|
54
|
+
array.on('shift', () => {
|
|
55
|
+
this.schedule({ op: 'shift' });
|
|
56
|
+
});
|
|
57
|
+
array.on('sort', ({ order }) => {
|
|
58
|
+
this.schedule({ op: 'sort', order });
|
|
59
|
+
});
|
|
60
|
+
array.on('splice', ({ deleteCount, items, start }) => {
|
|
61
|
+
this.schedule({ deleteCount, items, op: 'splice', start });
|
|
62
|
+
});
|
|
63
|
+
array.on('unshift', ({ items }) => {
|
|
64
|
+
this.schedule({ items, op: 'unshift' });
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
anchor(index = this.nodes.length - 1) {
|
|
68
|
+
let node = this.nodes[index];
|
|
69
|
+
if (node) {
|
|
70
|
+
return node.tail || node.head;
|
|
71
|
+
}
|
|
72
|
+
return this.marker;
|
|
73
|
+
}
|
|
74
|
+
clear() {
|
|
75
|
+
remove(...this.nodes.splice(0));
|
|
76
|
+
}
|
|
77
|
+
pop() {
|
|
78
|
+
let group = this.nodes.pop();
|
|
79
|
+
if (group) {
|
|
80
|
+
remove(group);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
push(items) {
|
|
84
|
+
let anchor = this.anchor();
|
|
85
|
+
this.nodes.push(...items.map(this.template));
|
|
86
|
+
anchor.after(this.fragment);
|
|
87
|
+
}
|
|
88
|
+
schedule(op) {
|
|
89
|
+
this.queue.push(op);
|
|
90
|
+
if (this.scheduled) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.scheduled = true;
|
|
94
|
+
raf(() => {
|
|
95
|
+
let queue = this.queue;
|
|
96
|
+
this.queue = [];
|
|
97
|
+
this.scheduled = false;
|
|
98
|
+
root(() => {
|
|
99
|
+
for (let i = 0, n = queue.length; i < n; i++) {
|
|
100
|
+
let op = queue[i];
|
|
101
|
+
switch (op.op) {
|
|
102
|
+
case 'clear':
|
|
103
|
+
this.clear();
|
|
104
|
+
break;
|
|
105
|
+
case 'concat':
|
|
106
|
+
this.push(op.items);
|
|
107
|
+
break;
|
|
108
|
+
case 'pop':
|
|
109
|
+
this.pop();
|
|
110
|
+
break;
|
|
111
|
+
case 'push':
|
|
112
|
+
this.push(op.items);
|
|
113
|
+
break;
|
|
114
|
+
case 'reverse':
|
|
115
|
+
this.nodes.reverse();
|
|
116
|
+
this.sync();
|
|
117
|
+
break;
|
|
118
|
+
case 'shift':
|
|
119
|
+
this.shift();
|
|
120
|
+
break;
|
|
121
|
+
case 'sort':
|
|
122
|
+
this.sort(op.order);
|
|
123
|
+
break;
|
|
124
|
+
case 'splice':
|
|
125
|
+
this.splice(op.start, op.deleteCount, op.items);
|
|
126
|
+
break;
|
|
127
|
+
case 'unshift':
|
|
128
|
+
this.unshift(op.items);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
write(this.signal, this.nodes.length);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
shift() {
|
|
137
|
+
let group = this.nodes.shift();
|
|
138
|
+
if (group) {
|
|
139
|
+
remove(group);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
sort(order) {
|
|
143
|
+
let nodes = this.nodes, n = nodes.length;
|
|
144
|
+
if (n !== order.length) {
|
|
145
|
+
remove(...nodes.splice(0));
|
|
146
|
+
this.nodes = this.array.map(this.template);
|
|
147
|
+
this.marker.after(this.fragment);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
let sorted = new Array(n);
|
|
151
|
+
for (let i = 0; i < n; i++) {
|
|
152
|
+
sorted[i] = nodes[order[i]];
|
|
153
|
+
}
|
|
154
|
+
this.nodes = sorted;
|
|
155
|
+
this.sync();
|
|
156
|
+
}
|
|
157
|
+
splice(start, stop = this.nodes.length, items) {
|
|
158
|
+
if (!items.length) {
|
|
159
|
+
remove(...this.nodes.splice(start, stop));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
remove(...this.nodes.splice(start, stop, ...items.map(this.template)));
|
|
163
|
+
this.anchor(start - 1).after(this.fragment);
|
|
164
|
+
}
|
|
165
|
+
sync() {
|
|
166
|
+
let nodes = this.nodes, n = nodes.length;
|
|
167
|
+
if (!n) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
for (let i = 0; i < n; i++) {
|
|
171
|
+
let group = nodes[i], next, node = group.head;
|
|
172
|
+
while (node) {
|
|
173
|
+
next = node === group.tail ? null : node.nextSibling;
|
|
174
|
+
this.fragment.append(node);
|
|
175
|
+
node = next;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
this.marker.after(this.fragment);
|
|
179
|
+
}
|
|
180
|
+
unshift(items) {
|
|
181
|
+
this.nodes.unshift(...items.map(this.template));
|
|
182
|
+
this.marker.after(this.fragment);
|
|
183
|
+
}
|
|
184
|
+
get length() {
|
|
185
|
+
return read(this.signal);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
Object.defineProperty(ArraySlot.prototype, ARRAY_SLOT, { value: true });
|
|
189
|
+
export { ArraySlot };
|