nude-element
Advanced tools
Comparing version
{ | ||
"name": "nude-element", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "Composable web component helpers", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -181,2 +181,5 @@ <header> | ||
the first time `connectedCallback` is called or at the end of your constructor. | ||
the first time `connectedCallback` is called or at the end of your constructor. | ||
Read more: | ||
- [Using Props](src/props/) |
@@ -8,6 +8,6 @@ /** | ||
let instanceInitialized = Symbol("instanceInitialized"); | ||
let classInitialized = Symbol("classInitialized"); | ||
const Self = class NudeElement extends HTMLElement { | ||
static initQueue = []; | ||
#initialized = false; | ||
constructor () { | ||
@@ -23,7 +23,7 @@ super(); | ||
connectedCallback () { | ||
if (!this.#initialized) { | ||
if (!this[instanceInitialized]) { | ||
// Stuff that runs once per element | ||
this.constructor.initQueue.forEach(init => init.call(this)); | ||
this.#initialized = true; | ||
this[instanceInitialized] = true; | ||
} | ||
@@ -34,6 +34,8 @@ } | ||
// Stuff that runs once per class | ||
if (this._initialized) { | ||
if (this[classInitialized]) { | ||
return; | ||
} | ||
this.initQueue ??= []; | ||
if (this.props) { | ||
@@ -51,3 +53,3 @@ defineProps(this); | ||
this._initialized = true; | ||
this[classInitialized] = true; | ||
} | ||
@@ -54,0 +56,0 @@ } |
@@ -25,8 +25,6 @@ import defineProps from "../props/defineProps.js"; | ||
return function init () { | ||
// Event is a subset of another event (either on this element or another element) | ||
// Event is a subset of another event (either on this element or other element(s)) | ||
let target = resolveValue(from?.on, [this]) ?? this; | ||
let host = this; | ||
target.addEventListener(type, event => { | ||
let listener = event => { | ||
if (!from.when || from.when(event)) { | ||
@@ -44,3 +42,13 @@ let EventConstructor = from.event ?? event.constructor; | ||
} | ||
}); | ||
}; | ||
if (Array.isArray(target)) { | ||
for (let t of target) { | ||
t.addEventListener(type, listener); | ||
} | ||
} | ||
else { | ||
target.addEventListener(type, listener); | ||
} | ||
} | ||
@@ -47,0 +55,0 @@ } |
@@ -6,5 +6,4 @@ import { | ||
import PropChangeEvent from "./PropChangeEvent.js"; | ||
import { equals, stringify, parse } from "./types.js"; | ||
const callableBuiltins = new Set([String, Number, Boolean, Array, Object, Function, Symbol, BigInt]); | ||
let Self = class Prop { | ||
@@ -80,5 +79,5 @@ constructor (name, spec, props) { | ||
// Just calls Self.equals() by default but can be overridden | ||
// Just calls equals() by default but can be overridden | ||
equals(a, b) { | ||
return Self.equals(a, b, this.type, this.typeOptions); | ||
return equals(a, b, this.type, this.typeOptions); | ||
} | ||
@@ -88,3 +87,3 @@ | ||
stringify (value) { | ||
return Self.stringify(value, this.type, this.typeOptions); | ||
return stringify(value, this.type, this.typeOptions); | ||
} | ||
@@ -96,3 +95,3 @@ | ||
parse (value) { | ||
return Self.parse(value, this.type, this.typeOptions); | ||
return parse(value, this.type, this.typeOptions); | ||
} | ||
@@ -153,3 +152,3 @@ | ||
set (element, value, {source = "property", name, oldValue} = {}) { | ||
set (element, value, {source, name, oldValue} = {}) { | ||
let oldInternalValue = element.props[this.name]; | ||
@@ -252,13 +251,18 @@ | ||
if (dependency === this.defaultProp) { | ||
// We have no way of checking if the default prop has changed | ||
// and there’s nothing to set, so let’s just called changed directly | ||
this.changed(element, {element, source: "default"}); | ||
return; | ||
} | ||
if (this.spec.get) { | ||
let value = this.spec.get.call(element); | ||
this.set(element, value, {source: "property", oldValue}); | ||
this.set(element, value, {source: "get", oldValue}); | ||
} | ||
else if (this.spec.convert && oldValue !== undefined) { | ||
if (this.spec.convert && oldValue !== undefined) { | ||
let value = this.spec.convert.call(element, oldValue); | ||
this.set(element, value, {source: "property", oldValue}); | ||
this.set(element, value, {source: "convert", oldValue}); | ||
} | ||
else if (dependency === this.defaultProp) { | ||
this.changed(element, {element, source: "default"}); | ||
} | ||
} | ||
@@ -292,134 +296,4 @@ | ||
} | ||
static equals (a, b, type, typeOptions) { | ||
if (a === null || b === null || a === undefined || b === undefined) { | ||
return a === b; | ||
} | ||
let equals = this.types.get(type)?.equals; | ||
return equals ? equals(a, b, typeOptions) : this.defaultType.equals(a, b, type, typeOptions); | ||
} | ||
// Cast a value to the desired type | ||
static parse (value, type, typeOptions) { | ||
if (!type || value === undefined || value === null) { | ||
return value; | ||
} | ||
let parse = this.types.get(type)?.parse; | ||
return parse ? parse(value, typeOptions) : this.defaultType.parse(value, type, typeOptions); | ||
} | ||
static stringify (value, type, typeOptions) { | ||
if (value === undefined || value === null) { | ||
return null; | ||
} | ||
if (!type) { | ||
return String(value); | ||
} | ||
let stringify = this.types.get(type)?.stringify; | ||
if (stringify === false) { | ||
throw new TypeError(`Cannot stringify ${type}`); | ||
} | ||
return stringify ? stringify(value, typeOptions) : this.defaultType.stringify(value, type, typeOptions); | ||
} | ||
static defaultType = { | ||
equals (a, b, type) { | ||
let simpleEquals = a === b; | ||
if (simpleEquals || a === null || a === undefined || b === null || b === undefined) { | ||
return simpleEquals; | ||
} | ||
if (typeof a.equals === "function") { | ||
return a.equals(b); | ||
} | ||
// Roundtrip | ||
return simpleEquals; | ||
}, | ||
parse (value, type, typeOptions) { | ||
if (value instanceof type) { | ||
return value; | ||
} | ||
return callableBuiltins.has(type) ? type(value) : new type(value); | ||
}, | ||
stringify (value, type, typeOptions) { | ||
return String(value); | ||
}, | ||
} | ||
static types = new Map([ | ||
[Boolean, { | ||
parse: value => value !== null, | ||
stringify: value => value ? "" : null, | ||
}], | ||
[Number, { | ||
equals: (a, b) => a === b || Number.isNaN(a) && Number.isNaN(b), | ||
}], | ||
[Function, { | ||
equals: (a, b) => a === b || a.toString() === b.toString(), | ||
parse (value, options = {}) { | ||
if (typeof value === "function") { | ||
return value; | ||
} | ||
value = String(value); | ||
return Function(...(options.arguments ?? []), value); | ||
}, | ||
// Just don’t do that | ||
stringify: false, | ||
}], | ||
[Array, { | ||
equals (a, b, { itemType } = {}) { | ||
if (a.length !== b.length) { | ||
return false; | ||
} | ||
if (itemType) { | ||
return a.every((item, i) => Self.equals(item, b[i], itemType)); | ||
} | ||
else { | ||
return a.every((item, i) => item === b[i]); | ||
} | ||
}, | ||
parse (value, { itemType, separator = ",", splitter } = {}) { | ||
if (!Array.isArray(value)) { | ||
if (!splitter) { | ||
// Make whitespace optional and flexible, unless the separator consists entirely of whitespace | ||
let isSeparatorWhitespace = !separator.trim(); | ||
splitter = isSeparatorWhitespace ? /\s+/ : new RegExp(separator.replace(/\s+/g, "\\s*")); | ||
} | ||
value = typeof value === "string" ? value.trim().split(splitter) : [value]; | ||
} | ||
if (itemType) { | ||
return value.map(item => Self.parse(item, itemType)); | ||
} | ||
}, | ||
stringify: (value, { itemType, separator = ",", joiner } = {}) => { | ||
if (itemType) { | ||
value = value.map(item => Self.stringify(item, itemType)); | ||
} | ||
if (!joiner) { | ||
let trimmedSeparator = separator.trim(); | ||
joiner = (!trimmedSeparator || trimmedSeparator === "," ? "" : " ") + separator + " "; | ||
} | ||
return value.join(joiner); | ||
}, | ||
}], | ||
]) | ||
} | ||
export default Self; |
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
45837
13.39%15
25%1052
1.15%184
1.66%0
-100%