@fluffjs/fluff 0.2.3 → 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 (66) 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 +12 -1
  19. package/runtime/FluffBase.js +70 -22
  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/createPropertyUnwrapNativeTestComponents.d.ts +4 -0
  58. package/runtime/tests/createPropertyUnwrapNativeTestComponents.js +23 -0
  59. package/runtime/tests/createTestInterpolationPipeComponent.js +2 -2
  60. package/runtime/tests/createTestInterpolationPipeWithArgsComponent.js +2 -2
  61. package/runtime/tests/typeguards.d.ts +21 -0
  62. package/runtime/tests/typeguards.js +48 -0
  63. package/utils/Property.d.ts +8 -2
  64. package/utils/Property.js +48 -8
  65. package/utils/DomUtils.d.ts +0 -3
  66. package/utils/DomUtils.js +0 -6
@@ -1,16 +1,16 @@
1
1
  import { getPipeTransform } from '../decorators/Pipe.js';
2
- import { DomUtils } from '../utils/DomUtils.js';
3
2
  import { Property } from '../utils/Property.js';
4
- import { Publisher } from '../utils/Publisher.js';
5
3
  import { FluffBase } from './FluffBase.js';
4
+ import { MarkerManager } from './MarkerManager.js';
6
5
  import { getScope } from './ScopeRegistry.js';
