@fluffjs/fluff 0.2.4 → 0.3.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.
Files changed (62) hide show
  1. package/bundle.min.js +1 -0
  2. package/decorators/Component.d.ts +0 -8
  3. package/decorators/Component.js +0 -11
  4. package/decorators/Input.d.ts +1 -1
  5. package/decorators/InputOutputHelper.d.ts +2 -1
  6. package/decorators/InputOutputHelper.js +3 -1
  7. package/decorators/Output.d.ts +1 -1
  8. package/decorators/Pipe.d.ts +1 -0
  9. package/decorators/Pipe.js +1 -1
  10. package/decorators/Reactive.d.ts +2 -1
  11. package/decorators/Reactive.js +1 -1
  12. package/index.d.ts +2 -1
  13. package/index.js +1 -1
  14. package/interfaces/FluffHostElement.d.ts +13 -3
  15. package/interfaces/ReactiveOptions.d.ts +6 -0
  16. package/interfaces/ReactiveOptions.js +1 -0
  17. package/package.json +1 -1
  18. package/runtime/FluffBase.d.ts +11 -1
  19. package/runtime/FluffBase.js +50 -17
  20. package/runtime/FluffElementImpl.d.ts +9 -21
  21. package/runtime/FluffElementImpl.js +48 -149
  22. package/runtime/ForController.js +1 -2
  23. package/runtime/IfController.js +1 -2
  24. package/runtime/MarkerConfigGuards.d.ts +0 -2
  25. package/runtime/MarkerConfigGuards.js +0 -3
  26. package/runtime/MarkerController.d.ts +1 -9
  27. package/runtime/MarkerController.js +28 -144
  28. package/runtime/MarkerManager.d.ts +2 -1
  29. package/runtime/MarkerManager.js +1 -2
  30. package/runtime/MarkerManagerInterface.d.ts +3 -1
  31. package/runtime/SwitchController.js +1 -2
  32. package/runtime/TextController.d.ts +0 -1
  33. package/runtime/TextController.js +5 -19
  34. package/runtime/tests/TestChildTasksListComponent.js +2 -2
  35. package/runtime/tests/TestForComponent.js +2 -2
  36. package/runtime/tests/TestForReinsertBindsInputParentComponent.js +2 -2
  37. package/runtime/tests/TestForTextMarkerCollisionNoTrackParentComponent.js +2 -2
  38. package/runtime/tests/TestForTextMarkerCollisionParentComponent.js +2 -2
  39. package/runtime/tests/TestForUnsubscribeNestedParentComponent.js +2 -2
  40. package/runtime/tests/TestGetterReactivityComponent.js +2 -2
  41. package/runtime/tests/TestHarness.d.ts +9 -0
  42. package/runtime/tests/TestHarness.js +29 -0
  43. package/runtime/tests/TestIfReinsertBindsInputChildComponent.js +2 -2
  44. package/runtime/tests/TestIfReinsertBindsInputParentComponent.js +2 -2
  45. package/runtime/tests/TestIfUnsubscribeNestedParentComponent.js +2 -2
  46. package/runtime/tests/TestInterpolationNestedPropertyComponentBase.js +2 -2
  47. package/runtime/tests/TestLateDefineForComponent.js +2 -2
  48. package/runtime/tests/TestNullInputTextComponent.js +2 -2
  49. package/runtime/tests/TestOutputBindingParentComponent.js +2 -2
  50. package/runtime/tests/TestParentBindsTasksComponent.js +2 -2
  51. package/runtime/tests/TestSwitchReinsertBindsInputChildComponent.js +2 -2
  52. package/runtime/tests/TestSwitchReinsertBindsInputParentComponent.js +2 -2
  53. package/runtime/tests/TestSwitchUnsubscribeNestedParentComponent.js +2 -2
  54. package/runtime/tests/TestTemplateNestedMarkersComponent.js +2 -2
  55. package/runtime/tests/TestUnsubscribeNestedGrandchildComponent.js +2 -2
  56. package/runtime/tests/createPipeUnwrapTestComponent.js +2 -2
  57. package/runtime/tests/createTestInterpolationPipeComponent.js +2 -2
  58. package/runtime/tests/createTestInterpolationPipeWithArgsComponent.js +2 -2
  59. package/utils/Property.d.ts +8 -2
  60. package/utils/Property.js +48 -8
  61. package/utils/DomUtils.d.ts +0 -3
  62. package/utils/DomUtils.js +0 -6
