@esportsplus/template 0.34.1 → 0.35.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.
@@ -4,68 +4,10 @@ import {
4
4
  COMPILER_TYPES,
5
5
  DIRECT_ATTACH_EVENTS,
6
6
  LIFECYCLE_EVENTS
7
- } from '../constants';
7
+ } from '~/constants';
8
8
  import { ts } from '@esportsplus/typescript';
9
9
 
10
10
 
11
- type SpreadAnalysis = {
12
- canUnpack: boolean;
13
- keys: string[];
14
- };
15
-
16
-
17
- function analyzeSpread(expr: ts.Expression, checker?: ts.TypeChecker): SpreadAnalysis {
18
- while (ts.isParenthesizedExpression(expr)) {
19
- expr = expr.expression;
20
- }
21
-
22
- if (ts.isObjectLiteralExpression(expr)) {
23
- let keys: string[] = [];
24
-
25
- for (let i = 0, n = expr.properties.length; i < n; i++) {
26
- let prop = expr.properties[i];
27
-
28
- if (ts.isPropertyAssignment(prop)) {
29
- if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) {
30
- keys.push(prop.name.text);
31
- }
32
- }
33
- else if (ts.isShorthandPropertyAssignment(prop)) {
34
- keys.push(prop.name.text);
35
- }
36
- else if (ts.isSpreadAssignment(prop)) {
37
- return { canUnpack: false, keys: [] };
38
- }
39
- }
40
-
41
- return { canUnpack: true, keys };
42
- }
43
-
44
- if (checker && (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr))) {
45
- try {
46
- let keys: string[] = [],
47
- props = checker.getTypeAtLocation(expr).getProperties();
48
-
49
- for (let i = 0, n = props.length; i < n; i++) {
50
- let name = props[i].getName();
51
-
52
- if (name.startsWith('__') || name.startsWith('[')) {
53
- continue;
54
- }
55
-
56
- keys.push(name);
57
- }
58
-
59
- if (keys.length > 0) {
60
- return { canUnpack: true, keys };
61
- }
62
- }
63
- catch { }
64
- }
65
-
66
- return { canUnpack: false, keys: [] };
67
- }
68
-
69
11
  function inferCOMPILER_TYPES(expr: ts.Expression, checker?: ts.TypeChecker): COMPILER_TYPES {
70
12
  while (ts.isParenthesizedExpression(expr)) {
71
13
  expr = expr.expression;
@@ -75,6 +17,7 @@ function inferCOMPILER_TYPES(expr: ts.Expression, checker?: ts.TypeChecker): COM
75
17
  return COMPILER_TYPES.Effect;
76
18
  }
77
19
 
20
+ // Only html.reactive() calls become ArraySlot - handled by generateReactiveInlining
78
21
  if (
79
22
  ts.isCallExpression(expr) &&
80
23
  ts.isPropertyAccessExpression(expr.expression) &&
@@ -89,10 +32,6 @@ function inferCOMPILER_TYPES(expr: ts.Expression, checker?: ts.TypeChecker): COM
89
32
  return COMPILER_TYPES.DocumentFragment;
90
33
  }
91
34
 
92
- if (ts.isArrayLiteralExpression(expr)) {
93
- return COMPILER_TYPES.ArraySlot;
94
- }
95
-
96
35
  if (
97
36
  ts.isNumericLiteral(expr) ||
98
37
  ts.isStringLiteral(expr) ||
@@ -131,10 +70,6 @@ function inferCOMPILER_TYPES(expr: ts.Expression, checker?: ts.TypeChecker): COM
131
70
  if (isTypeFunction(type, checker)) {
132
71
  return COMPILER_TYPES.Effect;
133
72
  }
134
-
135
- if (isTypeArray(type, checker)) {
136
- return COMPILER_TYPES.ArraySlot;
137
- }
138
73
  }
139
74
  catch {
140
75
  }
@@ -143,28 +78,26 @@ function inferCOMPILER_TYPES(expr: ts.Expression, checker?: ts.TypeChecker): COM
143
78
  return COMPILER_TYPES.Unknown;
144
79
  }
145
80
 
