@oscarpalmer/abydon 0.22.0 → 0.23.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
@@ -74,7 +74,9 @@ function handleItems(state: FragmentsState, items: unknown[]): void {
74
74
  }
75
75
  }
76
76
 
77
- instance.identify(key);
77
+ instance.configure({
78
+ identifier: key,
79
+ });
78
80
 
79
81
  state.instances[key] = instance;
80
82
 
@@ -88,7 +90,7 @@ function handleItems(state: FragmentsState, items: unknown[]): void {
88
90
  updateFragments(state, keys);
89
91
  }
90
92
 
91
- export function initializeFragments(state: FragmentsState): void {
93
+ function initializeFragments(state: FragmentsState): void {
92
94
  state.subscriber ??= state.array.subscribe(items => {
93
95
  handleItems(state, items);
94
96
  });
@@ -14,10 +14,6 @@ export function createNodes(value: unknown): ChildNode[] {
14
14
  return [new Text(getString(value))];
15
15
  }
16
16
 
17
- export function isInputElement(node: Node): node is HTMLInputElement | HTMLSelectElement {
18
- return node instanceof HTMLInputElement || node instanceof HTMLSelectElement;
19
- }
20
-
21
17
  export function removeNodes(nodes: ChildNode[]): void {
22
18
  const {length} = nodes;
23
19
 
@@ -26,7 +22,7 @@ export function removeNodes(nodes: ChildNode[]): void {
26
22
  }
27
23
  }
28
24
 
29
- export function replaceNodes(from: ChildNode[], to: ChildNode[]): void {
25
+ export function replaceNodes(from: ChildNode[], to: ChildNode[]): ChildNode[] {
30
26
  from[0]?.replaceWith(...to);
31
27
 
32
28
  const {length} = from;
@@ -34,4 +30,6 @@ export function replaceNodes(from: ChildNode[], to: ChildNode[]): void {
34
30
  for (let index = 1; index < length; index += 1) {
35
31
  from[index].remove();
36
32
  }
33
+
34
+ return to;
37
35
  }
@@ -1,4 +1,5 @@
1
- import type {PlainObject} from '@oscarpalmer/atoms/models';
1
+ import type {GenericCallback, PlainObject} from '@oscarpalmer/atoms/models';
2
+ import {computed, type Computed} from '@oscarpalmer/mora';
2
3
  import {
3
4
  ARRAY_COMPARISON_ADDED,
4
5
  ARRAY_COMPARISON_DISSIMILAR,
@@ -8,6 +9,7 @@ import {
8
9
  } from '../constants';
9
10
  import type {Fragment} from '../fragment';
10
11
  import type {Fragments} from '../fragments';
12
+ import type {FragmentData} from '../models';
11
13
 
12
14
  export function compareArrays(
13
15
  first: unknown[],
@@ -28,18 +30,18 @@ export function compareArrays(
28
30
  }
29
31
 
30
32
  /**
31
- * Is the value a Fragment?
33
+ * Is the value a _Fragment_?
32
34
  * @param value Value to check
33
- * @returns `true` if the value is a Fragment, otherwise `false`
35
+ * @returns `true` if the value is a _Fragment_, otherwise `false`
34
36
  */
35
37
  export function isFragment(value: unknown): value is Fragment {
36
38
  return isNamed(value, NAME_FRAGMENT);
37
39
  }
38
40
 
39
41
  /**
40
- * Is the value a Fragments?
42
+ * Is the value a _Fragments_ instance?
41
43
  * @param value Value to check
42
- * @returns `true` if the value is a Fragments, otherwise `false`
44
+ * @returns `true` if the value is a _Fragments_ instance, otherwise `false`
43
45
  */
44
46
  export function isFragments(value: unknown): value is Fragments {
45
47
  return isNamed(value, NAME_FRAGMENTS);
@@ -53,3 +55,15 @@ function isNamed(value: unknown, name: string): boolean {
53
55
  (value as PlainObject)[name] === true
54
56
  );
55
57
  }
58
+
59
+ export function setComputedValue(
60
+ data: FragmentData,
61
+ callback: GenericCallback,
62
+ after: (computation: Computed<unknown>) => void,
63
+ ): void {
64
+ const computation = computed(callback);
65
+
66
+ data.mora.values.add(computation);
67
+
68
+ after(computation);
69
+ }
package/src/index.ts CHANGED
@@ -4,11 +4,27 @@ import {Fragment} from './fragment';
4
4
  import {Fragments} from './fragments';
5
5
 
6
6
  /**
7
- * Create a Fragments from a reactive array
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
+ *
8
24
  * @param array Reactive array
9
- * @param identify Function to identify item
10
- * @param fragment Function to create fragment from item
11
- * @returns Fragments
25
+ * @param identify Function to identify item uniquely _(non-nullable)_
26
+ * @param fragment Function to create _Fragment_ from item
27
+ * @returns _Fragments_
12
28
  */
13
29
  export function fragments<Item>(
14
30
  array: ReactiveArray<Item>,
@@ -31,8 +47,19 @@ export function fragments<Item>(
31
47
  }
32
48
 
33
49
  /**
34
- * Create a Fragment from a template
35
- * @returns Fragment
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_
36
63
  */
37
64
  export function html(template: TemplateStringsArray, ...values: unknown[]): Fragment {
38
65
  return new Fragment(template, values);
package/src/models.ts CHANGED
@@ -1,16 +1,18 @@
1
1
  import type {Reactive, ReactiveArray, Unsubscribe} from '@oscarpalmer/mora';
2
2
  import type {Fragment} from './fragment';
3
3
 
4
+ /**
5
+ * Configuration for a _Fragment_
6
+ */
4
7
  export type FragmentConfiguration = {
5
8
  /**
6
9
  * Should the template be cached? _(defaults to `true`)_
7
10
  */
8
11
  cache?: boolean;
9
12
  /**
10
- * Identifier for the fragment
13
+ * Identifier for the _Fragment_
11
14
  *
12
- * _(An identifier can be used to uniquely identify a fragment,
13
- * which helps prevent re-rendering in certain scenarios)_
15
+ * _An identifier can be used to uniquely identify a Fragment, which helps prevent re-rendering in reactive arrays and Fragments_
14
16
  */
15
17
  identifier?: unknown;
16
18
  };
@@ -19,7 +21,7 @@ export type FragmentData = {
19
21
  expressions: unknown[];
20
22
  items: FragmentItem[];
21
23
  mora: MoraData;
22
- strings: TemplateStringsArray;
24
+ strings: TemplateStringsArray | string[];
23
25
  template?: string;
24
26
  values: unknown[];
25
27
  };
package/src/node/index.ts CHANGED
@@ -1,21 +1,12 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {getString} from '@oscarpalmer/atoms/string';
3
- import {
4
- computed,
5
- isComputed,
6
- isReactive,
7
- isSignal,
8
- type Computed,
9
- type ReactiveArray,
10
- } from '@oscarpalmer/mora';
2
+ import {isReactive, type ReactiveArray} from '@oscarpalmer/mora';
11
3
  import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
12
- import {mapAttributes} from '../attribute/index';
13
- import {EVENT_ON_VALUE, EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE} from '../constants';
4
+ import {mapAttributes, mapAttributeValue} from '../attribute/index';
5
+ import {EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE} from '../constants';
14
6
  import {Fragments, fragmentsStates, handleFragments} from '../fragments';
15
- import {isFragment, isFragments} from '../helpers';
7
+ import {isFragment, isFragments, setComputedValue} from '../helpers';
16
8
  import {createNodes} from '../helpers/dom';
17
9
  import type {FragmentData} from '../models';
18
- import {mapEvent} from './event';
19
10
  import {setReactiveValue} from './value';
20
11
 
21
12
  function mapNode(data: FragmentData, comment: Comment): void {
@@ -39,12 +30,17 @@ export function mapNodes(data: FragmentData, nodes: ChildNode[]): void {
39
30
  continue;
40
31
  }
41
32
 
42
- if (node instanceof HTMLTextAreaElement) {
43
- mapTextarea(data, node);
44
- }
45
-
46
33
  if (isHTMLOrSVGElement(node)) {
47
- mapAttributes(data, node);
34
+ let ignoreValueAttribute = false;
35
+
36
+ if (node instanceof HTMLTextAreaElement) {
37
+ // Textareas are a special case because their value can be set via the `value` property
38
+ // or the text content. In order to support both, we need to check for the presence of
39
+ // the expression in both places and map it accordingly.
40
+ ignoreValueAttribute = mapTextarea(data, node);
41
+ }
42
+
43
+ mapAttributes(data, node, ignoreValueAttribute);
48
44
  }
49
45
 
50
46
  if (node.hasChildNodes()) {
@@ -53,64 +49,32 @@ export function mapNodes(data: FragmentData, nodes: ChildNode[]): void {
53
49
  }
54
50
  }
55
51
 
56
- function mapTextarea(data: FragmentData, element: HTMLTextAreaElement): void {
52
+ function mapTextarea(data: FragmentData, element: HTMLTextAreaElement): boolean {
57
53
  const [, index] =
58
54
  EXPRESSION_TEXTAREA_VALUE.exec(element.textContent) ??
59
55
  EXPRESSION_TEXTAREA_VALUE.exec(element.value) ??
60
56
  [];
61
57
 
62
58
  if (index == null) {
63
- return;
59
+ return false;
64
60
  }
65
61
 
66
62
  element.textContent = '';
67
63
  element.value = '';
68
64
 
69
- const value = data.values[Number.parseInt(index, 10)];
65
+ mapAttributeValue(data, element, 'value', data.values[Number.parseInt(index, 10)]);
70
66
 
71
- if (isSignal(value)) {
72
- element.value = getString(value.peek());
73
-
74
- mapEvent(element, EVENT_ON_VALUE, () => {
75
- value.set(element.value);
76
- });
77
-
78
- data.mora.subscribers.add(
79
- value.subscribe(value => {
80
- element.value = getString(value);
81
- }),
82
- );
83
-
84
- return;
85
- }
86
-
87
- let reactive: Computed<unknown> | undefined;
88
-
89
- if (typeof value === 'function') {
90
- reactive = computed(value as GenericCallback);
91
- } else if (isComputed(value)) {
92
- reactive = value;
93
- }
94
-
95
- if (reactive == null) {
96
- element.value = '';
97
- } else {
98
- data.mora.subscribers.add(
99
- reactive.subscribe(value => {
100
- element.value = getString(value);
101
- }),
102
- );
103
- }
67
+ return true;
104
68
  }
105
69
 
106
70
  function mapValue(data: FragmentData, comment: Comment, value: unknown): void {
107
71
  switch (true) {
108
72
  case typeof value === 'function':
109
- setComputedValue(data, comment, value as GenericCallback);
73
+ setComputedNode(data, comment, value as GenericCallback);
110
74
  break;
111
75
 
112
76
  case isFragments(value):
113
- setFragmentsValue(data, comment, value);
77
+ setFragmentsNode(data, comment, value);
114
78
  break;
115
79
 
116
80
  case isReactive(value):
@@ -135,15 +99,13 @@ function replaceComment(data: FragmentData, comment: Comment, value: unknown): v
135
99
  comment.replaceWith(...nodes);
136
100
  }
137
101
 
138
- function setComputedValue(data: FragmentData, comment: Comment, callback: GenericCallback): void {
139
- const value = computed(callback);
140
-
141
- data.mora.values.add(value);
142
-
143
- setReactiveValue(data, comment, value);
102
+ function setComputedNode(data: FragmentData, comment: Comment, callback: GenericCallback): void {
103
+ setComputedValue(data, callback, computation => {
104
+ setReactiveValue(data, comment, computation);
105
+ });
144
106
  }
145
107
 
146
- function setFragmentsValue(data: FragmentData, comment: Comment, fragments: Fragments): void {
108
+ function setFragmentsNode(data: FragmentData, comment: Comment, fragments: Fragments): void {
147
109
  const state = fragmentsStates.get(fragments)!;
148
110
 
149
111
  handleFragments(state, false);
package/src/node/value.ts CHANGED
@@ -10,9 +10,30 @@ import type {FragmentData, FragmentItem} from '../models';
10
10
 
11
11
  //
12
12
 
13
+ type ArrayData = {
14
+ next: ArrayDataIdentifiers;
15
+ previous: ArrayDataIdentifiers;
16
+ template: ArrayDataTemplate;
17
+ };
18
+
19
+ type ArrayDataIdentifiers = {
20
+ array: unknown[];
21
+ set: Set<unknown>;
22
+ };
23
+
24
+ type ArrayDataTemplate = {
25
+ empty: boolean;
26
+ items: Fragment[];
27
+ };
28
+
13
29
  type Identifiers = {
14
- next: Set<unknown>;
15
- previous: Set<unknown>;
30
+ next: IdentifiersValues;
31
+ previous: IdentifiersValues;
32
+ };
33
+
34
+ type IdentifiersValues = {
35
+ array: unknown[];
36
+ set: Set<unknown>;
16
37
  };
17
38
 
18
39
  type BaseItems = {
@@ -34,7 +55,7 @@ function addToArray(
34
55
  ): void {
35
56
  let position = nodes[0];
36
57
 
37
- const before = added && !identifiers.previous.has(items.templates[0].identifier);
58
+ const before = added && !identifiers.previous.set.has(items.templates[0].identifier);
38
59
 
39
60
  const next = items.next.flatMap(fragment =>
40
61
  fragment.get().flatMap(node => ({
@@ -48,7 +69,7 @@ function addToArray(
48
69
  for (let index = 0; index < length; index += 1) {
49
70
  const node = next[index];
50
71
 
51
- if (!(added && identifiers.previous.has(node.identifier))) {
72
+ if (!(added && identifiers.previous.set.has(node.identifier))) {
52
73
  if (index === 0 && before) {
53
74
  position.before(node.value);
54
75
  } else {
@@ -60,6 +81,46 @@ function addToArray(
60
81
  }
61
82
  }
62
83
 
84
+ function getArrayData(item: FragmentItem, values: unknown[]): ArrayData {
85
+ const {length} = values;
86
+
87
+ const next: unknown[] = [];
88
+
89
+ let templates: Fragment[] = [];
90
+
91
+ for (let index = 0; index < length; index += 1) {
92
+ const value = values[index];
93
+
94
+ if (isFragment(value) && value.identifier != null) {
95
+ next.push(value.identifier);
96
+ templates.push(value);
97
+ }
98
+ }
99
+
100
+ const previous = item.fragments?.map(fragment => fragment.identifier) ?? [];
101
+
102
+ const nextSet = new Set(next);
103
+
104
+ if (nextSet.size !== templates.length) {
105
+ templates = [];
106
+ }
107
+
108
+ return {
109
+ next: {
110
+ array: next,
111
+ set: nextSet,
112
+ },
113
+ previous: {
114
+ array: previous,
115
+ set: new Set(previous),
116
+ },
117
+ template: {
118
+ empty: templates.length === 0,
119
+ items: templates,
120
+ },
121
+ };
122
+ }
123
+
63
124
  function handleArray(
64
125
  identifiers: Identifiers,
65
126
  items: BaseItems,
@@ -67,17 +128,18 @@ function handleArray(
67
128
  ): Partial<FragmentItem> {
68
129
  const next = items.templates.map(
69
130
  template =>
70
- items.fragments?.find(fragment => fragment.identifier === template.identifier) ?? template,
131
+ items.fragments.find(fragment => fragment.identifier === template.identifier) ?? template,
71
132
  );
72
133
 
73
- const comparison = compareArrays(items.fragments ?? [], items.templates);
134
+ const comparison = compareArrays(identifiers.previous.array, identifiers.next.array);
74
135
 
75
136
  if (comparison !== ARRAY_COMPARISON_REMOVED) {
76
137
  addToArray(identifiers, {...items, next}, nodes, comparison === ARRAY_COMPARISON_ADDED);
77
138
  }
78
139
 
79
- const toRemove =
80
- items.fragments?.filter(fragment => !identifiers.next.has(fragment.identifier)) ?? [];
140
+ const toRemove = items.fragments.filter(
141
+ fragment => !identifiers.next.set.has(fragment.identifier),
142
+ );
81
143
 
82
144
  const {length} = toRemove;
83
145
 
@@ -91,84 +153,70 @@ function handleArray(
91
153
  };
92
154
  }
93
155
 
94
- function removeFragments(fragments: Fragment[] | undefined): void {
95
- if (fragments != null) {
96
- const {length} = fragments;
156
+ function removeArray(item: FragmentItem, comment: Comment): Partial<FragmentItem> {
157
+ const fragments = (item.fragments ?? []).slice();
97
158
 
98
- for (let index = 0; index < length; index += 1) {
99
- fragments[index].remove();
100
- }
101
- }
159
+ const result = {
160
+ nodes: setText(item, comment),
161
+ };
162
+
163
+ removeFragmentsItems(fragments);
164
+
165
+ return result;
102
166
  }
103
167
 
104
- function replaceText(item: FragmentItem, comment: Comment, isNullable: boolean): void {
105
- let to: ChildNode[];
168
+ function removeFragmentsItems(fragments: Fragment[]): void {
169
+ const {length} = fragments;
106
170
 
107
- if (isNullable) {
108
- to = [comment];
109
- } else {
110
- to = item.text == null ? [] : [item.text];
171
+ for (let index = 0; index < length; index += 1) {
172
+ fragments[index].remove();
111
173
  }
112
-
113
- replaceNodes(item.nodes ?? [], to);
114
174
  }
115
175
 
116
176
  function setArray(item: FragmentItem, comment: Comment, value: unknown[]): Partial<FragmentItem> {
117
177
  if (value.length === 0) {
118
- return {
119
- nodes: setText(item, comment, value),
120
- };
178
+ return removeArray(item, comment);
121
179
  }
122
180
 
123
- let templates = value.filter(item => isFragment(item) && item.identifier != null) as Fragment[];
181
+ const {next, previous, template} = getArrayData(item, value);
124
182
 
125
- const next = templates.map(fragment => fragment.identifier) as unknown[];
126
- const previous = item.fragments?.map(fragment => fragment.identifier) ?? [];
183
+ if (
184
+ template.empty ||
185
+ item.nodes == null ||
186
+ previous.array.some(identifier => identifier == null)
187
+ ) {
188
+ const fragments = (item.fragments ?? []).slice();
127
189
 
128
- const nextSet = new Set(next);
129
-
130
- if (nextSet.size !== templates.length) {
131
- templates = [];
132
- }
133
-
134
- const noTemplates = templates.length === 0;
135
-
136
- if (noTemplates || item.nodes == null || previous.some(identifier => identifier == null)) {
137
- return {
138
- fragments: noTemplates ? undefined : templates,
139
- nodes: setNodes(
140
- item,
141
- comment,
142
- noTemplates
190
+ const result = {
191
+ fragments: template.empty ? undefined : template.items,
192
+ nodes: replaceNodes(
193
+ item.nodes ?? [comment],
194
+ template.empty
143
195
  ? value.flatMap(item => createNodes(item))
144
- : templates.flatMap(template => template.get()),
196
+ : template.items.flatMap(template => template.get()),
145
197
  ),
146
198
  };
199
+
200
+ removeFragmentsItems(fragments);
201
+
202
+ return result;
147
203
  }
148
204
 
149
205
  return handleArray(
150
206
  {
151
- next: nextSet,
152
- previous: new Set(previous),
207
+ next,
208
+ previous,
153
209
  },
154
210
  {
155
- templates,
156
211
  fragments: item.fragments ?? [],
212
+ templates: template.items,
157
213
  },
158
214
  item.nodes,
159
215
  );
160
216
  }
161
217
 
162
218
  function setNodes(item: FragmentItem, comment: Comment, next: ChildNode[]): ChildNode[] {
163
- if (item.nodes == null) {
164
- replaceNodes([comment], next);
165
- } else {
166
- replaceNodes(item.nodes, next);
167
- }
168
-
169
- removeFragments(item.fragments);
170
-
171
- return next;
219
+ return replaceNodes(item.nodes ?? [comment], next);
172
220
  }
173
221
 
174
222
  export function setReactiveValue(
@@ -180,8 +228,6 @@ export function setReactiveValue(
180
228
 
181
229
  item ??= {};
182
230
 
183
- item.text = new Text();
184
-
185
231
  data.mora.subscribers.add(
186
232
  reactive.subscribe(value => {
187
233
  if (Array.isArray(value)) {
@@ -189,8 +235,6 @@ export function setReactiveValue(
189
235
  } else {
190
236
  setReactiveValueForSingle(item, comment, value);
191
237
  }
192
-
193
- item.nodes ??= [comment];
194
238
  }),
195
239
  );
196
240
  }
@@ -203,43 +247,31 @@ function setReactiveValueForArray(item: FragmentItem, comment: Comment, value: u
203
247
  }
204
248
 
205
249
  function setReactiveValueForSingle(item: FragmentItem, comment: Comment, value: unknown): void {
206
- const valueIsFragment = isFragment(value);
250
+ const fragments = (item.fragments ?? []).slice();
207
251
 
208
- item.fragments = valueIsFragment ? [value] : undefined;
252
+ const valueIsFragment = isFragment(value);
209
253
 
210
254
  if (valueIsFragment || isChildNode(value)) {
211
255
  item.nodes = setNodes(item, comment, createNodes(value));
212
256
  } else {
213
257
  item.nodes = setText(item, comment, value);
214
258
  }
215
- }
216
259
 
217
- function setText(item: FragmentItem, comment: Comment, value: unknown): ChildNode[] | undefined {
218
- const isNullable = isNullableOrWhitespace(value);
260
+ item.fragments = valueIsFragment ? [value] : undefined;
219
261
 
220
- if (item.text != null) {
221
- item.text.textContent = isNullable ? '' : getString(value);
222
- }
262
+ removeFragmentsItems(fragments);
263
+ }
223
264
 
224
- let result = false;
265
+ function setText(item: FragmentItem, comment: Comment, value?: unknown): ChildNode[] {
266
+ const valueIsNullable = isNullableOrWhitespace(value);
225
267
 
226
- if (item.nodes != null) {
227
- replaceText(item, comment, isNullable);
268
+ item.text ??= new Text();
228
269
 
229
- result = !isNullable;
230
- } else if (isNullable && comment.parentNode == null) {
231
- item.text?.replaceWith(comment);
232
- } else if (!isNullable && item?.text?.parentNode == null) {
233
- if (item.text != null) {
234
- comment.replaceWith(item.text);
235
- }
270
+ item.text.textContent = valueIsNullable ? '' : getString(value);
236
271
 
237
- result = true;
238
- }
272
+ const result = valueIsNullable ? [comment] : [item.text];
239
273
 
240
- removeFragments(item.fragments);
274
+ replaceNodes(item.nodes ?? [comment], result);
241
275
 
242
- if (result) {
243
- return item.text == null ? [] : [item.text];
244
- }
276
+ return result;
245
277
  }
package/src/parse.ts CHANGED
@@ -48,7 +48,7 @@ export function parse(data: FragmentData): string {
48
48
  data.template = data.template.replaceAll(EXPRESSION_ABYDON_ATTRIBUTE_FULL, transformAttribute);
49
49
 
50
50
  data.expressions = [];
51
- data.strings = [] as never;
51
+ data.strings = [];
52
52
 
53
53
  return data.template;
54
54
  }