@@ -38,7 +38,6 @@ export class ForController extends MarkerController {
38
38
  }
39
39
  this.refreshParentBindings();
40
40
  };
41
- this.subscribeTo(deps, update);
42
- update();
41
+ this.subscribeAndRun(deps, update);
43
42
  }
44
43
  }
@@ -41,7 +41,6 @@ export class IfController extends MarkerController {
41
41
  this.refreshParentBindings();
42
42
  }
43
43
  };
44
- this.subscribeTo(allDeps, update);
45
- update();
44
+ this.subscribeAndRun(allDeps, update);
46
45
  }
47
46
  }
@@ -1,12 +1,10 @@
1
1
  import type { BreakMarkerConfig } from '../interfaces/BreakMarkerConfig.js';
2
- import type { FluffHostElement } from '../interfaces/FluffHostElement.js';
3
2
  import type { ForMarkerConfig } from '../interfaces/ForMarkerConfig.js';
4
3
  import type { IfMarkerConfig } from '../interfaces/IfMarkerConfig.js';
5
4
  import type { MarkerConfig } from '../interfaces/MarkerConfig.js';
6
5
  import type { SwitchMarkerConfig } from '../interfaces/SwitchMarkerConfig.js';
7
6
  import type { TextMarkerConfig } from '../interfaces/TextMarkerConfig.js';