146
- function isTypeArray(type: ts.Type, checker: ts.TypeChecker): boolean {
147
- if (checker.isArrayType(type)) {
148
- return true;
149
- }
150
-
151
- return type.getSymbol()?.getName() === 'ReactiveArray';
152
- }
153
-
154
81
  function isTypeFunction(type: ts.Type, checker: ts.TypeChecker): boolean {
155
- if (type.getCallSignatures().length > 0) {
156
- return true;
157
- }
158
-
82
+ // Union types that mix functions with non-functions (e.g., Renderable)
83
+ // should fall through to runtime slot dispatch
159
84
  if (type.isUnion()) {
85
+ let allFunctions = true,
86
+ hasFunction = false;
87
+
160
88
  for (let i = 0, n = type.types.length; i < n; i++) {
161
89
  if (isTypeFunction(type.types[i], checker)) {
162
- return true;
90
+ hasFunction = true;
91
+ }
92
+ else {
93
+ allFunctions = false;
163
94
  }
164
95
  }
96
+
97
+ return hasFunction && allFunctions;
165
98
  }
166
99
 
167
- return false;
100
+ return type.getCallSignatures().length > 0;
168
101
  }
169
102
 
170
103
 
@@ -172,94 +105,43 @@ const analyzeExpression = (expr: ts.Expression, checker?: ts.TypeChecker): COMPI
172
105
  return inferCOMPILER_TYPES(expr, checker);
173
106
  };
174
107
 
