@oscarpalmer/abydon 0.23.1 → 0.24.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.
package/src/fragments.ts CHANGED
@@ -1,47 +1,92 @@
1
1
  import {getString} from '@oscarpalmer/atoms/string';
2
- import {array, type ReactiveArray} from '@oscarpalmer/mora';
2
+ import {array, isReactiveArray, type ReactiveArray} from '@oscarpalmer/mora';
3
3
  import {
4
- ERROR_FRAGMENT,
5
- ERROR_IDENTIFIER_DUPLICATE,
6
- ERROR_IDENTIFIER_TYPE,
4
+ MESSAGE_FRAGMENTS_FRAGMENT_RESULT,
5
+ MESSAGE_FRAGMENTS_FRAGMENT_TYPE,
6
+ MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_DUPLICATE,
7
+ MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_TYPE,
8
+ MESSAGE_FRAGMENTS_IDENTIFIER_TYPE,
9
+ MESSAGE_FRAGMENTS_VALUE,
7
10
  NAME_FRAGMENTS,
11
+ SYMBOL,
8
12
  TEMPLATE_ITEM,
9
13
  } from './constants';
10
- import type {Fragment} from './fragment';
11
- import {isFragment, isFragments} from './helpers';
12
- import type {FragmentsState} from './models';
13
-
14
- export class Fragments {
15
- readonly #state: FragmentsState;
16
-
17
- constructor(
18
- items: ReactiveArray<unknown>,
19
- identify: (item: unknown) => unknown,
20
- fragment: (item: unknown) => Fragment,
21
- ) {
22
- Object.defineProperty(this, NAME_FRAGMENTS, {
23
- value: true,
24
- });
14
+ import {isFragment} from './helpers';
15
+ import type {Fragment, Fragments, FragmentsState, InternalFragments} from './models';
16
+
17
+ // #region Instances
18
+
19
+ function Fragments(
20
+ this: any,
21
+ items: ReactiveArray<unknown>,
22
+ identify: (item: unknown) => unknown,
23
+ fragment: (item: unknown) => Fragment,
24
+ ) {
25
+ this[SYMBOL] = {
26
+ fragment,
27
+ identify,
28
+ array: items,
29
+ instances: {},
30
+ mapped: array<Fragment>([]),
31
+ name: NAME_FRAGMENTS,
32
+ subscription: undefined,
33
+ };
34
+
35
+ initializeFragments.call(this);
36
+ }
25
37
 
26
- this.#state = {
27
- fragment,
28
- identify,
29
- array: items,
30
- instances: {},
31
- mapped: array<Fragment>([]),
32
- subscriber: undefined,
33
- };
38
+ Fragments.prototype.remove = removeFragments;
39
+
40
+ // #endregion
41
+
42
+ // #region Functions
43
+
44
+ /**
45
+ * Create a _Fragments_ instance from a reactive array
46
+ *
47
+ * _A Fragments instance can be used to efficiently render a list of items that may change over time, using unique identifiers to track each item, only adding, removing, or updating each related Fragment._
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const fruits = array(['Apple', 'Banana', 'Cherry']);
52
+ * const items = fragments(
53
+ * fruits,
54
+ * fruit => fruit, // Identifies a unique item
55
+ * fruit => html`<p>${fruit}</p>`, // Creates a Fragment from an item
56
+ * );
57
+ * html`${items}`.appendTo(document.body) // Renders '<p>Apple</p><p>Banana</p><p>Cherry</p>'
58
+ * fruits.push(['Date', 'Elderberry', 'Fig']); // Appends '<p>Date</p><p>Elderberry</p><p>Fig</p>'
59
+ * // without re-rendering the existing Fragments
60
+ * ```
61
+ *
62
+ * @param array Reactive array
63
+ * @param identify Function to identify item uniquely _(non-nullable)_
64
+ * @param fragment Function to create _Fragment_ from item
65
+ * @returns _Fragments_
66
+ */
67
+ export function fragments<Item>(
68
+ array: ReactiveArray<Item>,
69
+ identify: (item: Item) => unknown,
70
+ fragment: (item: Item) => Fragment,
71
+ ): Fragments {
72
+ if (!isReactiveArray(array)) {
73
+ throw new TypeError(MESSAGE_FRAGMENTS_VALUE);
74
+ }
34
75
 
35
- fragmentsStates.set(this, this.#state);
76
+ if (typeof identify !== 'function') {
77
+ throw new TypeError(MESSAGE_FRAGMENTS_IDENTIFIER_TYPE);
78
+ }
36
79
 
37
- initializeFragments(this.#state);
80
+ if (typeof fragment !== 'function') {
81
+ throw new TypeError(MESSAGE_FRAGMENTS_FRAGMENT_TYPE);
38
82
  }
39
- }
40
83
 
41
- export function handleFragments(item: Fragments | FragmentsState, remove: boolean): void {
42
- const state = isFragments(item) ? fragmentsStates.get(item) : item;
84
+ // @ts-expect-error All good, no worries :-)
85
+ return new Fragments(array as ReactiveArray<unknown>, identify as never, fragment as never);
86
+ }
43
87
 
44
- (remove ? removeFragments : initializeFragments)(state!);
88
+ export function handleFragments(instance: Fragments, remove: boolean): void {
89
+ (remove ? removeFragments : initializeFragments).call(instance as InternalFragments);
45
90
  }
46
91
 
47
92
  function handleItems(state: FragmentsState, items: unknown[]): void {
@@ -55,13 +100,13 @@ function handleItems(state: FragmentsState, items: unknown[]): void {
55
100
  const identifier = state.identify(item);
56
101
 
57
102
  if (identifier == null) {
58
- throw new TypeError(ERROR_IDENTIFIER_TYPE);
103
+ throw new TypeError(MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_TYPE);
59
104
  }
60
105
 
61
106
  const key = getString(identifier);
62
107
 
63
108
  if (keys.has(key)) {
64
- throw new Error(ERROR_IDENTIFIER_DUPLICATE.replace(TEMPLATE_ITEM, key));
109
+ throw new Error(MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_DUPLICATE.replace(TEMPLATE_ITEM, key));
65
110
  }
66
111
 
67
112
  let instance = state.instances[key];
@@ -70,7 +115,7 @@ function handleItems(state: FragmentsState, items: unknown[]): void {
70
115
  instance = state.fragment(item);
71
116
 
72
117
  if (!isFragment(instance)) {
73
- throw new Error(ERROR_FRAGMENT);
118
+ throw new Error(MESSAGE_FRAGMENTS_FRAGMENT_RESULT);
74
119
  }
75
120
  }
76
121
 
@@ -90,18 +135,24 @@ function handleItems(state: FragmentsState, items: unknown[]): void {
90
135
  updateFragments(state, keys);
91
136
  }
92
137
 
93
- function initializeFragments(state: FragmentsState): void {
94
- state.subscriber ??= state.array.subscribe(items => {
138
+ function initializeFragments(this: InternalFragments): void {
139
+ const state = this[SYMBOL];
140
+
141
+ state.subscription ??= state.array.subscribe(items => {
95
142
  handleItems(state, items);
96
143
  });
97
144
  }
98
145
 
99
- function removeFragments(state: FragmentsState): void {
100
- state.subscriber?.();
146
+ function removeFragments(this: InternalFragments): void {
147
+ const state = this[SYMBOL];
148
+
149
+ state.subscription?.unsubscribe();
150
+
151
+ state.mapped.set([]);
101
152
 
102
153
  updateFragments(state);
103
154
 
104
- state.subscriber = undefined;
155
+ state.subscription = undefined;
105
156
  }
106
157
 
107
158
  function updateFragments(state: FragmentsState, active?: Set<string>): void {
@@ -116,7 +167,7 @@ function updateFragments(state: FragmentsState, active?: Set<string>): void {
116
167
 
117
168
  if (active?.has(key)) {
118
169
  next[key] = previous[key];
119
- } else {
170
+ } else if (active == null) {
120
171
  previous[key].remove();
121
172
  }
122
173
  }
@@ -126,6 +177,4 @@ function updateFragments(state: FragmentsState, active?: Set<string>): void {
126
177
  active?.clear();
127
178
  }
128
179
 
129
- //
130
-
131
- export const fragmentsStates: WeakMap<Fragments, FragmentsState> = new WeakMap();
180
+ // #endregion
@@ -2,6 +2,8 @@ import {getString} from '@oscarpalmer/atoms/string';
2
2
  import {isChildNode} from '@oscarpalmer/toretto/is';
3
3
  import {isFragment} from './index';
4
4
 
5
+ // #region Functions
6
+
5
7
  export function createNodes(value: unknown): ChildNode[] {
6
8
  if (isFragment(value)) {
7
9
  return value.get() as ChildNode[];
@@ -33,3 +35,5 @@ export function replaceNodes(from: ChildNode[], to: ChildNode[]): ChildNode[] {
33
35
 
34
36
  return to;
35
37
  }
38
+
39
+ // #endregion
@@ -6,10 +6,11 @@ import {
6
6
  ARRAY_COMPARISON_REMOVED,
7
7
  NAME_FRAGMENT,
8
8
  NAME_FRAGMENTS,
9
+ SYMBOL,
9
10
  } from '../constants';
10
- import type {Fragment} from '../fragment';
11
- import type {Fragments} from '../fragments';
12
- import type {FragmentData} from '../models';
11
+ import type {Fragment, Fragments, FragmentState} from '../models';
12
+
13
+ // #region Functions
13
14
 
14
15
  export function compareArrays(
15
16
  first: unknown[],
@@ -31,6 +32,7 @@ export function compareArrays(
31
32
 
32
33
  /**
33
34
  * Is the value a _Fragment_?
35
+ *
34
36
  * @param value Value to check
35
37
  * @returns `true` if the value is a _Fragment_, otherwise `false`
36
38
  */
@@ -40,6 +42,7 @@ export function isFragment(value: unknown): value is Fragment {
40
42
 
41
43
  /**
42
44
  * Is the value a _Fragments_ instance?
45
+ *
43
46
  * @param value Value to check
44
47
  * @returns `true` if the value is a _Fragments_ instance, otherwise `false`
45
48
  */
@@ -50,14 +53,14 @@ export function isFragments(value: unknown): value is Fragments {
50
53
  function isNamed(value: unknown, name: string): boolean {
51
54
  return (
52
55
  typeof value === 'object' &&
53
- value != null &&
54
- name in value &&
55
- (value as PlainObject)[name] === true
56
+ value !== null &&
57
+ SYMBOL in value &&
58
+ (value[SYMBOL] as PlainObject).name === name
56
59
  );
57
60
  }
58
61
 
59
62
  export function setComputedValue(
60
- data: FragmentData,
63
+ data: FragmentState,
61
64
  callback: GenericCallback,
62
65
  after: (computation: Computed<unknown>) => void,
63
66
  ): void {
@@ -67,3 +70,5 @@ export function setComputedValue(
67
70
 
68
71
  after(computation);
69
72
  }
73
+
74
+ // #endregion
package/src/index.ts CHANGED
@@ -1,71 +1,7 @@
1
1
  import '@oscarpalmer/mora';
2
- import {isArray, type ReactiveArray} from '@oscarpalmer/mora';
3
- import {Fragment} from './fragment';
4
- import {Fragments} from './fragments';
5
-
6
- /**
7
- * Create a _Fragments_ instance from a reactive array
8
- *
9
- * _A Fragments instance can be used to efficiently render a list of items that may change over time, using unique identifiers to track each item, only adding, removing, or updating each related Fragment._
10
- *
11
- * @example
12
- * ```ts
13
- * const fruits = array(['Apple', 'Banana', 'Cherry']);
14
- * const items = fragments(
15
- * fruits,
16
- * fruit => fruit, // Identifies a unique item
17
- * fruit => html`<p>${fruit}</p>`, // Creates a Fragment from an item
18
- * );
19
- * html`${items}`.appendTo(document.body) // Renders '<p>Apple</p><p>Banana</p><p>Cherry</p>'
20
- * fruits.push(['Date', 'Elderberry', 'Fig']); // Appends '<p>Date</p><p>Elderberry</p><p>Fig</p>'
21
- * // without re-rendering the existing Fragments
22
- * ```
23
- *
24
- * @param array Reactive array
25
- * @param identify Function to identify item uniquely _(non-nullable)_
26
- * @param fragment Function to create _Fragment_ from item
27
- * @returns _Fragments_
28
- */
29
- export function fragments<Item>(
30
- array: ReactiveArray<Item>,
31
- identify: (item: Item) => unknown,
32
- fragment: (item: Item) => Fragment,
33
- ): Fragments {
34
- if (!isArray(array)) {
35
- throw new TypeError('Fragments array must be a reactive array');
36
- }
37
-
38
- if (typeof identify !== 'function') {
39
- throw new TypeError('Fragments identify must be a function');
40
- }
41
-
42
- if (typeof fragment !== 'function') {
43
- throw new TypeError('Fragments fragment must be a function');
44
- }
45
-
46
- return new Fragments(array as ReactiveArray<unknown>, identify as never, fragment as never);
47
- }
48
-
49
- /**
50
- * Create a _Fragment_ from a template
51
- *
52
- * _A Fragment can be used to efficiently render a template that may change over time, only updating the necessary parts of the DOM._
53
- *
54
- * @example
55
- * ```ts
56
- * const name = signal('World');
57
- * const fragment = html`<p>Hello, ${name}!</p>`;
58
- * fragment.appendTo(document.body); // Renders '<p>Hello, World!</p>'
59
- * name.set('Alice'); // Replaces 'World' with 'Alice'
60
- * ```
61
- *
62
- * @returns _Fragment_
63
- */
64
- export function html(template: TemplateStringsArray, ...values: unknown[]): Fragment {
65
- return new Fragment(template, values);
66
- }
67
2
 
68
3
  export * from '@oscarpalmer/mora';
69
- export type {Fragment} from './fragment';
70
- export type {Fragments} from './fragments';
4
+ export {fragment, html} from './fragment';
5
+ export {fragments} from './fragments';
71
6
  export {isFragment, isFragments} from './helpers';
7
+ export type {Fragment, Fragments} from './models';
package/src/models.ts CHANGED
@@ -1,5 +1,80 @@
1
- import type {Reactive, ReactiveArray, Unsubscribe} from '@oscarpalmer/mora';
2
- import type {Fragment} from './fragment';
1
+ import type {Reactive, ReactiveArray, Subscription} from '@oscarpalmer/mora';
2
+ import type {SYMBOL} from './constants';
3
+
4
+ // #region Types
5
+
6
+ export type InternalFragment = {
7
+ [SYMBOL]: FragmentState;
8
+ } & Fragment;
9
+
10
+ export type InternalFragments = {
11
+ [SYMBOL]: FragmentsState;
12
+ } & Fragments;
13
+
14
+ export type Fragment = {
15
+ /**
16
+ * Is template caching enabled?
17
+ */
18
+ get cache(): boolean;
19
+
20
+ /**
21
+ * Identifier for the _Fragment_
22
+ *
23
+ * _An identifier can be used to uniquely identify a Fragment, which helps prevent re-rendering in reactive arrays and Fragments_
24
+ */
25
+ get identifier(): unknown;
26
+
27
+ /**
28
+ * Insert the _Fragment_ after the given element
29
+ *
30
+ * @param element Element to insert after
31
+ */
32
+ after(element: Element): void;
33
+
34
+ /**
35
+ * Append the _Fragment_ to the given element
36
+ *
37
+ * @param element Element to append to
38
+ */
39
+ appendTo(element: Element): void;
40
+
41
+ /**
42
+ * Insert the _Fragment_ before the given element
43
+ *
44
+ * @param element Element to insert before
45
+ */
46
+ before(element: Element): void;
47
+
48
+ /**
49
+ * Configure the _Fragment_
50
+ *
51
+ * @param configuration Configuration options
52
+ * @returns _Fragment_
53
+ */
54
+ configure(configuration: FragmentConfiguration): Fragment;
55
+
56
+ /**
57
+ * Get a list of the _Fragment_'s nodes
58
+ *
59
+ * @returns List of nodes
60
+ */
61
+ get(): ChildNode[];
62
+
63
+ /**
64
+ * Prepend the _Fragment_ to the given element
65
+ *
66
+ * @param element Element to prepend to
67
+ */
68
+ prependTo(element: Element): void;
69
+
70
+ /**
71
+ * Remove the _Fragment_ _(and all its descendants)_ from the _DOM_
72
+ *
73
+ * - _Any events, reactive values, and Fragments will also be cleaned up and removed_
74
+ * - _After being removed, the Fragment can be re-inserted into the DOM_
75
+ */
76
+ remove(): void;
77
+ };
3
78
 
4
79
  /**
5
80
  * Configuration for a _Fragment_
@@ -17,19 +92,29 @@ export type FragmentConfiguration = {
17
92
  identifier?: unknown;
18
93
  };
19
94
 
20
- export type FragmentData = {
95
+ export type FragmentItem = {
96
+ fragments?: Fragment[];
97
+ nodes?: ChildNode[];
98
+ text?: Text;
99
+ };
100
+
101
+ export type FragmentState = {
102
+ cache: boolean;
21
103
  expressions: unknown[];
104
+ identifier: unknown;
22
105
  items: FragmentItem[];
23
106
  mora: MoraData;
107
+ name: string;
24
108
  strings: TemplateStringsArray | string[];
25
109
  template?: string;
26
110
  values: unknown[];
27
111
  };
28
112
 
29
- export type FragmentItem = {
30
- fragments?: Fragment[];
31
- nodes?: ChildNode[];
32
- text?: Text;
113
+ export type Fragments = {
114
+ /**
115
+ * Remove the _Fragments_ _(and all its descendants)_ from the _DOM_, including any events, reactive values, and Fragments
116
+ */
117
+ remove(): void;
33
118
  };
34
119
 
35
120
  export type FragmentsState = {
@@ -38,10 +123,13 @@ export type FragmentsState = {
38
123
  identify: (item: unknown) => unknown;
39
124
  instances: Record<string, Fragment>;
40
125
  mapped: ReactiveArray<Fragment>;
41
- subscriber: Unsubscribe | undefined;
126
+ name: string;
127
+ subscription: Subscription | undefined;
42
128
  };
43
129
 
44
130
  type MoraData = {
45
- subscribers: Set<() => void>;
131
+ subscriptions: Set<Subscription>;
46
132
  values: Set<Reactive<unknown>>;
47
133
  };
134
+
135
+ // #endregion
package/src/node/event.ts CHANGED
@@ -13,6 +13,8 @@ import {
13
13
  EXPRESSION_EVENT_OPTIONS_ONCE,
14
14
  } from '../constants';
15
15
 
16
+ // #region Functions
17
+
16
18
  function getOptions(options: string): AddEventListenerOptions {
17
19
  const parts = options.split(EVENT_OPTIONS_DELIMITER);
18
20
 
@@ -53,3 +55,5 @@ export function mapEvent(element: HTMLElement | SVGElement, name: string, value:
53
55
  }
54
56
  }
55
57
  }
58
+
59
+ // #endregion
package/src/node/index.ts CHANGED
@@ -1,15 +1,17 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {isReactive, type ReactiveArray} from '@oscarpalmer/mora';
2
+ import {isReactive} from '@oscarpalmer/mora';
3
3
  import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
4
4
  import {mapAttributes, mapAttributeValue} from '../attribute/index';
5
- import {EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE} from '../constants';
6
- import {Fragments, fragmentsStates, handleFragments} from '../fragments';
5
+ import {EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE, SYMBOL} from '../constants';
6
+ import {handleFragments} from '../fragments';
7
7
  import {isFragment, isFragments, setComputedValue} from '../helpers';
8
8
  import {createNodes} from '../helpers/dom';
9
- import type {FragmentData} from '../models';
9
+ import type {Fragments, FragmentState, InternalFragments} from '../models';
10
10
  import {setReactiveValue} from './value';
11
11
 
12
- function mapNode(data: FragmentData, comment: Comment): void {
12
+ // #region Functions
13
+
14
+ function mapNode(data: FragmentState, comment: Comment): void {
13
15
  const matches = EXPRESSION_ABYDON_CONTENT.exec(comment.textContent);
14
16
  const value = matches == null ? null : data.values[+matches[1]];
15
17
 
@@ -18,7 +20,7 @@ function mapNode(data: FragmentData, comment: Comment): void {
18
20
  }
19
21
  }
20
22
 
21
- export function mapNodes(data: FragmentData, nodes: ChildNode[]): void {
23
+ export function mapNodes(data: FragmentState, nodes: ChildNode[]): void {
22
24
  const {length} = nodes;
23
25
 
24
26
  for (let index = 0; index < length; index += 1) {
@@ -49,7 +51,7 @@ export function mapNodes(data: FragmentData, nodes: ChildNode[]): void {
49
51
  }
50
52
  }
51
53
 
52
- function mapTextarea(data: FragmentData, element: HTMLTextAreaElement): boolean {
54
+ function mapTextarea(data: FragmentState, element: HTMLTextAreaElement): boolean {
53
55
  const [, index] =
54
56
  EXPRESSION_TEXTAREA_VALUE.exec(element.textContent) ??
55
57
  EXPRESSION_TEXTAREA_VALUE.exec(element.value) ??
@@ -67,7 +69,7 @@ function mapTextarea(data: FragmentData, element: HTMLTextAreaElement): boolean
67
69
  return true;
68
70
  }
69
71
 
70
- function mapValue(data: FragmentData, comment: Comment, value: unknown): void {
72
+ function mapValue(data: FragmentState, comment: Comment, value: unknown): void {
71
73
  switch (true) {
72
74
  case typeof value === 'function':
73
75
  setComputedNode(data, comment, value as GenericCallback);
@@ -87,7 +89,7 @@ function mapValue(data: FragmentData, comment: Comment, value: unknown): void {
87
89
  }
88
90
  }
89
91
 
90
- function replaceComment(data: FragmentData, comment: Comment, value: unknown): void {
92
+ function replaceComment(data: FragmentState, comment: Comment, value: unknown): void {
91
93
  const item = data.items.find(item => item.nodes?.includes(comment));
92
94
  const nodes = createNodes(value);
93
95
 
@@ -99,16 +101,16 @@ function replaceComment(data: FragmentData, comment: Comment, value: unknown): v
99
101
  comment.replaceWith(...nodes);
100
102
  }
101
103
 
102
- function setComputedNode(data: FragmentData, comment: Comment, callback: GenericCallback): void {
104
+ function setComputedNode(data: FragmentState, comment: Comment, callback: GenericCallback): void {
103
105
  setComputedValue(data, callback, computation => {
104
106
  setReactiveValue(data, comment, computation);
105
107
  });
106
108
  }
107
109
 
108
- function setFragmentsNode(data: FragmentData, comment: Comment, fragments: Fragments): void {
109
- const state = fragmentsStates.get(fragments)!;
110
-
111
- handleFragments(state, false);
110
+ function setFragmentsNode(data: FragmentState, comment: Comment, fragments: Fragments): void {
111
+ handleFragments(fragments, false);
112
112
 
113
- setReactiveValue(data, comment, state.mapped as ReactiveArray<unknown>);
113
+ setReactiveValue(data, comment, (fragments as InternalFragments)[SYMBOL].mapped);
114
114
  }
115
+
116
+ // #endregion
package/src/node/value.ts CHANGED
@@ -3,12 +3,11 @@ import {getString} from '@oscarpalmer/atoms/string';
3
3
  import type {Reactive} from '@oscarpalmer/mora';
4
4
  import {isChildNode} from '@oscarpalmer/toretto/is';
5
5
  import {ARRAY_COMPARISON_ADDED, ARRAY_COMPARISON_REMOVED} from '../constants';
6
- import type {Fragment} from '../fragment';
7
6
  import {compareArrays, isFragment} from '../helpers';
8
7
  import {createNodes, replaceNodes} from '../helpers/dom';
9
- import type {FragmentData, FragmentItem} from '../models';
8
+ import type {Fragment, FragmentItem, FragmentState} from '../models';
10
9
 
11
- //
10
+ // #region Types
12
11
 
13
12
  type ArrayData = {
14
13
  next: ArrayDataIdentifiers;
@@ -45,7 +44,9 @@ type ExtendedItems = {
45
44
  next: Fragment[];
46
45
  } & BaseItems;
47
46
 
48
- //
47
+ // #endregion
48
+
49
+ // #region Functions
49
50
 
50
51
  function addToArray(
51
52
  identifiers: Identifiers,
@@ -183,6 +184,7 @@ function setArray(item: FragmentItem, comment: Comment, value: unknown[]): Parti
183
184
  if (
184
185
  template.empty ||
185
186
  item.nodes == null ||
187
+ item.fragments == null ||
186
188
  previous.array.some(identifier => identifier == null)
187
189
  ) {
188
190
  const fragments = (item.fragments ?? []).slice();
@@ -208,7 +210,7 @@ function setArray(item: FragmentItem, comment: Comment, value: unknown[]): Parti
208
210
  previous,
209
211
  },
210
212
  {
211
- fragments: item.fragments ?? [],
213
+ fragments: item.fragments,
212
214
  templates: template.items,
213
215
  },
214
216
  item.nodes,
@@ -220,7 +222,7 @@ function setNodes(item: FragmentItem, comment: Comment, next: ChildNode[]): Chil
220
222
  }
221
223
 
222
224
  export function setReactiveValue(
223
- data: FragmentData,
225
+ data: FragmentState,
224
226
  comment: Comment,
225
227
  reactive: Reactive<unknown>,
226
228
  ): void {
@@ -228,7 +230,7 @@ export function setReactiveValue(
228
230
 
229
231
  item ??= {};
230
232
 
231
- data.mora.subscribers.add(
233
+ data.mora.subscriptions.add(
232
234
  reactive.subscribe(value => {
233
235
  if (Array.isArray(value)) {
234
236
  setReactiveValueForArray(item, comment, value);
@@ -271,7 +273,7 @@ function setText(item: FragmentItem, comment: Comment, value?: unknown): ChildNo
271
273
 
272
274
  const result = valueIsNullable ? [comment] : [item.text];
273
275
 
274
- replaceNodes(item.nodes ?? [comment], result);
275
-
276
- return result;
276
+ return replaceNodes(item.nodes ?? [comment], result);
277
277
  }
278
+
279
+ // #endregion
package/src/parse.ts CHANGED
@@ -4,9 +4,11 @@ import {
4
4
  EXPRESSION_EVENT_ATTRIBUTE,
5
5
  WHITESPACE,
6
6
  } from './constants';
7
- import type {FragmentData} from './models';
7
+ import type {FragmentState} from './models';
8
8
 
9
- function handleExpression(data: FragmentData, prefix: string, expression: unknown): string {
9
+ // #region Functions
10
+
11
+ function handleExpression(data: FragmentState, prefix: string, expression: unknown): string {
10
12
  if (Array.isArray(expression)) {
11
13
  if (EXPRESSION_EVENT_ATTRIBUTE.test(prefix.split(WHITESPACE).at(-1)!)) {
12
14
  return transformExpression(prefix, data.values.push(expression) - 1);
@@ -32,7 +34,7 @@ function handleExpression(data: FragmentData, prefix: string, expression: unknow
32
34
  return asString.trim().length === 0 ? prefix : `${prefix}${asString}`;
33
35
  }
34
36
 
35
- export function parse(data: FragmentData): string {
37
+ export function parse(data: FragmentState): string {
36
38
  if (data.template != null) {
37
39
  return data.template;
38
40
  }
@@ -60,3 +62,5 @@ function transformAttribute(_: string, name: string, index: string): string {
60
62
  function transformExpression(prefix: string, index: number): string {
61
63
  return `${prefix}<!--abydon.${index}-->`;
62
64
  }
65
+
66
+ // #endregion