@thomas-siegfried/tapout 0.0.1 → 0.0.2

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 (47) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1205 -1205
  3. package/package.json +55 -55
  4. package/src/applyBindings.ts +372 -372
  5. package/src/arrayToDomMapping.ts +300 -300
  6. package/src/attributeInterpolationMarkup.ts +91 -91
  7. package/src/bindingContext.ts +207 -207
  8. package/src/bindingEvent.ts +198 -198
  9. package/src/bindingProvider.ts +260 -260
  10. package/src/bindings.ts +963 -963
  11. package/src/compareArrays.ts +122 -122
  12. package/src/componentBinding.ts +318 -318
  13. package/src/componentDecorator.ts +62 -62
  14. package/src/components.ts +367 -367
  15. package/src/computed.ts +439 -439
  16. package/src/configure.ts +52 -52
  17. package/src/controlFlowBindings.ts +206 -206
  18. package/src/core.ts +60 -60
  19. package/src/decorators.ts +407 -407
  20. package/src/dependencyDetection.ts +76 -76
  21. package/src/disposable.ts +36 -36
  22. package/src/domData.ts +42 -42
  23. package/src/domNodeDisposal.ts +94 -94
  24. package/src/effects.ts +29 -29
  25. package/src/event.ts +173 -173
  26. package/src/expressionRewriting.ts +219 -219
  27. package/src/extenders.ts +102 -102
  28. package/src/filters.ts +91 -91
  29. package/src/index.ts +150 -150
  30. package/src/interpolationMarkup.ts +130 -130
  31. package/src/memoization.ts +71 -71
  32. package/src/namespacedBindings.ts +132 -132
  33. package/src/observable.ts +48 -48
  34. package/src/observableArray.ts +397 -397
  35. package/src/options.ts +25 -25
  36. package/src/selectExtensions.ts +68 -68
  37. package/src/slotBinding.ts +84 -84
  38. package/src/subscribable.ts +266 -266
  39. package/src/tasks.ts +79 -79
  40. package/src/templateEngine.ts +108 -108
  41. package/src/templateRendering.ts +399 -399
  42. package/src/templateRewriting.ts +94 -94
  43. package/src/templateSources.ts +134 -134
  44. package/src/utils.ts +123 -123
  45. package/src/utilsDom.ts +87 -87
  46. package/src/virtualElements.ts +153 -153
  47. package/src/wireParams.ts +49 -49
