@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.
Files changed (39) hide show
  1. package/build/attributes.js +1 -2
  2. package/build/constants.d.ts +18 -3
  3. package/build/constants.js +31 -4
  4. package/build/html.d.ts +3 -3
  5. package/build/slot/array.d.ts +2 -2
  6. package/build/slot/array.js +5 -4
  7. package/build/slot/render.js +3 -2
  8. package/build/transformer/codegen.d.ts +2 -8
  9. package/build/transformer/codegen.js +87 -138
  10. package/build/transformer/index.d.ts +2 -3
  11. package/build/transformer/index.js +32 -31
  12. package/build/transformer/parser.d.ts +3 -2
  13. package/build/transformer/parser.js +4 -4
  14. package/build/transformer/plugins/tsc.js +8 -2
  15. package/build/transformer/plugins/vite.js +7 -9
  16. package/build/transformer/ts-parser.d.ts +1 -2
  17. package/build/transformer/ts-parser.js +25 -34
  18. package/build/transformer/type-analyzer.d.ts +4 -5
  19. package/build/transformer/type-analyzer.js +61 -71
  20. package/build/types.d.ts +1 -1
  21. package/package.json +7 -7
  22. package/src/attributes.ts +1 -4
  23. package/src/constants.ts +42 -6
  24. package/src/html.ts +3 -3
  25. package/src/slot/array.ts +9 -6
  26. package/src/slot/render.ts +5 -2
  27. package/src/transformer/codegen.ts +113 -175
  28. package/src/transformer/index.ts +43 -40
  29. package/src/transformer/parser.ts +10 -7
  30. package/src/transformer/plugins/tsc.ts +10 -2
  31. package/src/transformer/plugins/vite.ts +10 -14
  32. package/src/transformer/ts-parser.ts +31 -44
  33. package/src/transformer/type-analyzer.ts +75 -93
  34. package/src/types.ts +1 -1
  35. package/test/vite.config.ts +1 -1
  36. package/build/event/constants.d.ts +0 -3
  37. package/build/event/constants.js +0 -13
  38. package/src/event/constants.ts +0 -16
  39. 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: "slot";
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: "attributes";
15
+ type: COMPILER_TYPES.AttributeSlot;
15
16
  })[] | null;
16
17
  };
17
18
  };
@@ -1,4 +1,4 @@
1
- const SLOT_HTML = '<!--$-->';
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(`@esportsplus/template: attribute metadata could not be found for '${attr}'`);
128
+ throw new Error(`${PACKAGE}: attribute metadata could not be found for '${attr}'`);
129
129
  }
130
- slots.push({ attributes: attrs, path, type: 'attributes' });
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: 'slot' });
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 { createTransformer } from '../index.js';
1
+ import { transformCode } from '../../transformer/index.js';
2
2
  export default (program) => {
3
- return createTransformer(program);
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 sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true), result = transform(sourceFile, program.get(root));
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(`@esportsplus/template: Error transforming ${id}:`, 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
- declare const getTemplateExpressions: (info: TemplateInfo, sourceFile: ts.SourceFile) => string[];
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 === 'html' &&
35
- node.expression.name.text === 'reactive' &&
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 === 'html') {
50
- templates.push(extractTemplateInfo(node, depth));
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
- const getTemplateExpressions = (info, sourceFile) => {
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
- type SlotType = 'array-slot' | 'document-fragment' | 'effect' | 'node' | 'primitive' | 'static' | 'unknown';
3
- declare const analyzeExpression: (expr: ts.Expression, checker?: ts.TypeChecker) => SlotType;
4
- declare const generateAttributeBinding: (elementVar: string, name: string, expr: string, staticValue: string) => string;
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 { getNames } from './codegen.js';
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 getObjectPropertyValue(expr, key, sourceFile) {
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 'effect';
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 === 'html' &&
77
- expr.expression.name.text === 'reactive') {
78
- return 'array-slot';
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 === 'html') {
81
- return 'document-fragment';
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 'array-slot';
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 (expr.kind === ts.SyntaxKind.TrueKeyword || expr.kind === ts.SyntaxKind.FalseKeyword) {
93
- return 'static';
94
- }
95
- if (expr.kind === ts.SyntaxKind.NullKeyword || expr.kind === ts.SyntaxKind.UndefinedKeyword) {
96
- return 'static';
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 'primitive';
78
+ return COMPILER_TYPES.Primitive;
100
79
  }
101
80
  if (ts.isConditionalExpression(expr)) {
102
- let whenFalse = inferSlotType(expr.whenFalse, ctx), whenTrue = inferSlotType(expr.whenTrue, ctx);
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 === 'effect' || whenFalse === 'effect') {
107
- return 'effect';
85
+ if (whenTrue === COMPILER_TYPES.Effect || whenFalse === COMPILER_TYPES.Effect) {
86
+ return COMPILER_TYPES.Effect;
108
87
  }
109
- return 'unknown';
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 'effect';
96
+ return COMPILER_TYPES.Effect;
118
97
  }
119
98
  if (isTypeArray(type, checker)) {
120
- return 'array-slot';
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 'effect';
109
+ return COMPILER_TYPES.Effect;
131
110
  }
132
111
  if (isTypeArray(type, checker)) {
133
- return 'array-slot';
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 'effect';
122
+ return COMPILER_TYPES.Effect;
144
123
  }
145
124
  if (isTypeArray(type, checker)) {
146
- return 'array-slot';
125
+ return COMPILER_TYPES.ArraySlot;
147
126
  }
148
127
  }
149
128
  catch {
150
129
  }
151
130
  }
152
131
  }