175
- const generateAttributeBinding = (elementVar: string, name: string, expr: string, staticValue: string, ns: string): string => {
108
+ const generateAttributeBinding = (elementVar: string, name: string, expr: string, staticValue: string, addImport: (name: string) => string): string => {
176
109
  if (name.startsWith('on') && name.length > 2) {
177
110
  let event = name.slice(2).toLowerCase(),
178
111
  key = name.toLowerCase();
179
112
 
180
113
  if (LIFECYCLE_EVENTS.has(key)) {
181
- return `${ns}.${key}(${elementVar}, ${expr});`;
114
+ return `${addImport(key)}(${elementVar}, ${expr});`;
182
115
  }
183
116
 
184
117
  if (DIRECT_ATTACH_EVENTS.has(key)) {
185
- return `${ns}.on(${elementVar}, '${event}', ${expr});`;
118
+ return `${addImport('on')}(${elementVar}, '${event}', ${expr});`;
186
119
  }
187
120
 
188
- return `${ns}.delegate(${elementVar}, '${event}', ${expr});`;
121
+ return `${addImport('delegate')}(${elementVar}, '${event}', ${expr});`;
189
122
  }
190
123
 
191
124
  if (name === 'class') {
192
- return `${ns}.setClass(${elementVar}, '${staticValue}', ${expr});`;
125
+ return `${addImport('setClass')}(${elementVar}, '${staticValue}', ${expr});`;
193
126
  }
194
127
 
195
128
  if (name === COMPILER_TYPES.Attributes) {
196
- return `${ns}.setProperties(${elementVar}, ${expr});`;
129
+ return `${addImport('setProperties')}(${elementVar}, ${expr});`;
197
130
  }
198
131
 
199
132
  if (name === 'style') {
200
- return `${ns}.setStyle(${elementVar}, '${staticValue}', ${expr});`;
133
+ return `${addImport('setStyle')}(${elementVar}, '${staticValue}', ${expr});`;
201
134
  }
202
135
 
203
- return `${ns}.setProperty(${elementVar}, '${name}', ${expr});`;
136
+ return `${addImport('setProperty')}(${elementVar}, '${name}', ${expr});`;
204
137
  };
205
138
 
206
139
  const generateSpreadBindings = (
207
- expr: ts.Expression,
208
140
  exprCode: string,
209
141
  elementVar: string,
210
- checker: ts.TypeChecker | undefined,
211
- ns: string
142
+ addImport: (name: string) => string
212
143
  ): string[] => {
213
- while (ts.isParenthesizedExpression(expr)) {
214
- expr = expr.expression;
215
- }
216
-
217
- let analysis = analyzeSpread(expr, checker);
218
-
219
- if (!analysis.canUnpack) {
220
- return [`${ns}.setProperties(${elementVar}, ${exprCode});`];
221
- }
222
-
223
- let lines: string[] = [];
224
-
225
- if (ts.isObjectLiteralExpression(expr)) {
226
- for (let i = 0, n = analysis.keys.length; i < n; i++) {
227
- let key = analysis.keys[i],
228
- value: string | null = null;
229
-
230
- for (let j = 0, m = expr.properties.length; j < m; j++) {
231
- let prop = expr.properties[j];
232
-
233
- if (ts.isPropertyAssignment(prop)) {
234
- let text = ts.isIdentifier(prop.name)
235
- ? prop.name.text
236
- : ts.isStringLiteral(prop.name) ? prop.name.text : null;
237
-
238
- if (text === key) {
239
- value = prop.initializer.getText();
240
- break;
241
- }
242
- }
243
- else if (ts.isShorthandPropertyAssignment(prop) && prop.name.text === key) {
244
- value = prop.name.text;
245
- break;
246
- }
247
- }
248
-
249
- if (value !== null) {
250
- lines.push(generateAttributeBinding(elementVar, key, value, '', ns));
251
- }
252
- }
253
- }
254
- else {
255
- for (let i = 0, n = analysis.keys.length; i < n; i++) {
256
- let key = analysis.keys[i];
257
-
258
- lines.push(generateAttributeBinding(elementVar, key, `${exprCode}.${key}`, '', ns));
259
- }
260
- }
261
-
262
- return lines;
144
+ return [`${addImport('setProperties')}(${elementVar}, ${exprCode});`];
263
145
  };
264
146
 
265
147
 
package/src/constants.ts CHANGED
@@ -1,17 +1,11 @@
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
-
9
5
  const COMPILER_ENTRYPOINT = 'html';
10
6
 
11
7
  const COMPILER_ENTRYPOINT_REACTIVITY = 'reactive';
12
8
 
13
- const COMPILER_NAMESPACE = uid('template');
14
-
15
9
  const enum COMPILER_TYPES {
16
10
  ArraySlot = 'array-slot',
17
11
  Attributes = 'attributes',
@@ -40,9 +34,6 @@ const LIFECYCLE_EVENTS = new Set<string>([
40
34
 
41
35
  const PACKAGE = '@esportsplus/template';
42
36
 
43
- const PACKAGE_COMPILER = '@esportsplus/template/compiler';
44
-
45
-
46
37
  const SLOT_HTML = '<!--$-->';
47
38
 
48
39
  const STATE_HYDRATING = 0;
@@ -57,9 +48,9 @@ const STORE = Symbol('template.store');
57
48
  export {
58
49
  ARRAY_SLOT,
59
50
  CLEANUP,
60
- COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, COMPILER_TYPES,
51
+ COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_TYPES,
61
52
  DIRECT_ATTACH_EVENTS,
62
53
  LIFECYCLE_EVENTS,
63
- PACKAGE, PACKAGE_COMPILER,
54
+ PACKAGE,
64
55
  SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE,
65
- };
56
+ };
package/src/index.ts CHANGED
@@ -1,5 +1,20 @@
1
- import './runtime';
1
+ import { CLEANUP, STORE } from './constants';
2
+
3
+
4
+ // Pre-allocate on Node prototype to optimize property access
5
+ if (typeof Node !== 'undefined') {
6
+ (Node.prototype as any)[CLEANUP] = null;
7
+ (Node.prototype as any)[STORE] = null;
8
+ }
9
+
10
+
2
11
  export { default as html } from './html';
3
12
  export { default as render } from './render';
4
13
  export { default as svg } from './svg';
14
+ export * from './attributes';
15
+ export * from './event';
16
+ export { ArraySlot } from './slot/array';
17
+ export { EffectSlot } from './slot/effect';
18
+ export { default as slot } from './slot';
5
19
  export type { Attributes, Element, Renderable } from './types';
20
+ export * from './utilities';
package/src/utilities.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { SLOT_HTML } from './constants';
2
2
 
3
3
 
4
- let tmpl = document?.createElement('template'),
5
- txt = document?.createTextNode('');
4
+ let tmpl = document.createElement('template'),
5
+ txt = document.createTextNode('');
6
6
 
7
7
 
8
8
  // Firefox's importNode outperforms cloneNode in certain scenarios
@@ -1,7 +1,7 @@
1
1
  import { defineConfig } from 'vite';
2
2
  import { resolve } from 'path';
3
3
  import tsconfigPaths from 'vite-tsconfig-paths';
4
- import templatePlugin from '../src/compiler/vite';
4
+ import templatePlugin from '../src/compiler/plugins/vite';
5
5
 
6
6
 
7
7
  export default defineConfig({
@@ -1 +0,0 @@
1
- export {};
package/build/runtime.js DELETED
@@ -1,5 +0,0 @@
1
- import { CLEANUP, STORE } from './constants.js';
2
- if (typeof Node !== 'undefined') {
3
- Node.prototype[CLEANUP] = null;
4
- Node.prototype[STORE] = null;
5
- }
package/src/runtime.ts DELETED
@@ -1,8 +0,0 @@
1
- import { CLEANUP, STORE } from './constants';
2
-
3
-
4
- // Pre-allocate on Node prototype to optimize property access
5
- if (typeof Node !== 'undefined') {
6
- (Node.prototype as any)[CLEANUP] = null;
7
- (Node.prototype as any)[STORE] = null;
8
- }