@@ -1,260 +1,260 @@
1
- import type { BindingContext } from './bindingContext.js';
2
- import type { AllBindingsAccessor } from './expressionRewriting.js';
3
- export type { AllBindingsAccessor } from './expressionRewriting.js';
4
- import { preProcessBindings } from './expressionRewriting.js';
5
- import { hasBindingValue, virtualNodeBindingValue } from './virtualElements.js';
6
- import { unwrapObservable } from './utils.js';
7
-
8
- const DATA_BIND_ATTR = 'data-bind';
9
-
10
- // ---- Binding Handler Interface ----
11
-
12
- export interface BindingHandler {
13
- init?(
14
- node: Node,
15
- valueAccessor: () => unknown,
16
- allBindings: AllBindingsAccessor,
17
- viewModel: unknown,
18
- bindingContext: BindingContext,
19
- ): void | { controlsDescendantBindings: boolean };
20
-
21
- update?(
22
- node: Node,
23
- valueAccessor: () => unknown,
24
- allBindings: AllBindingsAccessor,
25
- viewModel: unknown,
26
- bindingContext: BindingContext,
27
- ): void;
28
-
29
- preprocess?(
30
- value: string,
31
- key: string,
32
- addBinding: (key: string, val: string) => void,
33
- ): string | void;
34
-
35
- after?: string[];
36
-
37
- getNamespacedHandler?(name: string, namespace: string, namespacedName: string): BindingHandler;
38
- }
39
-
40
- // ---- Binding Handler Registry ----
41
-
42
- export const bindingHandlers: Record<string, BindingHandler> = {};
43
-
44
- let _getBindingHandlerImpl = (key: string): BindingHandler | undefined => bindingHandlers[key];
45
-
46
- export function getBindingHandler(key: string): BindingHandler | undefined {
47
- return _getBindingHandlerImpl(key);
48
- }
49
-
50
- // ---- Preprocessor Infrastructure ----
51
-
52
- export type PreprocessFn = (
53
- value: string,
54
- key: string,
55
- addBinding: (key: string, val: string) => void,
56
- ) => string | void;
57
-
58
- export type NodePreprocessFn = (node: Node) => Node[] | void;
59
-
60
- function getOrCreateHandler(bindingKeyOrHandler: string | BindingHandler): BindingHandler {
61
- if (typeof bindingKeyOrHandler === 'object') return bindingKeyOrHandler;
62
- return getBindingHandler(bindingKeyOrHandler) || (bindingHandlers[bindingKeyOrHandler] = {});
63
- }
64
-
65
- export function chainPreprocessor(
66
- handler: BindingHandler,
67
- fn: PreprocessFn,
68
- ): BindingHandler {
69
- const previous = handler.preprocess;
70
- if (previous) {
71
- handler.preprocess = function (value, key, addBinding) {
72
- const result = previous(value, key, addBinding);
73
- if (result !== undefined) {
74
- return fn(result, key, addBinding);
75
- }
76
- };
77
- } else {
78
- handler.preprocess = fn;
79
- }
80
- return handler;
81
- }
82
-
83
- export function addBindingPreprocessor(
84
- bindingKeyOrHandler: string | BindingHandler,
85
- preprocessFn: PreprocessFn,
86
- ): BindingHandler {
87
- return chainPreprocessor(getOrCreateHandler(bindingKeyOrHandler), preprocessFn);
88
- }
89
-
90
- export function addNodePreprocessor(preprocessFn: NodePreprocessFn): void {
91
- const provider = instance;
92
- const previous = provider.preprocessNode;
93
- if (previous) {
94
- provider.preprocessNode = function (node: Node): Node[] | void {
95
- const newNodes = previous.call(this, node);
96
- if (!newNodes) {
97
- return preprocessFn.call(this, node);
98
- }
99
- return newNodes;
100
- };
101
- } else {
102
- provider.preprocessNode = preprocessFn;
103
- }
104
- }
105
-
106
- export function addBindingHandlerCreator(
107
- matchRegex: RegExp,
108
- callbackFn: (match: RegExpMatchArray, bindingKey: string) => BindingHandler | undefined,
109
- ): void {
110
- const previousImpl = _getBindingHandlerImpl;
111
- _getBindingHandlerImpl = (key) => {
112
- const existing = previousImpl(key);
113
- if (existing) return existing;
114
- const match = key.match(matchRegex);
115
- return match ? callbackFn(match, key) : undefined;
116
- };
117
- }
118
-
119
- // ---- Filters Registry (injected by filters module) ----
120
-
121
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
122
- let _filters: Record<string, Function> = {};
123
-
124
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
125
- export function setFiltersRegistry(filters: Record<string, Function>): void {
126
- _filters = filters;
127
- }
128
-
129
- // ---- Compiled Binding Function Cache ----
130
-
131
- type BindingEvaluator = (context: BindingContext, element: Node) => Record<string, unknown> | null;
132
-
133
- function createBindingsStringEvaluator(
134
- bindingsString: string,
135
- options?: { valueAccessors?: boolean },
136
- ): BindingEvaluator {
137
- const rewrittenBindings = preProcessBindings(bindingsString, {
138
- valueAccessors: options?.valueAccessors,
139
- getBindingHandler,
140
- });
141
- const functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
142
- // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
143
- const innerFn = new Function("$context", "$element", "$unwrap", "$filters", functionBody) as
144
- (context: BindingContext, element: Node, unwrap: typeof unwrapObservable, filters: Record<string, Function>) => Record<string, unknown> | null;
145
- return (context, element) => innerFn(context, element, unwrapObservable, _filters);
146
- }
147
-
148
- function createBindingsStringEvaluatorViaCache(
149
- bindingsString: string,
150
- cache: Record<string, BindingEvaluator>,
151
- options?: { valueAccessors?: boolean },
152
- ): BindingEvaluator {
153
- const cacheKey = bindingsString + (options?.valueAccessors ? 'true' : '');
154
- return cache[cacheKey] || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString, options));
155
- }
156
-
157
- // ---- Custom Element Hook (set by componentBinding.ts to avoid circular imports) ----
158
-
159
- export type GetComponentNameForNodeFn = (node: Node) => string | undefined;
160
- export type AddBindingsForCustomElementFn = (
161
- allBindings: Record<string, unknown> | null,
162
- node: Node,
163
- bindingContext: BindingContext,
164
- valueAccessors?: boolean,
165
- ) => Record<string, unknown> | null;
166
-
167
- let _getComponentNameForNode: GetComponentNameForNodeFn | undefined;
168
- let _addBindingsForCustomElement: AddBindingsForCustomElementFn | undefined;
169
-
170
- export function setCustomElementHooks(
171
- getComponentNameForNode: GetComponentNameForNodeFn,
172
- addBindingsForCustomElement: AddBindingsForCustomElementFn,
173
- ): void {
174
- _getComponentNameForNode = getComponentNameForNode;
175
- _addBindingsForCustomElement = addBindingsForCustomElement;
176
- }
177
-
178
- // ---- Binding Provider ----
179
-
180
- export class BindingProvider {
181
- bindingCache: Record<string, BindingEvaluator> = {};
182
- preprocessNode?: NodePreprocessFn;
183
-
184
- nodeHasBindings(node: Node): boolean {
185
- switch (node.nodeType) {
186
- case 1: // Element
187
- return (node as Element).getAttribute(DATA_BIND_ATTR) != null
188
- || !!(_getComponentNameForNode && _getComponentNameForNode(node));
189
- case 8: // Comment
190
- return hasBindingValue(node);
191
- default:
192
- return false;
193
- }
194
- }
195
-
196
- getBindingsString(node: Node): string | null {
197
- switch (node.nodeType) {
198
- case 1: // Element
199
- return (node as Element).getAttribute(DATA_BIND_ATTR);
200
- case 8: // Comment
201
- return virtualNodeBindingValue(node);
202
- default:
203
- return null;
204
- }
205
- }
206
-
207
- getBindingAccessors(
208
- node: Node,
209
- bindingContext: BindingContext,
210
- ): Record<string, () => unknown> | null {
211
- const bindingsString = this.getBindingsString(node);
212
- let result = bindingsString
213
- ? this.parseBindingsString(bindingsString, bindingContext, node, { valueAccessors: true }) as Record<string, () => unknown> | null
214
- : null;
215
-
216
- if (_addBindingsForCustomElement) {
217
- result = _addBindingsForCustomElement(result, node, bindingContext, true) as typeof result;
218
- }
219
-
220
- return result;
221
- }
222
-
223
- getBindings(
224
- node: Node,
225
- bindingContext: BindingContext,
226
- ): Record<string, unknown> | null {
227
- const bindingsString = this.getBindingsString(node);
228
- let result = bindingsString
229
- ? this.parseBindingsString(bindingsString, bindingContext, node)
230
- : null;
231
-
232
- if (_addBindingsForCustomElement) {
233
- result = _addBindingsForCustomElement(result, node, bindingContext, false) as typeof result;
234
- }
235
-
236
- return result;
237
- }
238
-
239
- parseBindingsString(
240
- bindingsString: string,
241
- bindingContext: BindingContext,
242
- node: Node,
243
- options?: { valueAccessors?: boolean },
244
- ): Record<string, unknown> | null {
245
- try {
246
- const bindingFunction = createBindingsStringEvaluatorViaCache(
247
- bindingsString,
248
- this.bindingCache,
249
- options,
250
- );
251
- return bindingFunction(bindingContext, node);
252
- } catch (ex) {
253
- const err = ex as Error;
254
- err.message = "Unable to parse bindings.\nBindings value: " + bindingsString + "\nMessage: " + err.message;
255
- throw err;
256
- }
257
- }
258
- }
259
-
260
- export const instance = new BindingProvider();
1
+ import type { BindingContext } from './bindingContext.js';
2
+ import type { AllBindingsAccessor } from './expressionRewriting.js';
3
+ export type { AllBindingsAccessor } from './expressionRewriting.js';
4
+ import { preProcessBindings } from './expressionRewriting.js';
5
+ import { hasBindingValue, virtualNodeBindingValue } from './virtualElements.js';
6
+ import { unwrapObservable } from './utils.js';
7
+
8
+ const DATA_BIND_ATTR = 'data-bind';
9
+
10
+ // ---- Binding Handler Interface ----
11
+
12
+ export interface BindingHandler {
13
+ init?(
14
+ node: Node,
15
+ valueAccessor: () => unknown,
16
+ allBindings: AllBindingsAccessor,
17
+ viewModel: unknown,
18
+ bindingContext: BindingContext,
19
+ ): void | { controlsDescendantBindings: boolean };
20
+
21
+ update?(
22
+ node: Node,
23
+ valueAccessor: () => unknown,
24
+ allBindings: AllBindingsAccessor,
25
+ viewModel: unknown,
26
+ bindingContext: BindingContext,
27
+ ): void;
28
+
29
+ preprocess?(
30
+ value: string,
31
+ key: string,
32
+ addBinding: (key: string, val: string) => void,
33
+ ): string | void;
34
+
35
+ after?: string[];
36
+
37
+ getNamespacedHandler?(name: string, namespace: string, namespacedName: string): BindingHandler;
38
+ }
39
+
40
+ // ---- Binding Handler Registry ----
41
+
42
+ export const bindingHandlers: Record<string, BindingHandler> = {};
43
+
44
+ let _getBindingHandlerImpl = (key: string): BindingHandler | undefined => bindingHandlers[key];
45
+
46
+ export function getBindingHandler(key: string): BindingHandler | undefined {
47
+ return _getBindingHandlerImpl(key);
48
+ }
49
+
50
+ // ---- Preprocessor Infrastructure ----
51
+
52
+ export type PreprocessFn = (
53
+ value: string,
54
+ key: string,
55
+ addBinding: (key: string, val: string) => void,
56
+ ) => string | void;
57
+
58
+ export type NodePreprocessFn = (node: Node) => Node[] | void;
59
+
60
+ function getOrCreateHandler(bindingKeyOrHandler: string | BindingHandler): BindingHandler {
61
+ if (typeof bindingKeyOrHandler === 'object') return bindingKeyOrHandler;
62
+ return getBindingHandler(bindingKeyOrHandler) || (bindingHandlers[bindingKeyOrHandler] = {});
63
+ }
64
+
65
+ export function chainPreprocessor(
66
+ handler: BindingHandler,
67
+ fn: PreprocessFn,
68
+ ): BindingHandler {
69
+ const previous = handler.preprocess;
70
+ if (previous) {
71
+ handler.preprocess = function (value, key, addBinding) {
72
+ const result = previous(value, key, addBinding);
73
+ if (result !== undefined) {
74
+ return fn(result, key, addBinding);
75
+ }
76
+ };
77
+ } else {
78
+ handler.preprocess = fn;
79
+ }
80
+ return handler;
81
+ }
82
+
83
+ export function addBindingPreprocessor(
84
+ bindingKeyOrHandler: string | BindingHandler,
85
+ preprocessFn: PreprocessFn,
86
+ ): BindingHandler {
87
+ return chainPreprocessor(getOrCreateHandler(bindingKeyOrHandler), preprocessFn);
88
+ }
89
+
90
+ export function addNodePreprocessor(preprocessFn: NodePreprocessFn): void {
91
+ const provider = instance;
92
+ const previous = provider.preprocessNode;
93
+ if (previous) {
94
+ provider.preprocessNode = function (node: Node): Node[] | void {
95
+ const newNodes = previous.call(this, node);
96
+ if (!newNodes) {
97
+ return preprocessFn.call(this, node);
98
+ }
99
+ return newNodes;
100
+ };
101
+ } else {
102
+ provider.preprocessNode = preprocessFn;
103
+ }
104
+ }
105
+
106
+ export function addBindingHandlerCreator(
107
+ matchRegex: RegExp,
108
+ callbackFn: (match: RegExpMatchArray, bindingKey: string) => BindingHandler | undefined,
109
+ ): void {
110
+ const previousImpl = _getBindingHandlerImpl;
111
+ _getBindingHandlerImpl = (key) => {
112
+ const existing = previousImpl(key);
113
+ if (existing) return existing;
114
+ const match = key.match(matchRegex);
115
+ return match ? callbackFn(match, key) : undefined;
116
+ };
117
+ }
118
+
119
+ // ---- Filters Registry (injected by filters module) ----
120
+
121
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
122
+ let _filters: Record<string, Function> = {};
123
+
124
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
125
+ export function setFiltersRegistry(filters: Record<string, Function>): void {
126
+ _filters = filters;
127
+ }
128
+
129
+ // ---- Compiled Binding Function Cache ----
130
+
131
+ type BindingEvaluator = (context: BindingContext, element: Node) => Record<string, unknown> | null;
132
+
133
+ function createBindingsStringEvaluator(
134
+ bindingsString: string,
135
+ options?: { valueAccessors?: boolean },
136
+ ): BindingEvaluator {
137
+ const rewrittenBindings = preProcessBindings(bindingsString, {
138
+ valueAccessors: options?.valueAccessors,
139
+ getBindingHandler,
140
+ });
141
+ const functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
142
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
143
+ const innerFn = new Function("$context", "$element", "$unwrap", "$filters", functionBody) as
144
+ (context: BindingContext, element: Node, unwrap: typeof unwrapObservable, filters: Record<string, Function>) => Record<string, unknown> | null;
145
+ return (context, element) => innerFn(context, element, unwrapObservable, _filters);
146
+ }
147
+
148
+ function createBindingsStringEvaluatorViaCache(
149
+ bindingsString: string,
150
+ cache: Record<string, BindingEvaluator>,
151
+ options?: { valueAccessors?: boolean },
152
+ ): BindingEvaluator {
153
+ const cacheKey = bindingsString + (options?.valueAccessors ? 'true' : '');
154
+ return cache[cacheKey] || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString, options));
155
+ }
156
+
157
+ // ---- Custom Element Hook (set by componentBinding.ts to avoid circular imports) ----
158
+
159
+ export type GetComponentNameForNodeFn = (node: Node) => string | undefined;
160
+ export type AddBindingsForCustomElementFn = (
161
+ allBindings: Record<string, unknown> | null,
162
+ node: Node,
163
+ bindingContext: BindingContext,
164
+ valueAccessors?: boolean,
165
+ ) => Record<string, unknown> | null;
166
+
167
+ let _getComponentNameForNode: GetComponentNameForNodeFn | undefined;
168
+ let _addBindingsForCustomElement: AddBindingsForCustomElementFn | undefined;
169
+
170
+ export function setCustomElementHooks(
171
+ getComponentNameForNode: GetComponentNameForNodeFn,
172
+ addBindingsForCustomElement: AddBindingsForCustomElementFn,
173
+ ): void {
174
+ _getComponentNameForNode = getComponentNameForNode;
175
+ _addBindingsForCustomElement = addBindingsForCustomElement;
176
+ }
177
+
178
+ // ---- Binding Provider ----
179
+
180
+ export class BindingProvider {
181
+ bindingCache: Record<string, BindingEvaluator> = {};
182
+ preprocessNode?: NodePreprocessFn;
183
+
184
+ nodeHasBindings(node: Node): boolean {
185
+ switch (node.nodeType) {
186
+ case 1: // Element
187
+ return (node as Element).getAttribute(DATA_BIND_ATTR) != null
188
+ || !!(_getComponentNameForNode && _getComponentNameForNode(node));
189
+ case 8: // Comment
190
+ return hasBindingValue(node);
191
+ default:
192
+ return false;
193
+ }
194
+ }
195
+
196
+ getBindingsString(node: Node): string | null {
197
+ switch (node.nodeType) {
198
+ case 1: // Element
199
+ return (node as Element).getAttribute(DATA_BIND_ATTR);
200
+ case 8: // Comment
201
+ return virtualNodeBindingValue(node);
202
+ default:
203
+ return null;
204
+ }
205
+ }
206
+
207
+ getBindingAccessors(
208
+ node: Node,
209
+ bindingContext: BindingContext,
210
+ ): Record<string, () => unknown> | null {
211
+ const bindingsString = this.getBindingsString(node);
212
+ let result = bindingsString
213
+ ? this.parseBindingsString(bindingsString, bindingContext, node, { valueAccessors: true }) as Record<string, () => unknown> | null
214
+ : null;
215
+
216
+ if (_addBindingsForCustomElement) {
217
+ result = _addBindingsForCustomElement(result, node, bindingContext, true) as typeof result;
218
+ }
219
+
220
+ return result;
221
+ }
222
+
223
+ getBindings(
224
+ node: Node,
225
+ bindingContext: BindingContext,
226
+ ): Record<string, unknown> | null {
227
+ const bindingsString = this.getBindingsString(node);
228
+ let result = bindingsString
229
+ ? this.parseBindingsString(bindingsString, bindingContext, node)
230
+ : null;
231
+
232
+ if (_addBindingsForCustomElement) {
233
+ result = _addBindingsForCustomElement(result, node, bindingContext, false) as typeof result;
234
+ }
235
+
236
+ return result;
237
+ }
238
+
239
+ parseBindingsString(
240
+ bindingsString: string,
241
+ bindingContext: BindingContext,
242
+ node: Node,
243
+ options?: { valueAccessors?: boolean },
244
+ ): Record<string, unknown> | null {
245
+ try {
246
+ const bindingFunction = createBindingsStringEvaluatorViaCache(
247
+ bindingsString,
248
+ this.bindingCache,
249
+ options,
250
+ );
251
+ return bindingFunction(bindingContext, node);
252
+ } catch (ex) {
253
+ const err = ex as Error;
254
+ err.message = "Unable to parse bindings.\nBindings value: " + bindingsString + "\nMessage: " + err.message;
255
+ throw err;
256
+ }
257
+ }
258
+ }
259
+
260
+ export const instance = new BindingProvider();