@oscarpalmer/abydon 0.12.0 → 0.14.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 (48) hide show
  1. package/dist/abydon.full.js +1153 -0
  2. package/dist/constants.js +10 -0
  3. package/dist/fragment.js +60 -0
  4. package/dist/helpers/dom.js +28 -0
  5. package/dist/helpers/index.js +11 -0
  6. package/dist/index.js +6 -911
  7. package/dist/node/attribute/index.js +39 -0
  8. package/dist/node/attribute/value.js +56 -0
  9. package/dist/node/event.js +30 -0
  10. package/dist/node/index.js +52 -0
  11. package/dist/node/value.js +108 -0
  12. package/dist/parse.js +18 -0
  13. package/package.json +38 -27
  14. package/src/constants.ts +20 -0
  15. package/src/fragment.ts +63 -36
  16. package/src/helpers/dom.ts +5 -4
  17. package/src/helpers/index.ts +0 -9
  18. package/src/index.ts +13 -3
  19. package/src/models.ts +14 -11
  20. package/src/node/attribute/index.ts +28 -22
  21. package/src/node/attribute/value.ts +38 -42
  22. package/src/node/event.ts +12 -13
  23. package/src/node/index.ts +7 -7
  24. package/src/node/value.ts +169 -122
  25. package/src/{html.ts → parse.ts} +15 -17
  26. package/types/constants.d.ts +9 -0
  27. package/types/fragment.d.ts +3 -5
  28. package/types/helpers/index.d.ts +0 -3
  29. package/types/index.d.ts +4 -2
  30. package/types/models.d.ts +7 -9
  31. package/types/node/attribute/index.d.ts +2 -1
  32. package/types/node/attribute/value.d.ts +2 -1
  33. package/types/node/event.d.ts +1 -1
  34. package/types/node/value.d.ts +2 -2
  35. package/types/parse.d.ts +2 -0
  36. package/dist/fragment.mjs +0 -75
  37. package/dist/helpers/dom.mjs +0 -44
  38. package/dist/helpers/index.mjs +0 -25
  39. package/dist/html.mjs +0 -36
  40. package/dist/index.mjs +0 -6
  41. package/dist/node/attribute/index.mjs +0 -40
  42. package/dist/node/attribute/value.mjs +0 -89
  43. package/dist/node/event.mjs +0 -38
  44. package/dist/node/index.mjs +0 -60
  45. package/dist/node/value.mjs +0 -123
  46. package/types/html.d.ts +0 -4
  47. package/types/index.d.cts +0 -26
  48. /package/dist/{models.mjs → models.js} +0 -0
package/src/models.ts CHANGED
@@ -1,24 +1,27 @@
1
- import type {Key} from '@oscarpalmer/atoms/models';
2
- import type {Effect, Reactive} from '@oscarpalmer/sentinel';
1
+ import type {Reactive} from '@oscarpalmer/mora';
3
2
  import type {Fragment} from './fragment';
4
3
 
4
+ export type FragmentConfiguration = {
5
+ identifier?: unknown;
6
+ ignoreCache?: boolean;
7
+ };
8
+
5
9
  export type FragmentData = {
6
10
  expressions: unknown[];
7
- identifier?: Key;
8
11
  items: FragmentItem[];
9
- sentinel: FragmentDataSentinel;
12
+ mora: MoraData;
10
13
  strings: TemplateStringsArray;
14
+ template?: string;
11
15
  values: unknown[];
12
16
  };
13
17
 
14
- type FragmentDataSentinel = {
15
- effects: Set<Effect>;
16
- values: Set<Reactive>;
17
- };
18
-
19
18
  export type FragmentItem = {
20
19
  fragments?: Fragment[];
21
- nodes: ChildNode[];
20
+ nodes?: ChildNode[];
21
+ text?: Text;
22
22
  };
23
23
 
