@oscarpalmer/abydon 0.4.0 → 0.5.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/bun.lockb CHANGED
Binary file
package/dist/index.js CHANGED
@@ -34,28 +34,156 @@ function getNodes(node) {
34
34
  return /^documentfragment$/i.test(node.constructor.name) ? [...node.childNodes] : [node];
35
35
  }
36
36
 
37
+ // node_modules/@oscarpalmer/sentinel/node_modules/@oscarpalmer/atoms/dist/js/queue.mjs
38
+ if (globalThis._atomic_queued == null) {
39
+ const queued = new Set;
40
+ Object.defineProperty(globalThis, "_atomic_queued", {
41
+ get() {
42
+ return queued;
43
+ }
44
+ });
45
+ }
46
+
47
+ // node_modules/@oscarpalmer/sentinel/dist/global.mjs
48
+ if (globalThis._sentinels == null) {
49
+ const effects = [];
50
+ Object.defineProperty(globalThis, "_sentinels", {
51
+ get() {
52
+ return effects;
53
+ }
54
+ });
55
+ }
56
+
57
+ // node_modules/@oscarpalmer/sentinel/dist/helpers/is.mjs
58
+ var isReactive = function(value) {
59
+ return isSentinel(value, /^array|computed|signal|store$/i);
60
+ };
61
+ var isSentinel = function(value, expression) {
62
+ return expression.test(value?.$sentinel ?? "");
63
+ };
64
+
65
+ // node_modules/@oscarpalmer/sentinel/dist/helpers/value.mjs
66
+ var arrayOperations = new Set([
67
+ "copyWithin",
68
+ "fill",
69
+ "pop",
70
+ "push",
71
+ "reverse",
72
+ "shift",
73
+ "sort",
74
+ "splice",
75
+ "unshift"
76
+ ]);
77
+
78
+ // node_modules/@oscarpalmer/sentinel/dist/reactive/index.mjs
79
+ var primitives = new Set(["boolean", "number", "string"]);
80
+
81
+ // src/node/index.ts
82
+ var mapNode = function(data, comment) {
83
+ const matches = commentExpression.exec(comment.textContent ?? "");
84
+ const value10 = matches == null ? null : data.values[+matches[1]];
85
+ if (value10 != null) {
86
+ mapValue(comment, value10);
87
+ }
88
+ };
89
+ function mapNodes(data, nodes) {
90
+ const { length } = nodes;
91
+ for (let index = 0;index < length; index += 1) {
92
+ const node = nodes[index];
93
+ if (node instanceof Comment) {
94
+ mapNode(data, node);
95
+ continue;
96
+ }
97
+ if (node instanceof Element) {
98
+ }
99
+ if (node.hasChildNodes()) {
100
+ mapNodes(data, [...node.childNodes]);
101
+ }
102
+ }
103
+ }
104
+ var mapReactive = function(comment, value10) {
105
+ };
106
+ var mapValue = function(comment, value10) {
107
+ switch (true) {
108
+ case value10 instanceof Node:
109
+ comment.replaceWith(value10);
110
+ return;
111
+ case isFragment(value10):
112
+ comment.replaceWith(value10.get());
113
+ break;
114
+ case isReactive(value10):
115
+ mapReactive(comment, value10);
116
+ break;
117
+ case typeof value10 === "function":
118
+ mapValue(comment, value10());
119
+ return;
120
+ case typeof value10 === "object":
121
+ comment.replaceWith(new Text(getString(value10)));
122
+ break;
123
+ }
124
+ };
125
+ var commentExpression = /^abydon\.(\d+)$/;
126
+
37
127
  // src/fragment.ts
