@oscarpalmer/abydon 0.15.0 → 0.16.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/dist/abydon.full.js +799 -751
- package/dist/constants.js +20 -5
- package/dist/fragment.js +8 -10
- package/dist/fragments.js +3 -2
- package/dist/helpers/dom.js +1 -11
- package/dist/helpers/index.js +11 -4
- package/dist/index.js +2 -2
- package/dist/node/attribute/index.js +14 -14
- package/dist/node/attribute/value.js +19 -13
- package/dist/node/event.js +15 -22
- package/dist/node/index.js +2 -2
- package/dist/node/value.js +5 -5
- package/dist/parse.js +9 -1
- package/package.json +13 -15
- package/src/constants.ts +30 -10
- package/src/fragment.ts +12 -24
- package/src/fragments.ts +3 -5
- package/src/helpers/dom.ts +1 -20
- package/src/helpers/index.ts +18 -15
- package/src/index.ts +14 -10
- package/src/models.ts +10 -1
- package/src/node/attribute/index.ts +22 -21
- package/src/node/attribute/value.ts +44 -37
- package/src/node/event.ts +26 -33
- package/src/node/index.ts +6 -18
- package/src/node/value.ts +15 -46
- package/src/parse.ts +15 -17
- package/types/constants.d.ts +12 -4
- package/types/index.d.ts +12 -1
- package/types/models.d.ts +10 -1
- package/types/node/attribute/index.d.ts +1 -2
- package/types/node/attribute/value.d.ts +1 -2
- package/types/node/event.d.ts +1 -3
package/dist/constants.js
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
|
-
const ABORT_CONTROLLERS = /* @__PURE__ */ new WeakMap();
|
|
2
1
|
const ATTRIBUTE_CLASS_PREFIX_LENGTH = 6;
|
|
2
|
+
const EVENT_DEFAULTS = {
|
|
3
|
+
A: "click",
|
|
4
|
+
BUTTON: "click",
|
|
5
|
+
DETAILS: "toggle",
|
|
6
|
+
FORM: "submit",
|
|
7
|
+
SELECT: "change",
|
|
8
|
+
TEXTAREA: "input"
|
|
9
|
+
};
|
|
10
|
+
const EXPRESSION_ABYDON_ATTRIBUTE_FULL = /(@?\w+)="<!--abydon.(\d+)-->"/g;
|
|
11
|
+
const EXPRESSION_ABYDON_ATTRIBUTE_PREFIX = /^_/;
|
|
12
|
+
const EXPRESSION_ABYDON_CONTENT = /^@?abydon\.(\d+)@?$/;
|
|
3
13
|
const EXPRESSION_ATTRIBUTE_CLASS = /^class\./;
|
|
4
14
|
const EXPRESSION_ATTRIBUTE_STYLE_FULL = /^style\.([\w-]+)(?:\.([\w-]+))?$/;
|
|
5
15
|
const EXPRESSION_ATTRIBUTE_STYLE_PREFIX = /^style\./;
|
|
6
|
-
const
|
|
7
|
-
const EXPRESSION_COMMENT_CONTENT = /^abydon\.(\d+)$/;
|
|
16
|
+
const EXPRESSION_EVENT_CHANGE_TYPES = /^(checkbox|radio)$/;
|
|
8
17
|
const EXPRESSION_EVENT_NAME = /^@([\w-]+)(?::([a-z:]+))?$/i;
|
|
9
|
-
const
|
|
10
|
-
|
|
18
|
+
const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(ctive)$/i;
|
|
19
|
+
const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(apture)$/i;
|
|
20
|
+
const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(nce)$/i;
|
|
21
|
+
const EXPRESSION_EVENT_PREFIX = /^@/;
|
|
22
|
+
const EXPRESSION_PERIOD = /\./;
|
|
23
|
+
const NAME_FRAGMENT = "$fragment";
|
|
24
|
+
const NAME_FRAGMENTS = "$fragments";
|
|
25
|
+
export { ATTRIBUTE_CLASS_PREFIX_LENGTH, EVENT_DEFAULTS, EXPRESSION_ABYDON_ATTRIBUTE_FULL, EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, EXPRESSION_ABYDON_CONTENT, EXPRESSION_ATTRIBUTE_CLASS, EXPRESSION_ATTRIBUTE_STYLE_FULL, EXPRESSION_ATTRIBUTE_STYLE_PREFIX, EXPRESSION_EVENT_CHANGE_TYPES, EXPRESSION_EVENT_NAME, EXPRESSION_EVENT_OPTIONS_ACTIVE, EXPRESSION_EVENT_OPTIONS_CAPTURE, EXPRESSION_EVENT_OPTIONS_ONCE, EXPRESSION_EVENT_PREFIX, EXPRESSION_PERIOD, NAME_FRAGMENT, NAME_FRAGMENTS };
|
package/dist/fragment.js
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NAME_FRAGMENT } from "./constants.js";
|
|
2
2
|
import { isFragments } from "./helpers/index.js";
|
|
3
3
|
import { handleFragments } from "./fragments.js";
|
|
4
4
|
import { removeNodes } from "./helpers/dom.js";
|
|
5
5
|
import { mapNodes } from "./node/index.js";
|
|
6
|
+
import { parse } from "./parse.js";
|
|
6
7
|
import { isPlainObject } from "@oscarpalmer/atoms/is";
|
|
7
8
|
import { html } from "@oscarpalmer/toretto/html";
|
|
8
9
|
var Fragment = class {
|
|
9
10
|
#data;
|
|
10
11
|
#configuration = {
|
|
11
12
|
identifier: void 0,
|
|
12
|
-
|
|
13
|
+
cache: true
|
|
13
14
|
};
|
|
14
15
|
get identifier() {
|
|
15
16
|
return this.#configuration.identifier;
|
|
16
17
|
}
|
|
17
18
|
constructor(strings, expressions) {
|
|
18
|
-
Object.defineProperty(this,
|
|
19
|
+
Object.defineProperty(this, NAME_FRAGMENT, { value: true });
|
|
19
20
|
this.#data = {
|
|
20
21
|
expressions,
|
|
21
22
|
strings,
|
|
@@ -32,21 +33,18 @@ var Fragment = class {
|
|
|
32
33
|
}
|
|
33
34
|
configure(configuration) {
|
|
34
35
|
const actual = isPlainObject(configuration) ? configuration : {};
|
|
35
|
-
if (
|
|
36
|
-
if (typeof actual.
|
|
36
|
+
if ("identifier" in actual) this.#configuration.identifier = actual.identifier;
|
|
37
|
+
if (typeof actual.cache === "boolean") this.#configuration.cache = actual.cache;
|
|
37
38
|
return this;
|
|
38
39
|
}
|
|
39
40
|
get() {
|
|
40
41
|
const data = this.#data;
|
|
41
42
|
if (data.items.length === 0) {
|
|
42
|
-
const templated = html(parse(data), {
|
|
43
|
-
ignoreCache: this.#configuration.ignoreCache,
|
|
44
|
-
sanitizeBooleanAttributes: false
|
|
45
|
-
});
|
|
43
|
+
const templated = html(parse(data), { cache: this.#configuration.cache });
|
|
46
44
|
data.items.splice(0, data.items.length, ...templated.map((node) => ({ nodes: [node] })));
|
|
47
45
|
mapNodes(data, data.items.flatMap((item) => item.fragments?.flatMap((fragment) => fragment.get()) ?? item.nodes ?? []));
|
|
48
46
|
}
|
|
49
|
-
return
|
|
47
|
+
return data.items.flatMap((item) => item.fragments?.flatMap((fragment) => fragment.get()) ?? item.nodes ?? []);
|
|
50
48
|
}
|
|
51
49
|
identify(identifier) {
|
|
52
50
|
this.#configuration.identifier = identifier;
|
package/dist/fragments.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { NAME_FRAGMENTS } from "./constants.js";
|
|
1
2
|
import { isFragment, isFragments } from "./helpers/index.js";
|
|
2
|
-
import { array } from "@oscarpalmer/mora";
|
|
3
3
|
import { getString } from "@oscarpalmer/atoms/string";
|
|
4
|
+
import { array } from "@oscarpalmer/mora";
|
|
4
5
|
var Fragments = class {
|
|
5
6
|
#state;
|
|
6
7
|
get items() {
|
|
7
8
|
return this.#state.mapped;
|
|
8
9
|
}
|
|
9
10
|
constructor(items, identify, fragment) {
|
|
10
|
-
Object.defineProperty(this,
|
|
11
|
+
Object.defineProperty(this, NAME_FRAGMENTS, { value: true });
|
|
11
12
|
this.#state = {
|
|
12
13
|
fragment,
|
|
13
14
|
identify,
|
package/dist/helpers/dom.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { isFragment } from "./index.js";
|
|
2
|
-
import { removeEvents } from "../node/event.js";
|
|
3
2
|
import { getString } from "@oscarpalmer/atoms/string";
|
|
4
|
-
import { isChildNode
|
|
3
|
+
import { isChildNode } from "@oscarpalmer/toretto/is";
|
|
5
4
|
function createNodes(value) {
|
|
6
5
|
if (isFragment(value)) return value.get();
|
|
7
6
|
if (isChildNode(value)) return [value];
|
|
8
7
|
return [new Text(getString(value))];
|
|
9
8
|
}
|
|
10
9
|
function removeNodes(nodes) {
|
|
11
|
-
sanitizeNodes(nodes);
|
|
12
10
|
const { length } = nodes;
|
|
13
11
|
for (let index = 0; index < length; index += 1) nodes[index].remove();
|
|
14
12
|
}
|
|
@@ -17,12 +15,4 @@ function replaceNodes(from, to) {
|
|
|
17
15
|
const { length } = from;
|
|
18
16
|
for (let index = 1; index < length; index += 1) from[index].remove();
|
|
19
17
|
}
|
|
20
|
-
function sanitizeNodes(nodes) {
|
|
21
|
-
const { length } = nodes;
|
|
22
|
-
for (let index = 0; index < length; index += 1) {
|
|
23
|
-
const node = nodes[index];
|
|
24
|
-
if (isHTMLOrSVGElement(node)) removeEvents(node);
|
|
25
|
-
if (node.hasChildNodes()) sanitizeNodes([...node.childNodes]);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
18
|
export { createNodes, removeNodes, replaceNodes };
|
package/dist/helpers/index.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
+
import { NAME_FRAGMENT, NAME_FRAGMENTS } from "../constants.js";
|
|
1
2
|
function compareArrays(first, second) {
|
|
2
3
|
const firstIsLarger = first.length > second.length;
|
|
3
4
|
const from = firstIsLarger ? first : second;
|
|
4
5
|
const to = firstIsLarger ? second : first;
|
|
5
|
-
if (!from.filter((key) => to.includes(key)).every((key, index) => to[index] === key)) return
|
|
6
|
-
return firstIsLarger ?
|
|
6
|
+
if (!from.filter((key) => to.includes(key)).every((key, index) => to[index] === key)) return COMPARISON_DISSIMILAR;
|
|
7
|
+
return firstIsLarger ? COMPARISON_REMOVED : COMPARISON_ADDED;
|
|
7
8
|
}
|
|
8
9
|
function isFragment(value) {
|
|
9
|
-
return
|
|
10
|
+
return isNamed(value, NAME_FRAGMENT);
|
|
10
11
|
}
|
|
11
12
|
function isFragments(value) {
|
|
12
|
-
return
|
|
13
|
+
return isNamed(value, NAME_FRAGMENTS);
|
|
13
14
|
}
|
|
15
|
+
function isNamed(value, name) {
|
|
16
|
+
return typeof value === "object" && value != null && name in value && value[name] === true;
|
|
17
|
+
}
|
|
18
|
+
var COMPARISON_ADDED = "added";
|
|
19
|
+
var COMPARISON_DISSIMILAR = "dissimilar";
|
|
20
|
+
var COMPARISON_REMOVED = "removed";
|
|
14
21
|
export { compareArrays, isFragment, isFragments };
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export * from "@oscarpalmer/mora";
|
|
|
5
5
|
function fragments(array$1, identify, fragment) {
|
|
6
6
|
return new Fragments(array$1, identify, fragment);
|
|
7
7
|
}
|
|
8
|
-
function html(
|
|
9
|
-
return new Fragment(
|
|
8
|
+
function html(template, ...values) {
|
|
9
|
+
return new Fragment(template, values);
|
|
10
10
|
}
|
|
11
11
|
export { fragments, html };
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, EXPRESSION_ABYDON_CONTENT, EXPRESSION_EVENT_PREFIX, EXPRESSION_PERIOD } from "../../constants.js";
|
|
2
2
|
import { mapEvent } from "../event.js";
|
|
3
|
-
import { setAttribute
|
|
3
|
+
import { setAttribute } from "./value.js";
|
|
4
4
|
import { computed, isReactive } from "@oscarpalmer/mora";
|
|
5
|
-
import { isBooleanAttribute, setProperty } from "@oscarpalmer/toretto/attribute";
|
|
6
5
|
function getValue(data, original) {
|
|
7
|
-
const matches =
|
|
6
|
+
const matches = EXPRESSION_ABYDON_CONTENT.exec(original ?? "");
|
|
8
7
|
return matches == null ? original : data.values[+matches[1]];
|
|
9
8
|
}
|
|
10
9
|
function mapAttributes(data, element) {
|
|
@@ -12,16 +11,17 @@ function mapAttributes(data, element) {
|
|
|
12
11
|
const { length } = attributes;
|
|
13
12
|
for (let index = 0; index < length; index += 1) {
|
|
14
13
|
const { name, value } = attributes[index];
|
|
15
|
-
const
|
|
14
|
+
const actualName = name.replace(EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, "");
|
|
15
|
+
const actualValue = getValue(data, value);
|
|
16
|
+
if (actualName !== name) element.removeAttribute(name);
|
|
16
17
|
switch (true) {
|
|
17
|
-
case
|
|
18
|
-
mapEvent(element,
|
|
18
|
+
case EXPRESSION_EVENT_PREFIX.test(actualName):
|
|
19
|
+
mapEvent(element, actualName, actualValue);
|
|
19
20
|
break;
|
|
20
|
-
case
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
setProperty(element, name, value);
|
|
21
|
+
case EXPRESSION_PERIOD.test(actualName):
|
|
22
|
+
case typeof actualValue === "function":
|
|
23
|
+
case isReactive(actualValue):
|
|
24
|
+
mapValue(data, element, actualName, actualValue);
|
|
25
25
|
break;
|
|
26
26
|
default: break;
|
|
27
27
|
}
|
|
@@ -29,11 +29,11 @@ function mapAttributes(data, element) {
|
|
|
29
29
|
}
|
|
30
30
|
function mapValue(data, element, name, value) {
|
|
31
31
|
if (typeof value === "function") setComputedAttribute(data, element, name, value);
|
|
32
|
-
else setAttribute
|
|
32
|
+
else setAttribute(data, element, name, value);
|
|
33
33
|
}
|
|
34
34
|
function setComputedAttribute(data, element, name, callback) {
|
|
35
35
|
const value = computed(callback);
|
|
36
36
|
data.mora.values.add(value);
|
|
37
|
-
setAttribute
|
|
37
|
+
setAttribute(data, element, name, value);
|
|
38
38
|
}
|
|
39
39
|
export { mapAttributes };
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { ATTRIBUTE_CLASS_PREFIX_LENGTH, EXPRESSION_ATTRIBUTE_CLASS, EXPRESSION_ATTRIBUTE_STYLE_FULL, EXPRESSION_ATTRIBUTE_STYLE_PREFIX } from "../../constants.js";
|
|
2
2
|
import { isReactive } from "@oscarpalmer/mora";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { isBooleanAttribute, setAttribute as setAttribute$1, setProperty } from "@oscarpalmer/toretto/attribute";
|
|
4
|
+
function getCallback(element, name) {
|
|
5
|
+
if (isBooleanAttribute(name) && name in element) return name === "checked" ? updateChecked : updateProperty;
|
|
6
|
+
return name === "value" ? updateValue : setAttribute$1;
|
|
7
|
+
}
|
|
5
8
|
function setAttribute(data, element, name, value) {
|
|
6
|
-
element.removeAttribute(name);
|
|
7
9
|
switch (true) {
|
|
8
10
|
case EXPRESSION_ATTRIBUTE_CLASS.test(name):
|
|
9
11
|
setClasses(data, element, name, value);
|
|
@@ -30,27 +32,31 @@ function setStyle(data, element, name, value) {
|
|
|
30
32
|
if (property == null) return;
|
|
31
33
|
function update(value$1) {
|
|
32
34
|
if (value$1 == null || value$1 === false || value$1 === true && unit == null) element.style.removeProperty(property);
|
|
33
|
-
else element.style.setProperty(property, value$1 === true ? unit :
|
|
35
|
+
else element.style.setProperty(property, value$1 === true ? unit : String(value$1));
|
|
34
36
|
}
|
|
35
37
|
if (isReactive(value)) data.mora.subscribers.add(value.subscribe(update));
|
|
36
38
|
else update(value);
|
|
37
39
|
}
|
|
38
40
|
function setValue(data, element, name, value) {
|
|
39
|
-
|
|
40
|
-
if (isBooleanAttribute(name) && name in element) callback = name === "selected" ? updateSelected : updateProperty;
|
|
41
|
-
else callback = setAttribute$1;
|
|
41
|
+
const callback = getCallback(element, name);
|
|
42
42
|
if (isReactive(value)) data.mora.subscribers.add(value.subscribe((next) => {
|
|
43
43
|
callback(element, name, next);
|
|
44
44
|
}));
|
|
45
45
|
else callback(element, name, value);
|
|
46
46
|
}
|
|
47
|
+
function updateChecked(element, name, value) {
|
|
48
|
+
updateElement("change", "checked", element, name, value, value === true);
|
|
49
|
+
}
|
|
50
|
+
function updateElement(event, property, element, name, value, next) {
|
|
51
|
+
if (!(property in element) || element[property] === next) return;
|
|
52
|
+
setAttribute$1(element, name, value);
|
|
53
|
+
element[property] = next;
|
|
54
|
+
element.dispatchEvent(new Event(event, { bubbles: true }));
|
|
55
|
+
}
|
|
47
56
|
function updateProperty(element, name, value) {
|
|
48
|
-
element
|
|
57
|
+
setProperty(element, name, value === true);
|
|
49
58
|
}
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
const options = [...select?.options ?? []];
|
|
53
|
-
if (select != null && options.includes(element)) select.dispatchEvent(new Event("change", { bubbles: true }));
|
|
54
|
-
updateProperty(element, name, value);
|
|
59
|
+
function updateValue(element, name, value) {
|
|
60
|
+
updateElement(element instanceof HTMLSelectElement ? "change" : "input", "value", element, name, value, String(value));
|
|
55
61
|
}
|
|
56
62
|
export { setAttribute };
|
package/dist/node/event.js
CHANGED
|
@@ -1,30 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
let controller = ABORT_CONTROLLERS.get(element);
|
|
4
|
-
if (controller == null) {
|
|
5
|
-
controller = new AbortController();
|
|
6
|
-
ABORT_CONTROLLERS.set(element, controller);
|
|
7
|
-
}
|
|
8
|
-
return controller;
|
|
9
|
-
}
|
|
1
|
+
import { EVENT_DEFAULTS, EXPRESSION_EVENT_CHANGE_TYPES, EXPRESSION_EVENT_NAME, EXPRESSION_EVENT_OPTIONS_ACTIVE, EXPRESSION_EVENT_OPTIONS_CAPTURE, EXPRESSION_EVENT_OPTIONS_ONCE } from "../constants.js";
|
|
2
|
+
import { on } from "@oscarpalmer/toretto/event";
|
|
10
3
|
function getOptions(options) {
|
|
11
4
|
const parts = options.split(":");
|
|
12
5
|
return {
|
|
13
|
-
capture: parts.
|
|
14
|
-
once: parts.
|
|
15
|
-
passive: !
|
|
6
|
+
capture: parts.some((part) => EXPRESSION_EVENT_OPTIONS_CAPTURE.test(part)),
|
|
7
|
+
once: parts.some((part) => EXPRESSION_EVENT_OPTIONS_ONCE.test(part)),
|
|
8
|
+
passive: !parts.some((part) => EXPRESSION_EVENT_OPTIONS_ACTIVE.test(part))
|
|
16
9
|
};
|
|
17
10
|
}
|
|
11
|
+
function getType(element, type) {
|
|
12
|
+
if (type !== "on") return type;
|
|
13
|
+
if (element instanceof HTMLInputElement) {
|
|
14
|
+
if (EXPRESSION_EVENT_CHANGE_TYPES.test(element.type)) return "change";
|
|
15
|
+
return element.type === "submit" ? "submit" : "input";
|
|
16
|
+
}
|
|
17
|
+
return EVENT_DEFAULTS[element.tagName] ?? type;
|
|
18
|
+
}
|
|
18
19
|
function mapEvent(element, name, value) {
|
|
19
|
-
element.removeAttribute(name);
|
|
20
20
|
const [, type, options] = EXPRESSION_EVENT_NAME.exec(name) ?? [];
|
|
21
|
-
if (typeof value === "function"
|
|
22
|
-
...getOptions(options ?? ""),
|
|
23
|
-
signal: getController(element).signal
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
function removeEvents(element) {
|
|
27
|
-
ABORT_CONTROLLERS.get(element)?.abort(REASON_EVENT_REMOVED);
|
|
28
|
-
ABORT_CONTROLLERS.delete(element);
|
|
21
|
+
if (type != null && typeof value === "function") on(element, getType(element, type), value, getOptions(options ?? ""));
|
|
29
22
|
}
|
|
30
|
-
export { mapEvent
|
|
23
|
+
export { mapEvent };
|
package/dist/node/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { EXPRESSION_ABYDON_CONTENT } from "../constants.js";
|
|
1
2
|
import { isFragment, isFragments } from "../helpers/index.js";
|
|
2
3
|
import { handleFragments } from "../fragments.js";
|
|
3
|
-
import { EXPRESSION_COMMENT_CONTENT } from "../constants.js";
|
|
4
4
|
import { createNodes } from "../helpers/dom.js";
|
|
5
5
|
import { mapAttributes } from "./attribute/index.js";
|
|
6
6
|
import { setReactiveValue } from "./value.js";
|
|
7
7
|
import { computed, isReactive } from "@oscarpalmer/mora";
|
|
8
8
|
import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
|
|
9
9
|
function mapNode(data, comment) {
|
|
10
|
-
const matches =
|
|
10
|
+
const matches = EXPRESSION_ABYDON_CONTENT.exec(comment.textContent ?? "");
|
|
11
11
|
const value = matches == null ? null : data.values[+matches[1]];
|
|
12
12
|
if (value != null) mapValue(data, comment, value);
|
|
13
13
|
}
|
package/dist/node/value.js
CHANGED
|
@@ -5,7 +5,7 @@ import { getString } from "@oscarpalmer/atoms/string";
|
|
|
5
5
|
import { isChildNode } from "@oscarpalmer/toretto/is";
|
|
6
6
|
function addToArray(identifiers, items, nodes, added) {
|
|
7
7
|
let position = nodes[0];
|
|
8
|
-
const before = added && !identifiers.previous.
|
|
8
|
+
const before = added && !identifiers.previous.has(items.templates[0].identifier);
|
|
9
9
|
const next = items.next.flatMap((fragment) => fragment.get().flatMap((node) => ({
|
|
10
10
|
identifier: fragment.identifier,
|
|
11
11
|
value: node
|
|
@@ -13,7 +13,7 @@ function addToArray(identifiers, items, nodes, added) {
|
|
|
13
13
|
const { length } = next;
|
|
14
14
|
for (let index = 0; index < length; index += 1) {
|
|
15
15
|
const node = next[index];
|
|
16
|
-
if (!(added && identifiers.previous.
|
|
16
|
+
if (!(added && identifiers.previous.has(node.identifier))) if (index === 0 && before) position.before(node.value);
|
|
17
17
|
else position.after(node.value);
|
|
18
18
|
position = node.value;
|
|
19
19
|
}
|
|
@@ -25,7 +25,7 @@ function handleArray(identifiers, items, nodes) {
|
|
|
25
25
|
...items,
|
|
26
26
|
next
|
|
27
27
|
}, nodes, comparison === "added");
|
|
28
|
-
const toRemove = items.fragments?.filter((fragment) => !identifiers.next.
|
|
28
|
+
const toRemove = items.fragments?.filter((fragment) => !identifiers.next.has(fragment.identifier)) ?? [];
|
|
29
29
|
const { length } = toRemove;
|
|
30
30
|
for (let index = 0; index < length; index += 1) toRemove[index].remove();
|
|
31
31
|
return {
|
|
@@ -57,8 +57,8 @@ function setArray(item, comment, value) {
|
|
|
57
57
|
nodes: setNodes(item, comment, noTemplates ? value.flatMap((item$1) => createNodes(item$1)) : templates.flatMap((template) => template.get()))
|
|
58
58
|
};
|
|
59
59
|
return handleArray({
|
|
60
|
-
next,
|
|
61
|
-
previous
|
|
60
|
+
next: new Set(next),
|
|
61
|
+
previous: new Set(previous)
|
|
62
62
|
}, {
|
|
63
63
|
templates,
|
|
64
64
|
fragments: item.fragments ?? []
|
package/dist/parse.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EXPRESSION_ABYDON_ATTRIBUTE_FULL } from "./constants.js";
|
|
1
2
|
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
2
3
|
function handleExpression(data, prefix, expression) {
|
|
3
4
|
if (Array.isArray(expression)) {
|
|
@@ -6,7 +7,7 @@ function handleExpression(data, prefix, expression) {
|
|
|
6
7
|
for (let index = 0; index < length; index += 1) expressions += handleExpression(data, "", expression[index]);
|
|
7
8
|
return `${prefix}${expressions}`;
|
|
8
9
|
}
|
|
9
|
-
if (typeof expression === "function" || typeof expression === "object" && expression != null) return
|
|
10
|
+
if (typeof expression === "function" || typeof expression === "object" && expression != null) return transformExpression(prefix, data.values.push(expression) - 1);
|
|
10
11
|
return isNullableOrWhitespace(expression) ? prefix : `${prefix}${expression}`;
|
|
11
12
|
}
|
|
12
13
|
function parse(data) {
|
|
@@ -14,8 +15,15 @@ function parse(data) {
|
|
|
14
15
|
const { length } = data.strings;
|
|
15
16
|
data.template = "";
|
|
16
17
|
for (let index = 0; index < length; index += 1) data.template += handleExpression(data, data.strings[index], data.expressions[index]);
|
|
18
|
+
data.template = data.template.replaceAll(EXPRESSION_ABYDON_ATTRIBUTE_FULL, transformAttribute);
|
|
17
19
|
data.expressions = [];
|
|
18
20
|
data.strings = [];
|
|
19
21
|
return data.template;
|
|
20
22
|
}
|
|
23
|
+
function transformAttribute(_, name, index) {
|
|
24
|
+
return `_${name}="@abydon.${index}@"`;
|
|
25
|
+
}
|
|
26
|
+
function transformExpression(prefix, index) {
|
|
27
|
+
return `${prefix}<!--abydon.${index}-->`;
|
|
28
|
+
}
|
|
21
29
|
export { parse };
|
package/package.json
CHANGED
|
@@ -4,23 +4,21 @@
|
|
|
4
4
|
"url": "https://oscarpalmer.se"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@oscarpalmer/atoms": "^0.
|
|
8
|
-
"@oscarpalmer/mora": "^0.
|
|
9
|
-
"@oscarpalmer/toretto": "^0.
|
|
7
|
+
"@oscarpalmer/atoms": "^0.118",
|
|
8
|
+
"@oscarpalmer/mora": "^0.25",
|
|
9
|
+
"@oscarpalmer/toretto": "^0.32"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@biomejs/biome": "^2.3",
|
|
13
12
|
"@oscarpalmer/oui": "^0.18",
|
|
14
|
-
"@
|
|
15
|
-
"@rollup/plugin-typescript": "^12.3",
|
|
16
|
-
"@types/node": "^24.9",
|
|
13
|
+
"@types/node": "^25",
|
|
17
14
|
"@vitest/coverage-istanbul": "^4",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
15
|
+
"jsdom": "^27.3",
|
|
16
|
+
"oxfmt": "^0.19",
|
|
17
|
+
"oxlint": "^1.34",
|
|
18
|
+
"rolldown": "1.0.0-beta.56",
|
|
21
19
|
"tslib": "^2.8",
|
|
22
20
|
"typescript": "^5.9",
|
|
23
|
-
"vite": "
|
|
21
|
+
"vite": "8.0.0-beta.2",
|
|
24
22
|
"vitest": "^4"
|
|
25
23
|
},
|
|
26
24
|
"exports": {
|
|
@@ -49,14 +47,14 @@
|
|
|
49
47
|
"url": "git+https://github.com/oscarpalmer/abydon.git"
|
|
50
48
|
},
|
|
51
49
|
"scripts": {
|
|
52
|
-
"build": "npm run clean && npx vite build && npm run
|
|
50
|
+
"build": "npm run clean && npx vite build && npm run rolldown:build && npx tsc",
|
|
53
51
|
"clean": "rm -rf ./dist && rm -rf ./types && rm -f ./tsconfig.tsbuildinfo",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
52
|
+
"rolldown:build": "npx rolldown -c",
|
|
53
|
+
"rolldown:watch": "npx rolldown -c ./rolldown.config.js --watch",
|
|
56
54
|
"test": "npx vitest --coverage",
|
|
57
55
|
"watch": "npx vite build --watch"
|
|
58
56
|
},
|
|
59
57
|
"type": "module",
|
|
60
58
|
"types": "types/index.d.ts",
|
|
61
|
-
"version": "0.
|
|
59
|
+
"version": "0.16.0"
|
|
62
60
|
}
|
package/src/constants.ts
CHANGED
|
@@ -1,20 +1,40 @@
|
|
|
1
|
-
export const ABORT_CONTROLLERS: WeakMap<HTMLOrSVGElement, AbortController> =
|
|
2
|
-
new WeakMap();
|
|
3
|
-
|
|
4
1
|
export const ATTRIBUTE_CLASS_PREFIX_LENGTH = 6;
|
|
5
2
|
|
|
3
|
+
export const EVENT_DEFAULTS: Record<string, string> = {
|
|
4
|
+
A: 'click',
|
|
5
|
+
BUTTON: 'click',
|
|
6
|
+
DETAILS: 'toggle',
|
|
7
|
+
FORM: 'submit',
|
|
8
|
+
SELECT: 'change',
|
|
9
|
+
TEXTAREA: 'input',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const EXPRESSION_ABYDON_ATTRIBUTE_FULL = /(@?\w+)="<!--abydon.(\d+)-->"/g;
|
|
13
|
+
|
|
14
|
+
export const EXPRESSION_ABYDON_ATTRIBUTE_PREFIX = /^_/;
|
|
15
|
+
|
|
16
|
+
export const EXPRESSION_ABYDON_CONTENT = /^@?abydon\.(\d+)@?$/;
|
|
17
|
+
|
|
6
18
|
export const EXPRESSION_ATTRIBUTE_CLASS = /^class\./;
|
|
7
19
|
|
|
8
|
-
export const EXPRESSION_ATTRIBUTE_STYLE_FULL =
|
|
9
|
-
/^style\.([\w-]+)(?:\.([\w-]+))?$/;
|
|
20
|
+
export const EXPRESSION_ATTRIBUTE_STYLE_FULL = /^style\.([\w-]+)(?:\.([\w-]+))?$/;
|
|
10
21
|
|
|
11
22
|
export const EXPRESSION_ATTRIBUTE_STYLE_PREFIX = /^style\./;
|
|
12
23
|
|
|
13
|
-
export const
|
|
14
|
-
|
|
15
|
-
export const EXPRESSION_COMMENT_CONTENT = /^abydon\.(\d+)$/;
|
|
24
|
+
export const EXPRESSION_EVENT_CHANGE_TYPES = /^(checkbox|radio)$/;
|
|
16
25
|
|
|
17
26
|
export const EXPRESSION_EVENT_NAME = /^@([\w-]+)(?::([a-z:]+))?$/i;
|
|
18
27
|
|
|
19
|
-
export const
|
|
20
|
-
|
|
28
|
+
export const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(ctive)$/i;
|
|
29
|
+
|
|
30
|
+
export const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(apture)$/i;
|
|
31
|
+
|
|
32
|
+
export const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(nce)$/i;
|
|
33
|
+
|
|
34
|
+
export const EXPRESSION_EVENT_PREFIX = /^@/;
|
|
35
|
+
|
|
36
|
+
export const EXPRESSION_PERIOD = /\./;
|
|
37
|
+
|
|
38
|
+
export const NAME_FRAGMENT = '$fragment';
|
|
39
|
+
|
|
40
|
+
export const NAME_FRAGMENTS = '$fragments';
|
package/src/fragment.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
2
|
import {html} from '@oscarpalmer/toretto/html';
|
|
3
|
+
import {NAME_FRAGMENT} from './constants';
|
|
3
4
|
import {handleFragments} from './fragments';
|
|
4
5
|
import {isFragments} from './helpers';
|
|
5
6
|
import {removeNodes} from './helpers/dom';
|
|
@@ -12,7 +13,7 @@ export class Fragment {
|
|
|
12
13
|
|
|
13
14
|
readonly #configuration: Required<FragmentConfiguration> = {
|
|
14
15
|
identifier: undefined,
|
|
15
|
-
|
|
16
|
+
cache: true,
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -23,7 +24,7 @@ export class Fragment {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
constructor(strings: TemplateStringsArray, expressions: unknown[]) {
|
|
26
|
-
Object.defineProperty(this,
|
|
27
|
+
Object.defineProperty(this, NAME_FRAGMENT, {
|
|
27
28
|
value: true,
|
|
28
29
|
});
|
|
29
30
|
|
|
@@ -55,12 +56,12 @@ export class Fragment {
|
|
|
55
56
|
configure(configuration: FragmentConfiguration): Fragment {
|
|
56
57
|
const actual = isPlainObject(configuration) ? configuration : {};
|
|
57
58
|
|
|
58
|
-
if (
|
|
59
|
+
if ('identifier' in actual) {
|
|
59
60
|
this.#configuration.identifier = actual.identifier;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
if (typeof actual.
|
|
63
|
-
this.#configuration.
|
|
63
|
+
if (typeof actual.cache === 'boolean') {
|
|
64
|
+
this.#configuration.cache = actual.cache;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
return this;
|
|
@@ -77,8 +78,7 @@ export class Fragment {
|
|
|
77
78
|
const parsed = parse(data);
|
|
78
79
|
|
|
79
80
|
const templated = html(parsed, {
|
|
80
|
-
|
|
81
|
-
sanitizeBooleanAttributes: false,
|
|
81
|
+
cache: this.#configuration.cache,
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
data.items.splice(
|
|
@@ -92,22 +92,14 @@ export class Fragment {
|
|
|
92
92
|
mapNodes(
|
|
93
93
|
data,
|
|
94
94
|
data.items.flatMap(
|
|
95
|
-
item =>
|
|
96
|
-
item.fragments?.flatMap(fragment => fragment.get()) ??
|
|
97
|
-
item.nodes ??
|
|
98
|
-
[],
|
|
95
|
+
item => item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes ?? [],
|
|
99
96
|
),
|
|
100
97
|
);
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
item.fragments?.flatMap(fragment => fragment.get()) ??
|
|
107
|
-
item.nodes ??
|
|
108
|
-
[],
|
|
109
|
-
),
|
|
110
|
-
];
|
|
100
|
+
return data.items.flatMap(
|
|
101
|
+
item => item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes ?? [],
|
|
102
|
+
);
|
|
111
103
|
}
|
|
112
104
|
|
|
113
105
|
/**
|
|
@@ -141,11 +133,7 @@ function removeFragment(data: FragmentData): void {
|
|
|
141
133
|
const {fragments, nodes} = data.items[index];
|
|
142
134
|
const fragmentsLength = fragments?.length ?? 0;
|
|
143
135
|
|
|
144
|
-
for (
|
|
145
|
-
let fragmentIndex = 0;
|
|
146
|
-
fragmentIndex < fragmentsLength;
|
|
147
|
-
fragmentIndex += 1
|
|
148
|
-
) {
|
|
136
|
+
for (let fragmentIndex = 0; fragmentIndex < fragmentsLength; fragmentIndex += 1) {
|
|
149
137
|
fragments?.[fragmentIndex]?.remove();
|
|
150
138
|
}
|
|
151
139
|
|
package/src/fragments.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {getString} from '@oscarpalmer/atoms/string';
|
|
2
2
|
import {array, type ReactiveArray} from '@oscarpalmer/mora';
|
|
3
|
+
import {NAME_FRAGMENTS} from './constants';
|
|
3
4
|
import type {Fragment} from './fragment';
|
|
4
5
|
import {isFragment, isFragments} from './helpers';
|
|
5
6
|
import type {FragmentsState} from './models';
|
|
@@ -19,7 +20,7 @@ export class Fragments {
|
|
|
19
20
|
identify: (item: unknown) => unknown,
|
|
20
21
|
fragment: (item: unknown) => Fragment,
|
|
21
22
|
) {
|
|
22
|
-
Object.defineProperty(this,
|
|
23
|
+
Object.defineProperty(this, NAME_FRAGMENTS, {
|
|
23
24
|
value: true,
|
|
24
25
|
});
|
|
25
26
|
|
|
@@ -38,10 +39,7 @@ export class Fragments {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
export function handleFragments(
|
|
42
|
-
item: Fragments | FragmentsState,
|
|
43
|
-
remove: boolean,
|
|
44
|
-
): void {
|
|
42
|
+
export function handleFragments(item: Fragments | FragmentsState, remove: boolean): void {
|
|
45
43
|
const state = isFragments(item) ? states.get(item) : item;
|
|
46
44
|
|
|
47
45
|
if (state != null) {
|