24
- export type HTMLOrSVGElement = HTMLElement | SVGElement;
24
+ type MoraData = {
25
+ subscribers: Set<() => void>;
26
+ values: Set<Reactive<unknown>>;
27
+ };
@@ -1,13 +1,14 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {computed, isReactive} from '@oscarpalmer/sentinel';
3
- import type {FragmentData, HTMLOrSVGElement} from '../../models';
2
+ import {computed, isReactive} from '@oscarpalmer/mora';
3
+ import {isBooleanAttribute, setProperty} from '@oscarpalmer/toretto/attribute';
4
+ import type {HTMLOrSVGElement} from '@oscarpalmer/toretto/models';
5
+ import {EXPRESSION_COMMENT_FULL} from '../../constants';
6
+ import type {FragmentData} from '../../models';
4
7
  import {mapEvent} from '../event';
5
8
  import {setAttribute} from './value';
6
9
 
7
- const commentExpression = /^<!--abydon\.(\d+)-->$/;
8
-
9
10
  function getValue(data: FragmentData, original: string): unknown {
10
- const matches = commentExpression.exec(original ?? '');
11
+ const matches = EXPRESSION_COMMENT_FULL.exec(original ?? '');
11
12
 
12
13
  return matches == null ? original : data.values[+matches[1]];
13
14
  }
@@ -24,14 +25,23 @@ export function mapAttributes(
24
25
 
25
26
  const actual = getValue(data, value);
26
27
 
27
- if (name.startsWith('@')) {
28
- mapEvent(element, name, actual);
29
- } else if (
30
- name.includes('.') ||
31
- typeof value === 'function' ||
32
- isReactive(actual)
33
- ) {
34
- mapValue(data, element, name, actual);
28
+ switch (true) {
29
+ case name.startsWith('@'):
30
+ mapEvent(element, name, actual);
31
+ break;
32
+
33
+ case name.includes('.') ||
34
+ typeof actual === 'function' ||
35
+ isReactive(actual):
36
+ mapValue(data, element, name, actual);
37
+ break;
38
+
39
+ case isBooleanAttribute(name):
40
+ setProperty(element, name, value);
41
+ break;
42
+
43
+ default:
44
+ break;
35
45
  }
36
46
  }
37
47
  }