38
128
  function createFragment(strings, expressions) {
39
129
  const data = {
40
130
  expressions,
41
131
  strings,
42
- nodes: []
132
+ nodes: [],
133
+ values: []
43
134
  };
44
135
  const instance = Object.create({
45
- append(parent) {
46
- if (data.nodes.length > 0) {
47
- return parent.append(...data.nodes);
136
+ get() {
137
+ if (data.nodes.length === 0) {
138
+ const parsed = parse(data);
139
+ const templated = createTemplate(parsed);
140
+ data.nodes.splice(0, data.nodes.length, ...getNodes(templated));
141
+ mapNodes(data, data.nodes);
142
+ }
143
+ return getFragment(data.nodes);
144
+ },
145
+ remove() {
146
+ const { length } = data.nodes;
147
+ for (let index = 0;index < length; index += 1) {
148
+ const node2 = data.nodes[index];
149
+ node2.parentNode?.removeChild(node2);
48
150
  }
49
- const parsed = parse(data);
50
- const templated = createTemplate(parsed);
51
- data.nodes.splice(0, data.nodes.length, ...getNodes(templated));
52
- parent.append(...data.nodes);
151
+ data.nodes.splice(0, data.nodes.length);
53
152
  }
54
153
  });
154
+ Object.defineProperty(instance, "$fragment", {
155
+ value: true
156
+ });
55
157
  return instance;
56
158
  }
159
+ function isFragment(value10) {
160
+ return typeof value10 === "object" && value10 != null && "$fragment" in value10;
161
+ }
57
162
 
58
163
  // src/html.ts
164
+ function getFragment(nodes) {
165
+ const fragment3 = document.createDocumentFragment();
166
+ fragment3.append(...nodes);
167
+ return fragment3;
168
+ }
169
+ var handleExpression = function(data, prefix, expression) {
170
+ if (isNullableOrWhitespace(expression)) {
171
+ return prefix;
172
+ }
173
+ if (Array.isArray(expression)) {
174
+ const { length } = expression;
175
+ let expressions = "";
176
+ for (let index = 0;index < length; index += 1) {
177
+ expressions += handleExpression(data, "", expression[index]);
178
+ }
179
+ return `${prefix}${expressions}`;
180
+ }
181
+ if (typeof expression === "object") {
182
+ const index = data.values.push(expression) - 1;
183
+ return `${prefix}<!--abydon.${index}-->`;
184
+ }
185
+ return `${prefix}${expression}`;
186
+ };
59
187
  function html2(strings, ...values) {
60
188
  return createFragment(strings, values);
61
189
  }
@@ -63,9 +191,7 @@ function parse(data) {
63
191
  const { length } = data.strings;
64
192
  let template = "";
65
193
  for (let index = 0;index < length; index += 1) {
66
- const part = data.strings[index];
67
- const expression = data.expressions[index];
68
- template += isNullableOrWhitespace(expression) ? part : `${part}${expression}`;
194
+ template += handleExpression(data, data.strings[index], data.expressions[index]);
69
195
  }
70
196
  return template;
71
197
  }
package/package.json CHANGED
@@ -4,7 +4,8 @@
4
4
  "url": "https://oscarpalmer.se"
5
5
  },
6
6
  "dependencies": {
7
- "@oscarpalmer/atoms": "^0.61.0"
7
+ "@oscarpalmer/atoms": "^0.61.0",
8
+ "@oscarpalmer/sentinel": "^0.22.0"
8
9
  },
9
10
  "devDependencies": {
10
11
  "@biomejs/biome": "^1.8.3",
@@ -35,5 +36,5 @@
35
36
  "watch": "bun build ./src/index.ts --outfile ./dist/index.js --watch"
36
37
  },
37
38
  "types": "src/types.d.ts",
38
- "version": "0.4.0"
39
+ "version": "0.5.0"
39
40
  }
package/src/fragment.ts CHANGED
@@ -1,14 +1,17 @@
1
1
  import {createTemplate, getNodes} from './helpers/dom';
2
- import {parse} from './html';
2
+ import {getFragment, parse} from './html';
3
+ import {mapNodes} from './node';
3
4
 
4
5
  export type Fragment = {
5
- append(parent: Node): void;
6
+ get(): DocumentFragment;
7
+ remove(): void;
6
8
  };
7
9
 
8
10
  export type FragmentData = {
9
11
  expressions: unknown[];
10
12
  nodes: Node[];
11
13
  strings: TemplateStringsArray;
14
+ values: unknown[];
12
15
  };
13
16
 