8
7
  export declare class MarkerConfigGuards {
9
- static isFluffHostElement(el: Element): el is FluffHostElement;
10
8
  static isIfConfig(config: MarkerConfig): config is IfMarkerConfig;
11
9
  static isForConfig(config: MarkerConfig): config is ForMarkerConfig;
12
10
  static isSwitchConfig(config: MarkerConfig): config is SwitchMarkerConfig;
@@ -1,7 +1,4 @@
1
1
  export class MarkerConfigGuards {
2
- static isFluffHostElement(el) {
3
- return '__getReactiveProp' in el;
4
- }
5
2
  static isIfConfig(config) {
6
3
  return config.type === 'if';
7
4
  }
@@ -2,7 +2,6 @@ import type { FluffHostElement } from '../interfaces/FluffHostElement.js';
2
2
  import type { PropertyChain } from '../interfaces/PropertyChain.js';
3
3
  import type { RenderContext } from '../interfaces/RenderContext.js';
4
4
  import type { Subscription } from '../interfaces/Subscription.js';
5
- import { Property } from '../utils/Property.js';
6
5
  import { type Scope } from './ScopeRegistry.js';
7
6
  interface MarkerManagerLike {
8
7
  getController: (id: number, startMarker: Comment) => MarkerController | undefined;
@@ -27,22 +26,15 @@ export declare abstract class MarkerController {
27
26
  cleanup(): void;
28
27
  updateRenderContext(renderContext?: RenderContext): void;
29
28
  protected evaluateExpr(exprId: number): unknown;
30
- private getCompiledExprFn;
31
29
  protected getScope(): Scope;
32
30
  protected collectLocalsFromScope(scope: Scope): Record<string, unknown>;
33
- protected subscribeTo(deps: PropertyChain[], callback: () => void): void;
34
- private subscribeToPropertyChain;
35
- private subscribeToNestedChain;
36
- protected getReactivePropFromScope(propName: string, scope: Scope): Property<unknown> | undefined;
31
+ protected subscribeAndRun(deps: PropertyChain[], callback: () => void): void;
37
32
  protected createChildScope(locals: Record<string, unknown>): Scope;
38
33
  protected clearContentBetweenMarkersWithCleanup(bindingsSubscriptions: Subscription[]): void;
39
34
  protected insertBeforeEndMarker(node: Node): void;
40
35
  protected refreshParentBindings(): void;
41
- protected processBindingsOnElement(el: HTMLElement, scope: Scope): void;
42
- protected processBindingsOnElementWithSubscriptions(el: HTMLElement, scope: Scope, subscriptions: Subscription[]): void;
43
36
  private __getFluffElementHost;
44
37
  protected setScopeOnChildren(node: Node, scope: Scope, renderContext?: RenderContext, markerManager?: MarkerManagerLike, bindingsSubscriptions?: Subscription[]): void;
45
- protected insertAndScopeTemplateContent(content: Node, nodes: Node[], scope: Scope, renderContext: RenderContext | undefined, markerManager: MarkerManagerLike | undefined, bindingsSubscriptions: Subscription[]): void;
46
38
  protected cloneAndInsertTemplate(template: HTMLTemplateElement, context: Record<string, unknown>, renderContext: RenderContext | undefined, bindingsSubscriptions: Subscription[]): void;
47
39
  protected processBindingsOnNode(node: HTMLElement, scope: Scope, bindingsSubscriptions?: Subscription[]): void;
48
40
  }
@@ -1,9 +1,7 @@
1
- import { DomUtils } from '../utils/DomUtils.js';
2
- import { Property } from '../utils/Property.js';
3
1
  import { FluffBase } from './FluffBase.js';
4
2
  import { FluffElement } from './FluffElement.js';
5
- import { MarkerConfigGuards } from './MarkerConfigGuards.js';
6
3
  import { registerScope } from './ScopeRegistry.js';
4
+ const MARKER_REGEX = /^fluff:(if|for|switch|text|break):(\d+)$/;
7
5
  export class MarkerController {
8
6
  id;
9
7
  startMarker;
@@ -44,21 +42,20 @@ export class MarkerController {
44
42
  evaluateExpr(exprId) {
45
43
  const scope = this.getScope();
46
44
  const allLocals = this.collectLocalsFromScope(scope);
47
- const fn = this.getCompiledExprFn(exprId);
45
+ if (scope.host.__evaluateExpr) {
46
+ return scope.host.__evaluateExpr(exprId, allLocals);
47
+ }
48
+ const fn = FluffBase.__e[exprId];
49
+ if (typeof fn !== 'function') {
50
+ return undefined;
51
+ }
48
52
  try {
49
53
  return fn(scope.host, allLocals);
50
54
  }
51
- catch (e) {
55
+ catch {
52
56
  return undefined;
53
57
  }
54
58
  }
55
- getCompiledExprFn(exprId) {
56
- const fn = FluffBase.__e[exprId];
57
- if (typeof fn !== 'function') {
58
- throw new Error(`Missing compiled expression function for exprId ${exprId}`);
59
- }
60
- return fn;
61
- }
62
59
  getScope() {
63
60
  if (this.parentScope) {
64
61
  return this.parentScope;
@@ -79,114 +76,18 @@ export class MarkerController {
79
76
  Object.assign(result, scope.locals);
80
77
  return result;
81
78
  }
82
- subscribeTo(deps, callback) {
79
+ subscribeAndRun(deps, callback) {
83
80
  const scope = this.getScope();
84
- for (const dep of deps) {
81
+ const filteredDeps = deps.filter(dep => {
85
82
  if (Array.isArray(dep)) {
86
- this.subscribeToPropertyChain(dep, scope, callback);
87
- }
88
- else {
89
- if (dep.startsWith('['))
90
- continue;
91
- const reactiveProp = this.getReactivePropFromScope(dep, scope);
92
- if (reactiveProp) {
93
- const sub = reactiveProp.onChange.subscribe(callback);
94
- this.subscriptions.push(sub);
95
- }
96
- }
97
- }
98
- }
99
- subscribeToPropertyChain(chain, scope, callback) {
100
- if (chain.length === 0)
101
- return;
102
- const [first, ...rest] = chain;
103
- if (first.startsWith('['))
104
- return;
105
- const reactiveProp = this.getReactivePropFromScope(first, scope);
106
- if (reactiveProp) {
107
- if (rest.length === 0) {
108
- this.subscriptions.push(reactiveProp.onChange.subscribe(callback));
109
- }
110
- else {
111
- let nestedSubs = [];
112
- const resubscribeNested = () => {
113
- for (const sub of nestedSubs) {
114
- sub.unsubscribe();
115
- }
116
- nestedSubs = [];
117
- const currentValue = reactiveProp.getValue();
118
- if (currentValue !== null && currentValue !== undefined) {
119
- this.subscribeToNestedChain(currentValue, rest, callback, (sub) => {
120
- nestedSubs.push(sub);
121
- this.subscriptions.push(sub);
122
- });
123
- }
124
- callback();
125
- };
126
- this.subscriptions.push(reactiveProp.onChange.subscribe(resubscribeNested));
127
- const currentValue = reactiveProp.getValue();
128
- if (currentValue !== null && currentValue !== undefined) {
129
- this.subscribeToNestedChain(currentValue, rest, callback, (sub) => {
130
- nestedSubs.push(sub);
131
- this.subscriptions.push(sub);
132
- });
133
- }
134
- }
135
- }
136
- }
137
- subscribeToNestedChain(obj, chain, callback, addSub) {
138
- if (chain.length === 0 || obj === null || obj === undefined)
139
- return;
140
- const [first, ...rest] = chain;
141
- const prop = Reflect.get(obj, first);
142
- if (prop instanceof Property) {
143
- if (rest.length === 0) {
144
- addSub(prop.onChange.subscribe(callback));
145
- }
146
- else {
147
- let nestedSubs = [];
148
- const resubscribeNested = () => {
149
- for (const sub of nestedSubs) {
150
- sub.unsubscribe();
151
- }
152
- nestedSubs = [];
153
- const currentValue = prop.getValue();
154
- if (currentValue !== null && currentValue !== undefined) {
155
- this.subscribeToNestedChain(currentValue, rest, callback, (sub) => {
156
- nestedSubs.push(sub);
157
- addSub(sub);
158
- });
159
- }
160
- callback();
161
- };
162
- addSub(prop.onChange.subscribe(resubscribeNested));
163
- const currentValue = prop.getValue();
164
- if (currentValue !== null && currentValue !== undefined) {
165
- this.subscribeToNestedChain(currentValue, rest, callback, (sub) => {
166
- nestedSubs.push(sub);
167
- addSub(sub);
168
- });
169
- }
83
+ return dep.length > 0 && !dep[0].startsWith('[');
170
84
  }
85
+ return !dep.startsWith('[');
86
+ });
87
+ if (scope.host.__subscribeToExpression) {
88
+ scope.host.__subscribeToExpression(filteredDeps, scope, callback, this.subscriptions);
171
89
  }
172
- else if (rest.length > 0 && prop !== null && prop !== undefined) {
173
- this.subscribeToNestedChain(prop, rest, callback, addSub);
174
- }
175
- }
176
- getReactivePropFromScope(propName, scope) {
177
- if (propName in scope.locals) {
178
- return undefined;
179
- }
180
- if (scope.parent) {
181
- return this.getReactivePropFromScope(propName, scope.parent);
182
- }
183
- if (MarkerConfigGuards.isFluffHostElement(scope.host) && scope.host.__getReactiveProp) {
184
- const reactiveProp = scope.host.__getReactiveProp(propName);
185
- if (reactiveProp instanceof Property) {
186
- return reactiveProp;
187
- }
188
- }
189
- return undefined;
90
+ callback();
190
91
  }
191
92
  createChildScope(locals) {
192
93
  return {
@@ -207,7 +108,7 @@ export class MarkerController {
207
108
  while (current && current !== this.endMarker) {
208
109
  const next = current.nextSibling;
209
110
  if (current instanceof Comment) {
210
- const markerMatch = /^fluff:(if|for|switch|text|break):(\d+)$/.exec(current.data);
111
+ const markerMatch = MARKER_REGEX.exec(current.data);
211
112
  if (markerMatch && this.markerManager?.cleanupController) {
212
113
  const markerId = parseInt(markerMatch[2], 10);
213
114
  this.markerManager.cleanupController(markerId, current);
@@ -239,24 +140,12 @@ export class MarkerController {
239
140
  const scope = this.getScope();
240
141
  fluffHost.__processBindingsOnElementPublic(parent, scope);
241
142
  }
242
- processBindingsOnElement(el, scope) {
243
- const fluffHost = this.__getFluffElementHost();
244
- if (!fluffHost)
245
- return;
246
- fluffHost.__processBindingsOnElementPublic(el, scope);
247
- }
248
- processBindingsOnElementWithSubscriptions(el, scope, subscriptions) {
249
- const fluffHost = this.__getFluffElementHost();
250
- if (!fluffHost)
251
- return;
252
- fluffHost.__processBindingsOnElementPublic(el, scope, subscriptions);
253
- }
254
143
  __getFluffElementHost() {
255
144
  return this.host instanceof FluffElement ? this.host : null;
256
145
  }
257
146
  setScopeOnChildren(node, scope, renderContext, markerManager, bindingsSubscriptions) {
258
147
  if (node instanceof Comment) {
259
- const markerMatch = /^fluff:(if|for|switch|text|break):(\d+)$/.exec(node.data);
148
+ const markerMatch = MARKER_REGEX.exec(node.data);
260
149
  if (markerMatch && markerManager) {
261
150
  const [, markerType, markerIdStr] = markerMatch;
262
151
  const markerId = parseInt(markerIdStr, 10);
@@ -290,7 +179,7 @@ export class MarkerController {
290
179
  node.__parentScope = scope;
291
180
  this.processBindingsOnNode(node, scope, bindingsSubscriptions);
292
181
  }
293
- else if (node instanceof HTMLElement && DomUtils.isCustomElement(node)) {
182
+ else if (node instanceof HTMLElement && customElements.get(node.tagName.toLowerCase()) !== undefined) {
294
183
  const scopeId = registerScope(scope);
295
184
  node.setAttribute('data-fluff-scope-id', scopeId);
296
185
  this.processBindingsOnNode(node, scope, bindingsSubscriptions);
@@ -302,12 +191,6 @@ export class MarkerController {
302
191
  this.setScopeOnChildren(child, scope, renderContext, markerManager, bindingsSubscriptions);
303
192
  }
304
193
  }
305
- insertAndScopeTemplateContent(content, nodes, scope, renderContext, markerManager, bindingsSubscriptions) {
306
- this.insertBeforeEndMarker(content);
307
- for (const node of nodes) {
308
- this.setScopeOnChildren(node, scope, renderContext, markerManager, bindingsSubscriptions);
309
- }
310
- }
311
194
  cloneAndInsertTemplate(template, context, renderContext, bindingsSubscriptions) {
312
195
  const content = template.content.cloneNode(true);
313
196
  if (!(content instanceof DocumentFragment)) {
@@ -315,14 +198,15 @@ export class MarkerController {
315
198
  }
316
199
  const nodes = Array.from(content.childNodes);
317
200
  const scope = this.createChildScope(context);
318
- this.insertAndScopeTemplateContent(content, nodes, scope, renderContext, this.markerManager, bindingsSubscriptions);
201
+ this.insertBeforeEndMarker(content);
202
+ for (const node of nodes) {
203
+ this.setScopeOnChildren(node, scope, renderContext, this.markerManager, bindingsSubscriptions);
204
+ }
319
205
  }
320
206
  processBindingsOnNode(node, scope, bindingsSubscriptions) {
321
- if (bindingsSubscriptions) {
322
- this.processBindingsOnElementWithSubscriptions(node, scope, bindingsSubscriptions);
323
- }
324
- else {
325
- this.processBindingsOnElement(node, scope);
326
- }
207
+ const fluffHost = this.__getFluffElementHost();
208
+ if (!fluffHost)
209
+ return;
210
+ fluffHost.__processBindingsOnElementPublic(node, scope, bindingsSubscriptions);
327
211
  }
328
212
  }
@@ -1,3 +1,4 @@
1
+ import type { MarkerConfigEntries } from './MarkerManagerInterface.js';
1
2
  import type { MarkerController } from './MarkerController.js';
2
3
  export declare class MarkerManager {
3
4
  private readonly controllers;
@@ -5,7 +6,7 @@ export declare class MarkerManager {
5
6
  private readonly host;
6
7
  private readonly shadowRoot;
7
8
  constructor(host: HTMLElement, shadowRoot: ShadowRoot);
8
- initializeFromConfig(configJson: string): void;
9
+ initializeFromConfig(entries: MarkerConfigEntries): void;
9
10
  ensureController(id: number, type: string, startMarker: Comment, endMarker: Comment | null): MarkerController | undefined;
10
11
  getController(id: number, startMarker: Comment): MarkerController | undefined;
11
12
  cleanupController(id: number, startMarker?: Comment): void;
@@ -13,8 +13,7 @@ export class MarkerManager {
13
13
  this.host = host;
14
14
  this.shadowRoot = shadowRoot;
15
15
  }
16
- initializeFromConfig(configJson) {
17
- const entries = JSON.parse(configJson);
16
+ initializeFromConfig(entries) {
18
17
  this.configs.clear();
19
18
  for (const [id, config] of entries) {
20
19
  this.configs.set(id, config);
@@ -1,4 +1,6 @@
1
+ import type { MarkerConfig } from '../interfaces/MarkerConfig.js';
2
+ export type MarkerConfigEntries = [number, MarkerConfig][];
1
3
  export interface MarkerManagerInterface {
2
- initializeFromConfig: (configJson: string) => void;
4
+ initializeFromConfig: (entries: MarkerConfigEntries) => void;
3
5
  cleanup: () => void;
4
6
  }
@@ -37,7 +37,6 @@ export class SwitchController extends MarkerController {
37
37
  }
38
38
  this.refreshParentBindings();
39
39
  };
40
- this.subscribeTo(deps, update);
41
- update();
40
+ this.subscribeAndRun(deps, update);
42
41
  }
43
42
  }
@@ -6,5 +6,4 @@ export declare class TextController extends MarkerController {
6
6
  constructor(id: number, startMarker: Comment, endMarker: Comment | null, host: HTMLElement, shadowRoot: ShadowRoot, config: TextMarkerConfig);
7
7
  initialize(): void;
8
8
  private formatValue;
9
- private applyPipe;
10
9
  }
@@ -1,5 +1,3 @@
1
- import { getPipeTransform } from '../decorators/Pipe.js';
2
- import { Property } from '../utils/Property.js';
3
1
  import { MarkerController } from './MarkerController.js';
4
2
  export class TextController extends MarkerController {
5
3
  config;
@@ -15,18 +13,16 @@ export class TextController extends MarkerController {
15
13
  const pipes = this.config.pipes ?? [];
16
14
  const update = () => {
17
15
  let result = this.evaluateExpr(this.config.exprId);
18
- if (result instanceof Property) {
19
- result = result.getValue();
20
- }
21
- for (const pipe of pipes) {
22
- result = this.applyPipe(pipe.name, result, pipe.argExprIds);
16
+ if (this.host.__applyPipesForController) {
17
+ const scope = this.getScope();
18
+ const allLocals = this.collectLocalsFromScope(scope);
19
+ result = this.host.__applyPipesForController(result, pipes, allLocals);
23
20
  }
24
21
  if (this.textNode) {
25
22
  this.textNode.textContent = this.formatValue(result);
26
23
  }
27
24
  };
28
- this.subscribeTo(deps, update);
29
- update();
25
+ this.subscribeAndRun(deps, update);
30
26
  }
31
27
  formatValue(result) {
32
28
  if (result === null || result === undefined) {
@@ -43,14 +39,4 @@ export class TextController extends MarkerController {
43
39
  }
44
40
  return '';
45
41
  }
46
- applyPipe(name, value, args) {
47
- const pipes = this.host.__pipes;
48
- const pipeFn = pipes?.[name] ?? getPipeTransform(name);
49
- if (!pipeFn) {
50
- console.warn(`Pipe "${name}" not found`);
51
- return value;
52
- }
53
- const evaluatedArgs = args.map(arg => this.evaluateExpr(arg));
54
- return pipeFn(value, ...evaluatedArgs);
55
- }
56
42
  }
@@ -17,9 +17,9 @@ export class TestChildTasksListComponent extends FluffElement {
17
17
  <div class="item"></div>
18
18
  </template>
19
19
  `;
20
- this.__setMarkerConfigs(JSON.stringify([
20
+ this.__setMarkerConfigs([
21
21
  [0, { type: 'for', iterator: 'task', iterableExprId: 2, deps: ['tasks'], hasEmpty: false }]
22
- ]));
22
+ ]);
23
23
  }
24
24
  __setupBindings() {
25
25
  this.__initializeMarkers(MarkerManager);
@@ -10,9 +10,9 @@ export class TestForComponent extends FluffElement {
10
10
  <test-for-child data-lid="l0"></test-for-child>
11
11
  </template>
12
12
  `;
13
- this.__setMarkerConfigs(JSON.stringify([
13
+ this.__setMarkerConfigs([
14
14
  [0, { type: 'for', iterator: 'item', iterableExprId: 0, deps: ['items'], hasEmpty: false }]
15
- ]));
15
+ ]);
16
16
  }
17
17
  __setupBindings() {
18
18
  this.__initializeMarkers(MarkerManager);
@@ -42,9 +42,9 @@ export class TestForReinsertBindsInputParentComponent extends FluffElement {
42
42
  <div class="empty">empty</div>
43
43
  </template>
44
44
  `;
45
- this.__setMarkerConfigs(JSON.stringify([
45
+ this.__setMarkerConfigs([
46
46
  [0, { type: 'for', iterator: 'item', iterableExprId: 0, deps: ['items'], hasEmpty: true }]
47
- ]));
47
+ ]);
48
48
  const bindings = {
49
49
  l0: [{ n: 'stats', b: 'property', e: 1, d: ['stats'] }]
50
50
  };
@@ -17,10 +17,10 @@ export class TestForTextMarkerCollisionNoTrackParentComponent extends FluffEleme
17
17
  <span class="tag"><!--fluff:text:9--><!--/fluff:text:9--></span>
18
18
  </template>
19
19
  `;
20
- this.__setMarkerConfigs(JSON.stringify([
20
+ this.__setMarkerConfigs([
21
21
  [0, { type: 'for', iterator: 'tag', iterableExprId: 0, deps: ['tags'], hasEmpty: false }],
22
22
  [9, { type: 'text', exprId: 1, deps: ['tag'] }]
23
- ]));
23
+ ]);
24
24
  }
25
25
  __setupBindings() {
26
26
  this.__initializeMarkers(MarkerManager);
@@ -28,7 +28,7 @@ export class TestForTextMarkerCollisionParentComponent extends FluffElement {
28
28
  <span class="tag"><!--fluff:text:9--><!--/fluff:text:9--></span>
29
29
  </template>
30
30
  `;
31
- this.__setMarkerConfigs(JSON.stringify([
31
+ this.__setMarkerConfigs([
32
32
  [0, { type: 'for', iterator: 'tag', iterableExprId: 0, deps: ['tags'], trackBy: 'tag', hasEmpty: false }],
33
33
  [
34
34
  9,
@@ -39,7 +39,7 @@ export class TestForTextMarkerCollisionParentComponent extends FluffElement {
39
39
  pipes: [{ name: 'lowercase', argExprIds: [] }, { name: 'capitalize', argExprIds: [] }]
40
40
  }
41
41
  ]
42
- ]));
42
+ ]);
43
43
  }
44
44
  __setupBindings() {
45
45
  this.__initializeMarkers(MarkerManager);
@@ -20,9 +20,9 @@ export class TestForUnsubscribeNestedParentComponent extends TestUnsubscribeNest
20
20
  <div class="empty">empty</div>
21
21
  </template>
22
22
  `;
23
- this.__setMarkerConfigs(JSON.stringify([
23
+ this.__setMarkerConfigs([
24
24
  [0, { type: 'for', iterator: 'item', iterableExprId: 0, deps: ['items'], hasEmpty: true }]
25
- ]));
25
+ ]);
26
26
  const bindings = {
27
27
  l0: [{ n: 'stats', b: 'property', e: 1, d: ['stats'] }]
28
28
  };
@@ -16,9 +16,9 @@ export class TestGetterReactivityComponent extends FluffElement {
16
16
  this.__getShadowRoot().innerHTML = `
17
17
  <div class="count"><!--fluff:text:0--><!--/fluff:text:0--></div>
18
18
  `;
19
- this.__setMarkerConfigs(JSON.stringify([
19
+ this.__setMarkerConfigs([
20
20
  [0, { type: 'text', exprId: 0, deps: ['items'], pipes: [] }]
21
- ]));
21
+ ]);
22
22
  }
23
23
  __setupBindings() {
24
24
  this.__initializeMarkers(MarkerManager);
@@ -0,0 +1,9 @@
1
+ import { type ExpressionFn, type HandlerFn } from '../FluffBase.js';
2
+ export declare class TestHarness {
3
+ static setExpressionTable(expressions: ExpressionFn[], handlers: HandlerFn[]): void;
4
+ static resetExpressionTable(): void;
5
+ static defineCustomElement(tag: string, ctor: CustomElementConstructor): void;
6
+ static mount(tag: string): HTMLElement;
7
+ static tick(count?: number): Promise<void>;
8
+ static waitForTimeout(): Promise<void>;
9
+ }
@@ -0,0 +1,29 @@
1
+ import { FluffBase } from '../FluffBase.js';
2
+ export class TestHarness {
3
+ static setExpressionTable(expressions, handlers) {
4
+ FluffBase.__setExpressionTable(expressions, handlers);
5
+ }
6
+ static resetExpressionTable() {
7
+ FluffBase.__setExpressionTable([], []);
8
+ }
9
+ static defineCustomElement(tag, ctor) {
10
+ if (!customElements.get(tag)) {
11
+ customElements.define(tag, ctor);
12
+ }
13
+ }
14
+ static mount(tag) {
15
+ const el = document.createElement(tag);
16
+ document.body.appendChild(el);
17
+ return el;
18
+ }
19
+ static async tick(count = 1) {
20
+ for (let i = 0; i < count; i++) {
21
+ await Promise.resolve();
22
+ }
23
+ }
24
+ static async waitForTimeout() {
25
+ await new Promise((resolve) => {
26
+ setTimeout(resolve, 0);
27
+ });
28
+ }
29
+ }
@@ -28,9 +28,9 @@ export class TestIfReinsertBindsInputChildComponent extends FluffElement {
28
28
  this.__getShadowRoot().innerHTML = `
29
29
  <div class="total"><!--fluff:text:0--><!--/fluff:text:0--></div>
30
30
  `;
31
- this.__setMarkerConfigs(JSON.stringify([
31
+ this.__setMarkerConfigs([
32
32
  [0, { type: 'text', exprId: 2, deps: ['stats'], pipes: [] }]
33
- ]));
33
+ ]);
34
34
  }
35
35
  __setupBindings() {
36
36
  this.__initializeMarkers(MarkerManager);
@@ -39,9 +39,9 @@ export class TestIfReinsertBindsInputParentComponent extends FluffElement {
39
39
  <test-if-reinsert-binds-input-child x-fluff-component data-lid="l0"></test-if-reinsert-binds-input-child>
40
40
  </template>
41
41
  `;
42
- this.__setMarkerConfigs(JSON.stringify([
42
+ this.__setMarkerConfigs([
43
43
  [0, { type: 'if', branches: [{ exprId: 0, deps: ['show'] }] }]
44
- ]));
44
+ ]);
45
45
  const bindings = {
46
46
  l0: [{ n: 'stats', b: 'property', e: 1, d: ['stats'] }]
47
47
  };
@@ -17,9 +17,9 @@ export class TestIfUnsubscribeNestedParentComponent extends TestUnsubscribeNeste
17
17
  <test-unsubscribe-nested-child x-fluff-component data-lid="l0"></test-unsubscribe-nested-child>
18
18
  </template>
19
19
  `;
20
- this.__setMarkerConfigs(JSON.stringify([
20
+ this.__setMarkerConfigs([
21
21
  [0, { type: 'if', branches: [{ exprId: 0, deps: ['show'] }] }]
22
- ]));
22
+ ]);
23
23
  const bindings = {
24
24
  l0: [{ n: 'stats', b: 'property', e: 1, d: ['stats'] }]
25
25
  };
@@ -19,9 +19,9 @@ export class TestInterpolationNestedPropertyComponentBase extends FluffElement {
19
19
  }
20
20
  __render() {
21
21
  this.__getShadowRoot().innerHTML = '<!--fluff:text:0--><!--/fluff:text:0-->';
22
- this.__setMarkerConfigs(JSON.stringify([
22
+ this.__setMarkerConfigs([
23
23
  [0, { type: 'text', exprId: 0, deps: [['hostClass', 'childProp']], pipes: [] }]
24
- ]));
24
+ ]);
25
25
  }
26
26
  __setupBindings() {
27
27
  this.__initializeMarkers(MarkerManager);
@@ -14,9 +14,9 @@ export class TestLateDefineForComponent extends FluffElement {
14
14
  <late-define-for-child x-fluff-component="" data-lid="l0"></late-define-for-child>
15
15
  </template>
16
16
  `;
17
- this.__setMarkerConfigs(JSON.stringify([
17
+ this.__setMarkerConfigs([
18
18
  [0, { type: 'for', iterator: 'column', iterableExprId: 0, deps: ['columns'], hasEmpty: false }]
19
- ]));
19
+ ]);
20
20
  }
21
21
  __setupBindings() {
22
22
  this.__initializeMarkers(MarkerManager);
@@ -25,10 +25,10 @@ export class TestNullInputTextComponent extends FluffElement {
25
25
  </template>
26
26
  <template data-fluff-branch="test-null-input-text-0-1"></template>
27
27
  `;
28
- this.__setMarkerConfigs(JSON.stringify([
28
+ this.__setMarkerConfigs([
29
29
  [0, { type: 'if', branches: [{ exprId: 0, deps: ['isEditing'] }, { exprId: undefined, deps: [] }] }],
30
30
  [1, { type: 'text', exprId: 1, deps: ['task'], pipes: [] }]
31
- ]));
31
+ ]);
32
32
  }
33
33
  __setupBindings() {
34
34
  this.__initializeMarkers(MarkerManager);
@@ -23,9 +23,9 @@ export class TestOutputBindingParentComponent extends FluffElement {
23
23
  <test-output-binding-child x-fluff-component data-lid="l0"></test-output-binding-child>
24
24
  </template>
25
25
  `;
26
- this.__setMarkerConfigs(JSON.stringify([
26
+ this.__setMarkerConfigs([
27
27
  [0, { type: 'if', branches: [{ exprId: 0, deps: ['show'] }] }]
28
- ]));
28
+ ]);
29
29
  const bindings = {
30
30
  l0: [{ n: 'edit', b: 'event', h: 0, d: ['onChildEdit'] }]
31
31
  };