@@ -42,14 +52,10 @@ function mapValue(
42
52
  name: string,
43
53
  value: unknown,
44
54
  ): void {
45
- switch (true) {
46
- case typeof value === 'function':
47
- setComputedAttribute(data, element, name, value as GenericCallback);
48
- break;
49
-
50
- default:
51
- setAttribute(data, element, name, value);
52
- break;
55
+ if (typeof value === 'function') {
56
+ setComputedAttribute(data, element, name, value as GenericCallback);
57
+ } else {
58
+ setAttribute(data, element, name, value);
53
59
  }
54
60
  }
55
61
 
@@ -61,7 +67,7 @@ function setComputedAttribute(
61
67
  ): void {
62
68
  const value = computed(callback);
63
69
 
64
- data.sentinel.values.add(value);
70
+ data.mora.values.add(value);
65
71
 
66
72
  setAttribute(data, element, name, value);
67
73
  }
@@ -1,15 +1,18 @@
1
1
  import type {PlainObject} from '@oscarpalmer/atoms/models';
2
2
  import {getString} from '@oscarpalmer/atoms/string';
3
- import {effect, isReactive} from '@oscarpalmer/sentinel';
3
+ import {isReactive} from '@oscarpalmer/mora';
4
4
  import {
5
5
  isBooleanAttribute,
6
6
  setAttribute as setAttr,
7
7
  } from '@oscarpalmer/toretto/attribute';
8
- import type {FragmentData, HTMLOrSVGElement} from '../../models';
9
-
10
- const classExpression = /^class\./;
11
- const styleFullExpression = /^style\.([\w-]+)(?:\.([\w-]+))?$/;
12
- const stylePrefixExpression = /^style\./;
8
+ import type {HTMLOrSVGElement} from '@oscarpalmer/toretto/models';
9
+ import {
10
+ ATTRIBUTE_CLASS_PREFIX_LENGTH,
11
+ EXPRESSION_ATTRIBUTE_CLASS,
12
+ EXPRESSION_ATTRIBUTE_STYLE_FULL,
13
+ EXPRESSION_ATTRIBUTE_STYLE_PREFIX,
14
+ } from '../../constants';
15
+ import type {FragmentData} from '../../models';
13
16
 
14
17
  export function setAttribute(
15
18
  data: FragmentData,
@@ -20,11 +23,11 @@ export function setAttribute(
20
23
  element.removeAttribute(name);
21
24
 
22
25
  switch (true) {
23
- case classExpression.test(name):
26
+ case EXPRESSION_ATTRIBUTE_CLASS.test(name):
24
27
  setClasses(data, element, name, value);
25
28
  return;
26
29
 
27
- case stylePrefixExpression.test(name):
30
+ case EXPRESSION_ATTRIBUTE_STYLE_PREFIX.test(name):
28
31
  setStyle(data, element, name, value);
29
32
  return;
30
33
 
@@ -48,14 +51,10 @@ function setClasses(
48
51
  }
49
52
  }
50
53
 
51
- const classes = name.slice(6).split('.');
54
+ const classes = name.slice(ATTRIBUTE_CLASS_PREFIX_LENGTH).split('.');
52
55
 
53
56
  if (isReactive(value)) {
54
- data.sentinel.effects.add(
55
- effect(() => {
56
- update(value.get());
57
- }),
58
- );
57
+ data.mora.subscribers.add(value.subscribe(update));
59
58
  } else {
60
59
  update(value);
61
60
  }
@@ -67,6 +66,12 @@ function setStyle(
67
66
  name: string,
68
67
  value: unknown,
69
68
  ): void {
69
+ const [, property, unit] = EXPRESSION_ATTRIBUTE_STYLE_FULL.exec(name) ?? [];
70
+
71
+ if (property == null) {
72
+ return;
73
+ }
74
+
70
75
  function update(value: unknown): void {
71
76
  if (value == null || value === false || (value === true && unit == null)) {
72
77
  element.style.removeProperty(property);
@@ -78,18 +83,10 @@ function setStyle(
78
83
  }
79
84
  }
80
85
 
81
- const [, property, unit] = styleFullExpression.exec(name) ?? [];
82
-
83
- if (property != null) {
84
- if (isReactive(value)) {
85
- data.sentinel.effects.add(
86
- effect(() => {
87
- update(value.get());
88
- }),
89
- );
90
- } else {
91
- update(value);
92
- }
86
+ if (isReactive(value)) {
87
+ data.mora.subscribers.add(value.subscribe(update));
88
+ } else {
89
+ update(value);
93
90
  }
94
91
  }
95
92
 
@@ -99,17 +96,22 @@ function setValue(
99
96
  name: string,
100
97
  value: unknown,
101
98
  ): void {
102
- const callback =
103
- isBooleanAttribute(name) && name in element
104
- ? name === 'selected'
105
- ? updateSelected
106
- : updateProperty
107
- : setAttr;
99
+ let callback: (
100
+ element: HTMLOrSVGElement,
101
+ name: string,
102
+ value: unknown,
103
+ ) => void;
104
+
105
+ if (isBooleanAttribute(name) && name in element) {
106
+ callback = name === 'selected' ? updateSelected : updateProperty;
107
+ } else {
108
+ callback = setAttr;
109
+ }
108
110
 
109
111
  if (isReactive(value)) {
110
- data.sentinel.effects.add(
111
- effect(() => {
112
- callback(element, name, value.get());
112
+ data.mora.subscribers.add(
113
+ value.subscribe(next => {
114
+ callback(element, name, next);
113
115
  }),
114
116
  );
115
117
  } else {
@@ -117,12 +119,6 @@ function setValue(
117
119
  }
118
120
  }
119
121
 
120
- function triggerSelectChange(select: HTMLSelectElement): void {
121
- if (select != null) {
122
- select.dispatchEvent(new Event('change', {bubbles: true}));
123
- }
124
- }
125
-
126
122
  function updateProperty(
127
123
  element: HTMLOrSVGElement,
128
124
  name: string,
@@ -140,7 +136,7 @@ function updateSelected(
140
136
  const options = [...(select?.options ?? [])];
141
137
 
142
138
  if (select != null && options.includes(element as HTMLOptionElement)) {
143
- triggerSelectChange(select);
139
+ select.dispatchEvent(new Event('change', {bubbles: true}));
144
140
  }
145
141
 
146
142
  updateProperty(element, name, value);
package/src/node/event.ts CHANGED
@@ -1,18 +1,17 @@
1
- import type {HTMLOrSVGElement} from '../models';
2
-
3
- const controllers = new WeakMap<HTMLOrSVGElement, AbortController>();
4
-
5
- const eventNamePattern = /^@([\w-]+)(?::([a-z:]+))?$/i;
6
-
7
- const reason = 'Event removed as element was removed from document by Abydon';
1
+ import type {HTMLOrSVGElement} from '@oscarpalmer/toretto/models';
2
+ import {
3
+ ABORT_CONTROLLERS,
4
+ EXPRESSION_EVENT_NAME,
5
+ REASON_EVENT_REMOVED,
6
+ } from '../constants';
8
7
 
9
8
  function getController(element: HTMLOrSVGElement): AbortController {
10
- let controller = controllers.get(element);
9
+ let controller = ABORT_CONTROLLERS.get(element);
11
10
 
12
11
  if (controller == null) {
13
12
  controller = new AbortController();
14
13
 
15
- controllers.set(element, controller);
14
+ ABORT_CONTROLLERS.set(element, controller);
16
15
  }
17
16
 
18
17
  return controller;
@@ -24,7 +23,7 @@ function getOptions(options: string): AddEventListenerOptions {
24
23
  return {
25
24
  capture: parts.includes('c') || parts.includes('capture'),
26
25
  once: parts.includes('o') || parts.includes('once'),
27
- passive: !parts.includes('a') && !parts.includes('active'),
26
+ passive: !(parts.includes('a') || parts.includes('active')),
28
27
  };
29
28
  }
30
29
 
@@ -35,7 +34,7 @@ export function mapEvent(
35
34
  ): void {
36
35
  element.removeAttribute(name);
37
36
 
38
- const [, type, options] = eventNamePattern.exec(name) ?? [];
37
+ const [, type, options] = EXPRESSION_EVENT_NAME.exec(name) ?? [];
39
38
 
40
39
  if (typeof value === 'function' && type != null) {
41
40
  element.addEventListener(type, value as EventListener, {
@@ -46,6 +45,6 @@ export function mapEvent(
46
45
  }
47
46
 
48
47
  export function removeEvents(element: HTMLOrSVGElement): void {
49
- controllers.get(element)?.abort(reason);
50
- controllers.delete(element);
48
+ ABORT_CONTROLLERS.get(element)?.abort(REASON_EVENT_REMOVED);
49
+ ABORT_CONTROLLERS.delete(element);
51
50
  }
package/src/node/index.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {computed, isReactive} from '@oscarpalmer/sentinel';
3
- import {isFragment, isHTMLOrSVGElement} from '../helpers';
2
+ import {computed, isReactive} from '@oscarpalmer/mora';
3
+ import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
4
+ import {EXPRESSION_COMMENT_CONTENT} from '../constants';
5
+ import {isFragment} from '../helpers';
4
6
  import {createNodes} from '../helpers/dom';
5
7
  import type {FragmentData} from '../models';
6
8
  import {mapAttributes} from './attribute';
7
9
  import {setReactiveValue} from './value';
8
10
 
9
- const commentExpression = /^abydon\.(\d+)$/;
10
-
11
11
  function mapNode(data: FragmentData, comment: Comment): void {
12
- const matches = commentExpression.exec(comment.textContent ?? '');
12
+ const matches = EXPRESSION_COMMENT_CONTENT.exec(comment.textContent ?? '');
13
13
  const value = matches == null ? null : data.values[+matches[1]];
14
14
 
15
15
  if (value != null) {
@@ -60,7 +60,7 @@ function replaceComment(
60
60
  comment: Comment,
61
61
  value: unknown,
62
62
  ): void {
63
- const item = data.items.find(item => item.nodes.includes(comment));
63
+ const item = data.items.find(item => item.nodes?.includes(comment));
64
64
  const nodes = createNodes(value);
65
65
 
66
66
  if (item != null) {
@@ -78,7 +78,7 @@ function setComputedValue(
78
78
  ): void {
79
79
  const value = computed(callback);
80
80
 
81
- data.sentinel.values.add(value);
81
+ data.mora.values.add(value);
82
82
 
83
83
  setReactiveValue(data, comment, value);
84
84
  }