14
17
  export function createFragment(
@@ -19,22 +22,42 @@ export function createFragment(
19
22
  expressions,
20
23
  strings,
21
24
  nodes: [],
25
+ values: [],
22
26
  };
23
27
 
24
28
  const instance = Object.create({
25
- append(parent: ParentNode) {
26
- if (data.nodes.length > 0) {
27
- return parent.append(...data.nodes);
29
+ get(): Node {
30
+ if (data.nodes.length === 0) {
31
+ const parsed = parse(data);
32
+ const templated = createTemplate(parsed);
33
+
34
+ data.nodes.splice(0, data.nodes.length, ...getNodes(templated));
35
+
36
+ mapNodes(data, data.nodes);
28
37
  }
29
38
 
30
- const parsed = parse(data);
31
- const templated = createTemplate(parsed);
39
+ return getFragment(data.nodes);
40
+ },
41
+ remove(): void {
42
+ const {length} = data.nodes;
32
43
 
33
- data.nodes.splice(0, data.nodes.length, ...getNodes(templated));
44
+ for (let index = 0; index < length; index += 1) {
45
+ const node = data.nodes[index];
46
+
47
+ node.parentNode?.removeChild(node);
48
+ }
34
49
 
35
- parent.append(...data.nodes);
50
+ data.nodes.splice(0, data.nodes.length);
36
51
  },
37
52
  });
38
53
 
54
+ Object.defineProperty(instance, '$fragment', {
55
+ value: true,
56
+ });
57
+
39
58
  return instance;
40
59
  }
60
+
61
+ export function isFragment(value: unknown): value is Fragment {
62
+ return typeof value === 'object' && value != null && '$fragment' in value;
63
+ }
package/src/html.ts CHANGED
@@ -1,5 +1,43 @@
1
1
  import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
2
- import {createFragment, type FragmentData, type Fragment} from './fragment';
2
+ import {type Fragment, type FragmentData, createFragment} from './fragment';
3
+
4
+ export function getFragment(nodes: Node[]): DocumentFragment {
5
+ const fragment = document.createDocumentFragment();
6
+
7
+ fragment.append(...nodes);
8
+
9
+ return fragment;
10
+ }
11
+
12
+ function handleExpression(
13
+ data: FragmentData,
14
+ prefix: string,
15
+ expression: unknown,
16
+ ): string {
17
+ if (isNullableOrWhitespace(expression)) {
18
+ return prefix;
19
+ }
20
+
21
+ if (Array.isArray(expression)) {
22
+ const {length} = expression;
23
+
24
+ let expressions = '';
25
+
26
+ for (let index = 0; index < length; index += 1) {
27
+ expressions += handleExpression(data, '', expression[index]);
28
+ }
29
+
30
+ return `${prefix}${expressions}`;
31
+ }
32
+
33
+ if (typeof expression === 'object') {
34
+ const index = data.values.push(expression) - 1;
35
+
36
+ return `${prefix}<!--abydon.${index}-->`;
37
+ }
38
+
39
+ return `${prefix}${expression}`;
40
+ }
3
41
 
4
42
  export function html(
5
43
  strings: TemplateStringsArray,
@@ -14,12 +52,11 @@ export function parse(data: FragmentData): string {
14
52
  let template = '';
15
53
 
16
54
  for (let index = 0; index < length; index += 1) {
17
- const part = data.strings[index];
18
- const expression = data.expressions[index];
19
-
20
- template += isNullableOrWhitespace(expression)
21
- ? part
22
- : `${part}${expression}`;
55
+ template += handleExpression(
56
+ data,
57
+ data.strings[index],
58
+ data.expressions[index],
59
+ );
23
60
  }
24
61
 
25
62
  return template;
@@ -0,0 +1,64 @@
1
+ import {getString} from '@oscarpalmer/atoms/string';
2
+ import {isReactive, type Reactive} from '@oscarpalmer/sentinel';
3
+ import {type FragmentData, isFragment} from '../fragment';
4
+
5
+ const commentExpression = /^abydon\.(\d+)$/;
6
+
7
+ function mapNode(data: FragmentData, comment: Comment): void {
8
+ const matches = commentExpression.exec(comment.textContent ?? '');
9
+ const value = matches == null ? null : data.values[+matches[1]];
10
+
11
+ if (value != null) {
12
+ mapValue(comment, value);
13
+ }
14
+ }
15
+
16
+ export function mapNodes(data: FragmentData, nodes: Node[]): void {
17
+ const {length} = nodes;
18
+
19
+ for (let index = 0; index < length; index += 1) {
20
+ const node = nodes[index];
21
+
22
+ if (node instanceof Comment) {
23
+ mapNode(data, node);
24
+
25
+ continue;
26
+ }
27
+
28
+ if (node instanceof Element) {
29
+ // Attributes
30
+ }
31
+
32
+ if (node.hasChildNodes()) {
33
+ mapNodes(data, [...node.childNodes]);
34
+ }
35
+ }
36
+ }
37
+
38
+ function mapReactive(comment: Comment, value: Reactive): void {
39
+ // ?
40
+ }
41
+
42
+ function mapValue(comment: Comment, value: unknown): void {
43
+ switch (true) {
44
+ case value instanceof Node:
45
+ comment.replaceWith(value);
46
+ return;
47
+
48
+ case isFragment(value):
49
+ comment.replaceWith(value.get());
50
+ break;
51
+
52
+ case isReactive(value):
53
+ mapReactive(comment, value);
54
+ break;
55
+
56
+ case typeof value === 'function':
57
+ mapValue(comment, value());
58
+ return;
59
+
60
+ case typeof value === 'object':
61
+ comment.replaceWith(new Text(getString(value)));
62
+ break;
63
+ }
64
+ }
@@ -1,9 +1,12 @@
1
1
  export type Fragment = {
2
- append(parent: Node): void;
2
+ get(): DocumentFragment;
3
+ remove(): void;
3
4
  };
4
5
  export type FragmentData = {
5
6
  expressions: unknown[];
6
7
  nodes: Node[];
7
8
  strings: TemplateStringsArray;
9
+ values: unknown[];
8
10
  };
9
11
  export declare function createFragment(strings: TemplateStringsArray, expressions: unknown[]): Fragment;
12
+ export declare function isFragment(value: unknown): value is Fragment;
package/types/html.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- import { type FragmentData, type Fragment } from './fragment';
1
+ import { type Fragment, type FragmentData } from './fragment';
2
+ export declare function getFragment(nodes: Node[]): DocumentFragment;
2
3
  export declare function html(strings: TemplateStringsArray, ...values: unknown[]): Fragment;
3
4
  export declare function parse(data: FragmentData): string;
@@ -0,0 +1,2 @@
1
+ import { type FragmentData } from '../fragment';
2
+ export declare function mapNodes(data: FragmentData, nodes: Node[]): void;