153
- return 'unknown';
132
+ return COMPILER_TYPES.Unknown;
154
133
  }
155
134
  function isTypeArray(type, checker) {
156
- let typeStr = checker.typeToString(type);
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
- if (symbol && (symbol.getName() === 'Array' || symbol.getName() === 'ReactiveArray')) {
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 inferSlotType(expr, checker ? { checker } : undefined);
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 `${n.event}.${key}(${elementVar}, ${expr});`;
161
+ return `${ns}.event.${key}(${elementVar}, ${expr});`;
188
162
  }
189
163
  if (DIRECT_ATTACH_EVENTS.has(key)) {
190
- return `${n.event}.direct(${elementVar}, '${event}', ${expr});`;
164
+ return `${ns}.event.direct(${elementVar}, '${event}', ${expr});`;
191
165
  }
192
- return `${n.event}.delegate(${elementVar}, '${event}', ${expr});`;
166
+ return `${ns}.event.delegate(${elementVar}, '${event}', ${expr});`;
193
167
  }
194
168
  if (name === 'class') {
195
- return `${n.attr}.setClass(${elementVar}, '${staticValue}', ${expr});`;
169
+ return `${ns}.attributes.setClass(${elementVar}, '${staticValue}', ${expr});`;
196
170
  }
197
171
  if (name === 'spread') {
198
- return `${n.attr}.spread(${elementVar}, ${expr});`;
172
+ return `${ns}.attributes.spread(${elementVar}, ${expr});`;
199
173
  }
200
174
  if (name === 'style') {
201
- return `${n.attr}.setStyle(${elementVar}, '${staticValue}', ${expr});`;
175
+ return `${ns}.attributes.setStyle(${elementVar}, '${staticValue}', ${expr});`;
202
176
  }
203
- return `${n.attr}.setProperty(${elementVar}, '${name}', ${expr});`;
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 [`${getNames().attr}.spread(${elementVar}, ${exprCode});`];
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 = getObjectPropertyValue(expr, key, sourceFile);
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> = DocumentFragment | ArraySlot<T> | Effect<T> | Node | NodeList | Primitive | 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.2",
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.3",
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
- "import": "./build/transformer/plugins/vite.js",
31
- "types": "./build/transformer/plugins/vite.d.ts"
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.0",
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 { fragment } from './utilities';
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
- const EMPTY_FRAGMENT = fragment('');
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
- EMPTY_FRAGMENT,
25
- SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE
26
- };
27
- export * from './event/constants';
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 { ReactiveArray } from '@esportsplus/reactivity';
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> | ArraySlot<T> | Renderable<T>;
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: ReactiveArray<T>, _template: (value: T) => DocumentFragment): ArraySlot<T> => {
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, set, signal, ReactiveArray } from '@esportsplus/reactivity';
2
- import { ARRAY_SLOT, EMPTY_FRAGMENT } from '~/constants';
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: ReactiveArray<T>, template: ((value: T) => ReturnType<typeof html>)) {
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
- set(this.signal, this.nodes.length);
177
+ write(this.signal, this.nodes.length);
175
178
  });
176
179
  }
177
180
 
@@ -1,10 +1,13 @@
1
1
  import { isArray } from '@esportsplus/utilities';
2
- import { ARRAY_SLOT, EMPTY_FRAGMENT } from '~/constants';
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;