7
6
  export class FluffElement extends FluffBase {
8
7
  __pipes = {};
9
8
  _shadowRoot;
10
9
  _subscriptions = [];
11
10
  _initialized = false;
11
+ _pendingInit = false;
12
12
  _markerManager = null;
13
- _markerConfigJson = null;
13
+ _markerConfigEntries = null;
14
14
  _MarkerManagerClass = null;
15
15
  constructor() {
16
16
  super();
@@ -18,6 +18,16 @@ export class FluffElement extends FluffBase {
18
18
  }
19
19
  connectedCallback() {
20
20
  if (!this._initialized) {
21
+ if (!FluffBase.__areExpressionsReady()) {
22
+ if (!this._pendingInit) {
23
+ this._pendingInit = true;
24
+ FluffBase.__addPendingInit(() => {
25
+ this._pendingInit = false;
26
+ this.connectedCallback();
27
+ });
28
+ }
29
+ return;
30
+ }
21
31
  const scopeId = this.getAttribute('data-fluff-scope-id');
22
32
  if (scopeId && !this.__parentScope) {
23
33
  this.__parentScope = getScope(scopeId);
@@ -73,12 +83,10 @@ export class FluffElement extends FluffBase {
73
83
  this.__processBindingsOnElement(el, scope, subscriptions);
74
84
  }
75
85
  __setupBindings() {
86
+ this.__initializeMarkers(MarkerManager);
76
87
  this.__processBindings();
77
88
  this.__initializeMarkersInternal();
78
89
  }
79
- __addSubscription(sub) {
80
- this._subscriptions.push(sub);
81
- }
82
90
  __pipe(name, value, ...args) {
83
91
  const pipe = this.__pipes[name];
84
92
  if (!pipe) {
@@ -93,102 +101,47 @@ export class FluffElement extends FluffBase {
93
101
  __getShadowRoot() {
94
102
  return this._shadowRoot;
95
103
  }
96
- __setMarkerConfigs(configJson) {
97
- this._markerConfigJson = configJson;
104
+ __createProp(name, options) {
105
+ const prop = new Property(options);
106
+ Object.defineProperty(this, name, {
107
+ get() {
108
+ return prop.getValue();
109
+ },
110
+ set(v) {
111
+ prop.setValue(v);
112
+ },
113
+ enumerable: true,
114
+ configurable: true
115
+ });
116
+ return prop;
117
+ }
118
+ __defineClassHostBinding(name, className, privateName) {
119
+ Object.defineProperty(this, name, {
120
+ get: () => Boolean(Reflect.get(this, privateName)),
121
+ set: (v) => {
122
+ Reflect.set(this, privateName, v);
123
+ if (v) {
124
+ this.classList.add(className);
125
+ }
126
+ else {
127
+ this.classList.remove(className);
128
+ }
129
+ },
130
+ enumerable: true,
131
+ configurable: true
132
+ });
133
+ }
134
+ __setMarkerConfigs(entries) {
135
+ this._markerConfigEntries = entries;
98
136
  }
99
137
  __initializeMarkers(MarkerManagerClass) {
100
138
  this._MarkerManagerClass = MarkerManagerClass;
101
139
  }
102
140
  __initializeMarkersInternal() {
103
- if (!this._markerConfigJson || !this._MarkerManagerClass)
141
+ if (!this._markerConfigEntries || !this._MarkerManagerClass)
104
142
  return;
105
143
  this._markerManager = new this._MarkerManagerClass(this, this._shadowRoot);
106
- this._markerManager.initializeFromConfig(this._markerConfigJson);
107
- }
108
- __getElement(id) {
109
- return this._shadowRoot.querySelector(`[data-lid="${id}"]`);
110
- }
111
- __setText(id, text) {
112
- const el = this.__getElement(id);
113
- if (el)
114
- el.textContent = text;
115
- }
116
- __bindText(id, getter) {
117
- const el = this.__getElement(id);
118
- if (!el)
119
- return;
120
- const expr = el.getAttribute('data-text-bind') ?? '';
121
- const propMatch = /this\.([a-zA-Z_][a-zA-Z0-9_]*)/.exec(expr);
122
- const update = () => {
123
- try {
124
- el.textContent = getter();
125
- }
126
- catch {
127
- el.textContent = '';
128
- }
129
- };
130
- if (propMatch) {
131
- const [, propName] = propMatch;
132
- const reactiveProp = this.__getReactiveProp(propName);
133
- if (reactiveProp) {
134
- this.__bindPropertyChange(reactiveProp, update);
135
- }
136
- }
137
- update();
138
- }
139
- __setProperty(id, prop, value) {
140
- const el = this.__getElement(id);
141
- if (this.isHTMLElement(el)) {
142
- if (prop in el) {
143
- Reflect.set(el, prop, value);
144
- }
145
- else {
146
- el.setAttribute(prop, String(value));
147
- }
148
- }
149
- }
150
- __addClass(id, className) {
151
- const el = this.__getElement(id);
152
- if (this.isHTMLElement(el))
153
- el.classList.add(className);
154
- }
155
- __removeClass(id, className) {
156
- const el = this.__getElement(id);
157
- if (this.isHTMLElement(el))
158
- el.classList.remove(className);
159
- }
160
- __bindEvent(id, event, handler) {
161
- const el = this.__getElement(id);
162
- if (el) {
163
- el.addEventListener(event, handler);
164
- }
165
- }
166
- __bindPropertyChange(prop, callback) {
167
- const sub = prop.onChange.subscribe(callback);
168
- this._subscriptions.push(sub);
169
- const currentVal = prop.getValue();
170
- if (currentVal !== null) {
171
- callback(currentVal);
172
- }
173
- }
174
- __connectProperties(source, target) {
175
- const sub = source.onChange.subscribe((val) => {
176
- target.setValue(val, true);
177
- });
178
- this._subscriptions.push(sub);
179
- const currentVal = source.getValue();
180
- if (currentVal !== null) {
181
- target.setValue(currentVal, true);
182
- }
183
- }
184
- __connectOutput(source, handler) {
185
- const sub = source.subscribe(handler);
186
- this._subscriptions.push(sub);
187
- }
188
- __bindOutput(id, outputName, handler) {
189
- const el = this.__getElement(id);
190
- if (el)
191
- this.__bindOutputOnElement(el, outputName, handler);
144
+ this._markerManager.initializeFromConfig(this._markerConfigEntries);
192
145
  }
193
146
  __setChildProperty(el, propName, value) {
194
147
  if (el instanceof HTMLElement && el.hasAttribute('x-fluff-component')) {
@@ -202,47 +155,6 @@ export class FluffElement extends FluffBase {
202
155
  }
203
156
  super.__setChildProperty(el, propName, value);
204
157
  }
205
- __bindToChild(id, propName, value) {
206
- const el = this.__getElement(id);
207
- if (!el)
208
- return;
209
- this.__setChildPropertyDeferred(el, propName, value);
210
- }
211
- __setChildPropertyDeferred(el, propName, value) {
212
- if (Reflect.get(el, propName) !== undefined) {
213
- this.__setChildProperty(el, propName, value);
214
- return;
215
- }
216
- if (DomUtils.isCustomElement(el)) {
217
- this.__whenDefined(el.tagName.toLowerCase(), () => {
218
- this.__setChildProperty(el, propName, value);
219
- });
220
- }
221
- else {
222
- this.__setChildProperty(el, propName, value);
223
- }
224
- }
225
- __bindOutputOnElement(el, outputName, handler) {
226
- const maybeOutput = Reflect.get(el, outputName);
227
- if (maybeOutput instanceof Publisher) {
228
- this.__connectOutput(maybeOutput, handler);
229
- return;
230
- }
231
- if (DomUtils.isCustomElement(el)) {
232
- this.__whenDefined(el.tagName.toLowerCase(), () => {
233
- const innerOutput = Reflect.get(el, outputName);
234
- if (innerOutput instanceof Publisher) {
235
- this.__connectOutput(innerOutput, handler);
236
- }
237
- else {
238
- el.addEventListener(outputName, handler);
239
- }
240
- });
241
- }
242
- else {
243
- el.addEventListener(outputName, handler);
244
- }
245
- }
246
158
  __getReactivePropFromScope(propName, scope) {
247
159
  const key = `__${propName}`;
248
160
  if (key in scope.host) {
@@ -286,20 +198,7 @@ export class FluffElement extends FluffBase {
286
198
  }
287
199
  Reflect.deleteProperty(this, '__pendingProps');
288
200
  }
289
- isHTMLElement(el) {
290
- return el !== null;
291
- }
292
201
  isRecord(value) {
293
202
  return value !== null && typeof value === 'object' && !Array.isArray(value);
294
203
  }
295
- __getReactiveProp(propName) {
296
- const key = `__${propName}`;
297
- if (key in this) {
298
- const candidate = Reflect.get(this, key);
299
- if (candidate instanceof Property) {
300
- return candidate;
301
- }
302
- }
303
- return undefined;
304
- }
305
204
  }
@@ -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
  }