@gluon/gluon 2.5.2 → 3.0.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/CHANGELOG.md +41 -0
- package/README.md +268 -122
- package/dist/gluon.d.ts +124 -0
- package/dist/gluon.min.js +4 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +254 -0
- package/dist/properties.d.ts +60 -0
- package/dist/styles.d.ts +26 -0
- package/package.json +44 -44
- package/gluon.es5.js +0 -2061
- package/gluon.es5.js.map +0 -1
- package/gluon.js +0 -126
- package/gluon.umd.js +0 -1591
- package/gluon.umd.js.map +0 -1
package/dist/gluon.d.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2026 Goffert van Gool
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { type PropertyDeclarations, type PropertyValues } from "./properties.js";
|
|
7
|
+
import { type Styles } from "./styles.js";
|
|
8
|
+
type GluonConstructor = typeof GluonElement;
|
|
9
|
+
/**
|
|
10
|
+
* Registers an element class with a custom element registry, under the tag name `name`
|
|
11
|
+
*
|
|
12
|
+
* A name that is already registered is not registered again, so a module can be loaded more than once.
|
|
13
|
+
* When the name is registered with a different class, the existing registration is kept and a warning is logged.
|
|
14
|
+
*/
|
|
15
|
+
export declare const define: <T extends GluonConstructor>(ctor: T, name: string, registry?: CustomElementRegistry) => T;
|
|
16
|
+
/**
|
|
17
|
+
* A base class for custom elements
|
|
18
|
+
*
|
|
19
|
+
* - Declares reactive properties in a static `properties` map, with attributes that are converted and reflected
|
|
20
|
+
* - Renders the result of `render()` with lite-html into a shadow root, batched in a microtask after properties change
|
|
21
|
+
* - Applies the static `styles` to the shadow root, once per class
|
|
22
|
+
* - Calls event handlers in the template with the element as `this`
|
|
23
|
+
*/
|
|
24
|
+
export declare class GluonElement extends HTMLElement {
|
|
25
|
+
#private;
|
|
26
|
+
/**
|
|
27
|
+
|
|
28
|
+
* The reactive properties of this element, by name
|
|
29
|
+
*
|
|
30
|
+
* Each declared property is an accessor that requests an update when its value changes.
|
|
31
|
+
* With `attribute: true`, the property is also set from the attribute with its kebab-cased name.
|
|
32
|
+
*/
|
|
33
|
+
static properties: PropertyDeclarations;
|
|
34
|
+
/**
|
|
35
|
+
* The styles of this element: CSS text, a constructed `CSSStyleSheet`, or a list of them
|
|
36
|
+
*
|
|
37
|
+
* The styles are applied to the shadow root of every instance, once per class.
|
|
38
|
+
* They are not applied when the element renders into its light DOM.
|
|
39
|
+
*/
|
|
40
|
+
static styles?: Styles;
|
|
41
|
+
/**
|
|
42
|
+
* The options for the shadow root that `createRenderRoot()` attaches
|
|
43
|
+
*/
|
|
44
|
+
static shadowRootOptions: ShadowRootInit;
|
|
45
|
+
static get observedAttributes(): string[];
|
|
46
|
+
/**
|
|
47
|
+
* The node that `render()` renders into. A shadow root, unless `createRenderRoot()` is overridden
|
|
48
|
+
*/
|
|
49
|
+
readonly renderRoot: ParentNode;
|
|
50
|
+
/**
|
|
51
|
+
* Whether an update is scheduled and has not been performed yet
|
|
52
|
+
*/
|
|
53
|
+
isUpdatePending: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Whether the element has performed its first update
|
|
56
|
+
*/
|
|
57
|
+
hasUpdated: boolean;
|
|
58
|
+
constructor();
|
|
59
|
+
/**
|
|
60
|
+
* Returns the node to render into
|
|
61
|
+
*
|
|
62
|
+
* Attaches a shadow root with the static `shadowRootOptions`, or returns the shadow root that already exists.
|
|
63
|
+
* Override this method to render somewhere else, for example `return this` to render into the light DOM.
|
|
64
|
+
*/
|
|
65
|
+
protected createRenderRoot(): ParentNode;
|
|
66
|
+
/**
|
|
67
|
+
* An `AbortSignal` that is aborted when the element is disconnected
|
|
68
|
+
*
|
|
69
|
+
* Pass it as the `signal` of listeners and observers that are set up in `connectedCallback()`,
|
|
70
|
+
* so they are removed when the element is disconnected. A new signal is created on every connect.
|
|
71
|
+
*/
|
|
72
|
+
get disconnectSignal(): AbortSignal;
|
|
73
|
+
/**
|
|
74
|
+
* Resolves when the pending update completes
|
|
75
|
+
*
|
|
76
|
+
* Resolves with `true` when no further update was requested during the update, and `false` otherwise
|
|
77
|
+
*/
|
|
78
|
+
get updateComplete(): Promise<boolean>;
|
|
79
|
+
connectedCallback(): void;
|
|
80
|
+
disconnectedCallback(): void;
|
|
81
|
+
attributeChangedCallback(name: string, _oldValue: string | null, value: string | null): void;
|
|
82
|
+
/**
|
|
83
|
+
* Requests an update, which is performed in a microtask
|
|
84
|
+
*
|
|
85
|
+
* Called by the setter of every declared property. Call it without arguments to update after a change
|
|
86
|
+
* that the element cannot see, such as a mutation of an array or object in a property.
|
|
87
|
+
*/
|
|
88
|
+
requestUpdate(name?: string, oldValue?: unknown): void;
|
|
89
|
+
/**
|
|
90
|
+
* Performs the pending update now, instead of in a microtask
|
|
91
|
+
*
|
|
92
|
+
* Runs `shouldUpdate()`, `willUpdate()`, `update()`, `firstUpdated()`, and `updated()`
|
|
93
|
+
*/
|
|
94
|
+
performUpdate(): void;
|
|
95
|
+
/**
|
|
96
|
+
* Decides whether an update is performed. Defaults to `true`
|
|
97
|
+
*/
|
|
98
|
+
protected shouldUpdate(_changed: PropertyValues<this>): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Called before `update()`, to compute values that depend on the changed properties
|
|
101
|
+
*/
|
|
102
|
+
protected willUpdate(_changed: PropertyValues<this>): void;
|
|
103
|
+
/**
|
|
104
|
+
* Reflects the changed properties to their attributes, and renders the result of `render()` into the render root
|
|
105
|
+
*
|
|
106
|
+
* Nothing is rendered when `render()` returns `noChange`
|
|
107
|
+
*/
|
|
108
|
+
protected update(_changed: PropertyValues<this>): void;
|
|
109
|
+
/**
|
|
110
|
+
* Returns what to render into the render root, usually a lite-html template
|
|
111
|
+
*
|
|
112
|
+
* Return `noChange` to leave the render root as it is
|
|
113
|
+
*/
|
|
114
|
+
protected render(): unknown;
|
|
115
|
+
/**
|
|
116
|
+
* Called after the first update
|
|
117
|
+
*/
|
|
118
|
+
protected firstUpdated(_changed: PropertyValues<this>): void;
|
|
119
|
+
/**
|
|
120
|
+
* Called after every update
|
|
121
|
+
*/
|
|
122
|
+
protected updated(_changed: PropertyValues<this>): void;
|
|
123
|
+
}
|
|
124
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** @license Copyright (c) 2026 Goffert van Gool, SPDX-License-Identifier: MIT */
|
|
2
|
+
var e=Math.random().toString(36).slice(2).padStart(10,`0`),t=`attribute-${e}`,n=`comment-${e}`,r=`node-${e}`,i=`${r}" ${r} `,a=()=>document.createComment(``),o=(e,t,n)=>{let r=e.nextSibling;for(;r&&r!==t;){let e=r.nextSibling;n?n.append(r):r.remove(),r=e}},s=/* @__PURE__ */ new WeakSet,c=e=>typeof e==`function`&&s.has(e),l=e=>(s.add(e),e),u=e=>typeof e==`string`||typeof e==`number`||typeof e==`boolean`,d=e=>typeof e[Symbol.iterator]==`function`,ee=e=>typeof e.then==`function`,f={},p={},m={},h=class e{node;value;beforeNode;afterNode;instance;iterableParts;promise;options;constructor({node:e,options:t}){this.node=p,this.value=f,this.options=t,this.beforeNode=e,this.afterNode=e.nextSibling}get parentNode(){return this.beforeNode.parentNode}render(e){if(c(e))e(this);else if(e!==f){if(e==null)this.clear();else if(u(e))this._renderText(e);else if(e instanceof D)this._renderTemplateResult(e);else if(d(e))this._renderIterable(e);else if(e instanceof Node)this._renderNode(e);else if(ee(e)){this._renderPromise(e);return}else e=String(e),this._renderText(e);this.promise=void 0,this.value=e}}_renderText(e){this.value!==e&&(u(this.value)?this.node.data=String(e):this._renderNode(document.createTextNode(String(e))))}_renderTemplateResult(e){if(this.instance?.template===e.template)this.instance.render(e.values);else{let t=new O(e.template,this.options);t.render(e.values),this._renderNode(t.fragment),this.instance=t}}_renderIterable(t){let n=this.iterableParts;(this.node!==m||!n)&&(this.clear(),this.node=m,n=this.iterableParts=[]);let r=0,i=this.afterNode?this.afterNode.previousSibling:this.parentNode.lastChild;for(let o of t){let t=n[r];if(t===void 0){let r=a();this.parentNode.insertBefore(r,this.afterNode),t=new e({node:i,options:this.options}),n.push(t),i=r}t.render(o),r++}if(r===0)o(this.beforeNode,this.afterNode);else if(r<n.length){let e=n[r-1];o(e.afterNode,this.afterNode)}n.length=r}_renderNode(e){this.node!==e&&(this.clear(),this.parentNode.insertBefore(e,this.afterNode),this.node=e)}_renderPromise(e){this.promise!==e&&(this.promise=e,e.then(t=>{this.promise===e&&(this.promise=void 0,this.render(t))}))}clear(){o(this.beforeNode,this.afterNode,this.node instanceof DocumentFragment?this.node:void 0),this.node=p,this.instance=this.iterableParts=void 0}},g=class{node;constructor({node:e}){this.node=e}render(e){this.node.textContent=e==null?``:String(e)}},_=class{node;value;type;name;options;_render;constructor({node:e,attribute:t,options:n}){switch(this.node=e,this.value=f,this.options=n,t[0]){case`.`:this.type=`property`,this._render=this._renderProperty;break;case`?`:this.type=`boolean`,this._render=this._renderBoolean;break;case`@`:this.type=`event`,this._render=this._renderEvent;break;default:this.type=`attribute`,this._render=this._renderAttribute}this.name=this.type===`attribute`?t:t.slice(1),this.type===`event`&&this.node.addEventListener(this.name,this)}render(e){c(e)?e(this):e!==f&&this._render(e)}_renderProperty(e){this.node[this.name]=e}_renderBoolean(e){this.value!==!!e&&(e?this.node.setAttribute(this.name,``):this.node.removeAttribute(this.name),this.value=!!e)}_renderEvent(e){this.value=e}handleEvent(e){let t=this.value;typeof t==`function`?t.call(this.options?.host??this.node,e):t?.handleEvent?.(e)}_renderAttribute(e){this.value!==e&&(this.node.setAttribute(this.name,String(e??``)),this.value=e)}},te=/[ \x09\x0a\x0c\x0d]([^\0-\x1F\x7F-\x9F \x09\x0a\x0c\x0d"'>=/]+)[ \x09\x0a\x0c\x0d]*=$/,v=(a,o)=>{let s=[],c=(t,n)=>{if(t.includes(e))throw Error(`Parts are not allowed inside <${n?.localName}> elements`)},l=(e,o)=>{if(e instanceof Comment)e.data===n?s.push({type:g,path:o}):e.data===i&&(e.data=``,s.push({type:h,path:o}));else if(e instanceof Text)c(e.data,e.parentElement);else{if(e instanceof Element){if(e.hasAttribute(r))throw Error(`The '>' character is not allowed in attribute literals. Replace with '>'`);if(e.localName===`template`&&c(e.innerHTML,e),e.hasAttribute(t)){e.removeAttribute(t);let n=[...e.attributes].filter(e=>e.value===t);for(let t of n){e.removeAttributeNode(t);let n=te.exec(a[s.length])[1];s.push({type:_,path:o,attribute:n})}}}let n=e.childNodes,i=n.length;for(let e=0;e<i;e++)l(n[e],[...o,e])}};if(l(o.content,[]),s.length<a.length-1)throw Error("Double attribute assignments are not allowed: '<div a=${0} a=${0}>'");return s},y=`${t} ${t}`,b=`--><!--${n}--><!-- `,x=`<!--${i}-->`,S=/<[a-zA-Z/!?]/g,C=(e,t)=>{let n=e.lastIndexOf(`<!--`);if((t===b||n>-1)&&e.indexOf(`-->`,n+1)<0)return b;let r=e.lastIndexOf(`>`);return S.lastIndex=r+1,S.test(e)?y:r>-1?x:t},w=e=>{let t=``,n=x;for(let r of e.slice(0,-1)){if(n=C(r,n),n===y&&!r.endsWith(`=`))throw Error("Only bare attribute parts are allowed: `<div a=${0}>`");t+=r+n}return t+e.at(-1)},T=(e,t)=>{let n=document.createElement(`template`),r=w(e);if(t){n.innerHTML=`<svg>${r}</svg>`;let e=n.content.firstChild;e.replaceWith(...e.childNodes)}else n.innerHTML=r;let o=n.content.lastChild;return o instanceof Comment&&o.data===i&&n.content.append(a()),n},E=/* @__PURE__ */ new WeakMap,ne=class{strings;element;parts;constructor(e,t){this.strings=e,this.element=T(e,t),this.parts=v(e,this.element)}},D=class{strings;values;isSvg;_template;constructor(e,t,n){this.strings=e,this.values=t,this.isSvg=n,this._template=void 0}get template(){if(this._template)return this._template;let e=E.get(this.strings);return e||(e=new ne(this.strings,this.isSvg),E.set(this.strings,e)),this._template=e,e}},O=class{template;fragment;parts;constructor(e,t){this.template=e,this.fragment=document.importNode(e.element.content,!0),this.parts=e.parts.map(e=>{let n=this.fragment;return e.path.forEach(e=>{n=n.childNodes[e]}),`attribute`in e?new _({node:n,attribute:e.attribute,options:t}):e.type===g?new g({node:n}):new h({node:n,options:t})})}render(e){this.parts.forEach((t,n)=>t.render(e[n]))}},k=/* @__PURE__ */ new WeakMap,A=e=>l(t=>{let n=k.get(t);n||(n=/* @__PURE__ */ new Map,k.set(t,n));let r=e instanceof D?n.get(e.template):void 0;r&&t.instance!==r&&(t._renderNode(r.fragment),t.instance=r),t.render(e),t.instance&&n.set(t.instance.template,t.instance)}),j=/* @__PURE__ */ new WeakMap,re=(e,t)=>l(n=>{let r=j.get(n);(!r||r.length!==e.length||e.some((e,t)=>e!==r[t]))&&(j.set(n,[...e]),n.render(t()))}),ie=e=>l(t=>{e==null&&t instanceof _&&t.type===`attribute`?(t.node.removeAttribute(t.name),t.value=f):t.render(e)}),M=/* @__PURE__ */ new WeakMap,N=({beforeNode:e,afterNode:t})=>{let n=document.createRange();return n.setStartBefore(e),n.setEndAfter(t),n},P=(e,t,n)=>l(r=>{if(!n){r.render(Array.from(e,t));return}let i=M.get(r);(!i||r.node!==i)&&(r.clear(),r.value=r.promise=void 0,i=[]);let o=/* @__PURE__ */ new Map;for(let{key:e,itemPart:t}of i)o.has(e)?N(t).deleteContents():o.set(e,t);let s=r.parentNode,c=[],l=r.beforeNode.nextSibling,u=0;for(let i of e){let e=t(i,u),d=o.get(e);if(d)o.delete(e),d.beforeNode!==l&&s.insertBefore(N(d).extractContents(),l);else{let e=a();s.insertBefore(e,l),s.insertBefore(a(),l),d=new h({node:e,options:r.options})}d.render(n(i,u++)),c.push({key:e,itemPart:d}),l=d.afterNode.nextSibling}o.forEach(e=>N(e).deleteContents()),M.set(r,c),r.node=c}),F=/* @__PURE__ */ new WeakMap,I=e=>l(t=>{let n=F.get(t);if(!n||n.htmlString!==e||t.node!==n.fragment){let n=document.createElement(`template`);n.innerHTML=e;let r=document.importNode(n.content,!0);t.render(r),F.set(t,{htmlString:e,fragment:r})}}),L=/* @__PURE__ */ new WeakMap,R=(e,t)=>l(n=>{L.has(e)?n.render(L.get(e)):(e.then(t=>L.set(e,t),()=>{}),n.render(t),n.render(e))}),z=(e,t,n)=>l(r=>{e?r.render(t):r.render(n)}),B=/* @__PURE__ */ new WeakMap,V=(e,...t)=>new D(e,t),H=(e,...t)=>new D(e,t,!0),U=(e,t,n)=>{let r=B.get(t);if(!r){let e=a();t.replaceChildren(e),r=new h({node:e,options:n}),B.set(t,r)}r.render(e)},W={fromAttribute(e,t){switch(t){case Boolean:return e!==null;case Number:return e===null?null:Number(e);case Array:case Object:return e===null?null:JSON.parse(e);default:return e}},toAttribute(e,t){switch(t){case Boolean:return e?``:null;case Array:case Object:return e==null?null:JSON.stringify(e);default:return e==null?null:String(e)}}},G=(e,t)=>!Object.is(e,t),K=e=>e.replace(/([a-z](?=[A-Z]))|([A-Z](?=[A-Z][a-z]))/g,`$1$2-`).toLowerCase(),q=(e,{attribute:t})=>t?K(e):void 0,J=(e,...t)=>e.reduce((e,n,r)=>e+String(t[r-1])+n),Y=/* @__PURE__ */ new Map,ae=e=>{if(typeof e!=`string`)return e;let t=Y.get(e);return t||(t=new CSSStyleSheet,t.replaceSync(e),Y.set(e,t)),t},oe=e=>typeof e==`string`?e:Array.from(e.cssRules,e=>e.cssText).join(`
|
|
3
|
+
`),X=/* @__PURE__ */ new WeakMap,Z=/* @__PURE__ */ new WeakSet,se=(e,t)=>{let n;for(let r=e;r!==$.prototype&&!n;r=Object.getPrototypeOf(r))n=Object.getOwnPropertyDescriptor(r,t);if(n?.set&&Z.has(n.set))return;let r=Symbol(t),i=n?.get??function(){return this[r]},a=n?.set??function(e){this[r]=e},o=function(e){let n=i.call(this);a.call(this,e),this.requestUpdate(t,n)};Z.add(o),Object.defineProperty(e,t,{get:i,set:o,configurable:!0,enumerable:!0})},Q=e=>{let t=X.get(e);if(t)return t;let n=e===$?void 0:Q(Object.getPrototypeOf(e));if(t={properties:new Map(n?.properties),attributes:new Map(n?.attributes)},X.set(e,t),Object.hasOwn(e,`properties`))for(let[n,r]of Object.entries(e.properties)){t.properties.set(n,r);for(let[e,r]of t.attributes)r===n&&t.attributes.delete(e);let i=q(n,r);i&&t.attributes.set(i,n),se(e.prototype,n)}return t},ce=e=>{let t=Q(e);return t.styles??=[].concat(e.styles??[])},le=(e,t,n=customElements)=>{let r=n.get(t);return r?r!==e&&console.warn(`<${t}> is already defined with a different class, the existing definition is kept`):n.define(t,e),e},$=class extends HTMLElement{static properties={};static styles;static shadowRootOptions={mode:`open`};static get observedAttributes(){return[...Q(this).attributes.keys()]}renderRoot;isUpdatePending=!1;hasUpdated=!1;#e=/* @__PURE__ */ new Map;#t=/* @__PURE__ */ new Set;#n;#r;#i;#a=!1;#o;#s=new AbortController;constructor(){super(),Q(this.constructor),this.#r=new Promise(e=>this.#i=e),this.renderRoot=this.createRenderRoot(),this.#u(),this.requestUpdate()}createRenderRoot(){return this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions)}get disconnectSignal(){return this.#s.signal}get updateComplete(){return this.#r}connectedCallback(){if(this.#s.signal.aborted&&(this.#s=new AbortController),!this.#a){this.#a=!0,this.#l();for(let e of Q(this.constructor).properties.keys())this.requestUpdate(e,void 0);this.#i(!0)}}disconnectedCallback(){this.#s.abort()}attributeChangedCallback(e,t,n){let r=Q(this.constructor),i=r.attributes.get(e);if(i===void 0||this.#n===i)return;let a=r.properties.get(i),o=a.converter?.fromAttribute??W.fromAttribute;this.#n=i,this[i]=o(n,a.type),this.#n=void 0}requestUpdate(e,t){if(e!==void 0){let n=Q(this.constructor).properties.get(e)??{};if(!(n.hasChanged??G)(this[e],t))return;this.#e.has(e)||this.#e.set(e,t),n.reflect&&this.#n!==e&&this.#t.add(e)}this.isUpdatePending||(this.isUpdatePending=!0,this.#r=this.#c(this.#r))}performUpdate(){if(!this.isUpdatePending)return;let e=this.#e,t=!1;try{t=this.shouldUpdate(e),t&&(this.willUpdate(e),this.update(e))}finally{this.#e=/* @__PURE__ */ new Map,this.isUpdatePending=!1}t&&(this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e))}shouldUpdate(e){return!0}willUpdate(e){}update(e){for(let e of this.#t)this.#d(e);this.#t.clear();let t=this.render();if(t!==f&&(U(t,this.renderRoot,{host:this}),this.#o!==void 0)){let e=document.createElement(`style`);e.textContent=this.#o,this.renderRoot.prepend(e),this.#o=void 0}}render(){return f}firstUpdated(e){}updated(e){}async#c(e){await e;try{this.performUpdate()}catch(e){Promise.reject(e)}return!this.isUpdatePending}#l(){let e=Object.getPrototypeOf(this),t=this;for(let n of Object.getOwnPropertyNames(this))if(n in e){let e=t[n];delete t[n],t[n]=e}}#u(){let e=this.renderRoot,t=ce(this.constructor);e instanceof ShadowRoot&&t.length!==0&&(`adoptedStyleSheets`in e?e.adoptedStyleSheets=t.map(ae):this.#o=t.map(oe).join(`
|
|
4
|
+
`))}#d(e){let t=Q(this.constructor).properties.get(e),n=q(e,t);if(n===void 0)return;let r=(t.converter?.toAttribute??W.toAttribute)(this[e],t.type);this.#n=e,r===null?this.removeAttribute(n):this.setAttribute(n,r),this.#n=void 0}};export{$ as GluonElement,A as cache,J as css,le as define,re as guard,V as html,ie as ifDefined,f as noChange,U as render,P as repeat,H as svg,I as unsafeHTML,R as until,z as when};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2026 Goffert van Gool
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
export * from "lite-html";
|
|
7
|
+
export { GluonElement, define } from "./gluon.js";
|
|
8
|
+
export { type PropertyType, type AttributeConverter, type PropertyDeclaration, type PropertyDeclarations, type PropertyValues, } from "./properties.js";
|
|
9
|
+
export { type StyleSheet, type Styles, css } from "./styles.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/** @license Copyright (c) 2026 Goffert van Gool, SPDX-License-Identifier: MIT */
|
|
2
|
+
import { noChange, render } from "lite-html";
|
|
3
|
+
|
|
4
|
+
export * from "lite-html"
|
|
5
|
+
|
|
6
|
+
//#region src/properties.ts
|
|
7
|
+
var defaultConverter = {
|
|
8
|
+
fromAttribute(value, type) {
|
|
9
|
+
switch (type) {
|
|
10
|
+
case Boolean: return value !== null;
|
|
11
|
+
case Number: return value === null ? null : Number(value);
|
|
12
|
+
case Array:
|
|
13
|
+
case Object: return value === null ? null : JSON.parse(value);
|
|
14
|
+
default: return value;
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
toAttribute(value, type) {
|
|
18
|
+
switch (type) {
|
|
19
|
+
case Boolean: return value ? "" : null;
|
|
20
|
+
case Array:
|
|
21
|
+
case Object: return value == null ? null : JSON.stringify(value);
|
|
22
|
+
default: return value == null ? null : String(value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var notEqual = (value, oldValue) => !Object.is(value, oldValue);
|
|
27
|
+
var camelToKebab = (camel) => camel.replace(/([a-z](?=[A-Z]))|([A-Z](?=[A-Z][a-z]))/g, "$1$2-").toLowerCase();
|
|
28
|
+
var attributeName = (name, { attribute }) => attribute ? camelToKebab(name) : void 0;
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/styles.ts
|
|
32
|
+
var css = (strings, ...values) => strings.reduce((result, string, index) => result + String(values[index - 1]) + string);
|
|
33
|
+
var styleSheets = /* @__PURE__ */ new Map();
|
|
34
|
+
var toStyleSheet = (style) => {
|
|
35
|
+
if (typeof style !== "string") return style;
|
|
36
|
+
let sheet = styleSheets.get(style);
|
|
37
|
+
if (!sheet) {
|
|
38
|
+
sheet = new CSSStyleSheet();
|
|
39
|
+
sheet.replaceSync(style);
|
|
40
|
+
styleSheets.set(style, sheet);
|
|
41
|
+
}
|
|
42
|
+
return sheet;
|
|
43
|
+
};
|
|
44
|
+
var toCSSText = (style) => typeof style === "string" ? style : Array.from(style.cssRules, (rule) => rule.cssText).join("\n");
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/gluon.ts
|
|
48
|
+
var classInfos = /* @__PURE__ */ new WeakMap();
|
|
49
|
+
var reactiveSetters = /* @__PURE__ */ new WeakSet();
|
|
50
|
+
var defineAccessor = (prototype, name) => {
|
|
51
|
+
let descriptor;
|
|
52
|
+
for (let object = prototype; object !== GluonElement.prototype && !descriptor; object = Object.getPrototypeOf(object)) descriptor = Object.getOwnPropertyDescriptor(object, name);
|
|
53
|
+
if (descriptor?.set && reactiveSetters.has(descriptor.set)) return;
|
|
54
|
+
const key = Symbol(name);
|
|
55
|
+
const get = descriptor?.get ?? function() {
|
|
56
|
+
return this[key];
|
|
57
|
+
};
|
|
58
|
+
const set = descriptor?.set ?? function(value) {
|
|
59
|
+
this[key] = value;
|
|
60
|
+
};
|
|
61
|
+
const reactiveSet = function(value) {
|
|
62
|
+
const oldValue = get.call(this);
|
|
63
|
+
set.call(this, value);
|
|
64
|
+
this.requestUpdate(name, oldValue);
|
|
65
|
+
};
|
|
66
|
+
reactiveSetters.add(reactiveSet);
|
|
67
|
+
Object.defineProperty(prototype, name, {
|
|
68
|
+
get,
|
|
69
|
+
set: reactiveSet,
|
|
70
|
+
configurable: true,
|
|
71
|
+
enumerable: true
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
var finalize = (ctor) => {
|
|
75
|
+
let info = classInfos.get(ctor);
|
|
76
|
+
if (info) return info;
|
|
77
|
+
const inherited = ctor === GluonElement ? void 0 : finalize(Object.getPrototypeOf(ctor));
|
|
78
|
+
info = {
|
|
79
|
+
properties: new Map(inherited?.properties),
|
|
80
|
+
attributes: new Map(inherited?.attributes)
|
|
81
|
+
};
|
|
82
|
+
classInfos.set(ctor, info);
|
|
83
|
+
if (Object.hasOwn(ctor, "properties")) for (const [name, declaration] of Object.entries(ctor.properties)) {
|
|
84
|
+
info.properties.set(name, declaration);
|
|
85
|
+
for (const [attribute, property] of info.attributes) if (property === name) info.attributes.delete(attribute);
|
|
86
|
+
const attribute = attributeName(name, declaration);
|
|
87
|
+
if (attribute) info.attributes.set(attribute, name);
|
|
88
|
+
defineAccessor(ctor.prototype, name);
|
|
89
|
+
}
|
|
90
|
+
return info;
|
|
91
|
+
};
|
|
92
|
+
var getStyles = (ctor) => {
|
|
93
|
+
const info = finalize(ctor);
|
|
94
|
+
return info.styles ??= [].concat(ctor.styles ?? []);
|
|
95
|
+
};
|
|
96
|
+
var define = (ctor, name, registry = customElements) => {
|
|
97
|
+
const existing = registry.get(name);
|
|
98
|
+
if (!existing) registry.define(name, ctor);
|
|
99
|
+
else if (existing !== ctor) console.warn(`<${name}> is already defined with a different class, the existing definition is kept`);
|
|
100
|
+
return ctor;
|
|
101
|
+
};
|
|
102
|
+
var GluonElement = class extends HTMLElement {
|
|
103
|
+
static properties = {};
|
|
104
|
+
static styles;
|
|
105
|
+
static shadowRootOptions = { mode: "open" };
|
|
106
|
+
static get observedAttributes() {
|
|
107
|
+
return [...finalize(this).attributes.keys()];
|
|
108
|
+
}
|
|
109
|
+
renderRoot;
|
|
110
|
+
isUpdatePending = false;
|
|
111
|
+
hasUpdated = false;
|
|
112
|
+
#changed = /* @__PURE__ */ new Map();
|
|
113
|
+
#reflecting = /* @__PURE__ */ new Set();
|
|
114
|
+
#reflectingProperty;
|
|
115
|
+
#updatePromise;
|
|
116
|
+
#enableUpdating;
|
|
117
|
+
#connected = false;
|
|
118
|
+
#styleText;
|
|
119
|
+
#disconnect = new AbortController();
|
|
120
|
+
constructor() {
|
|
121
|
+
super();
|
|
122
|
+
finalize(this.constructor);
|
|
123
|
+
this.#updatePromise = new Promise((resolve) => this.#enableUpdating = resolve);
|
|
124
|
+
this.renderRoot = this.createRenderRoot();
|
|
125
|
+
this.#adoptStyles();
|
|
126
|
+
this.requestUpdate();
|
|
127
|
+
}
|
|
128
|
+
createRenderRoot() {
|
|
129
|
+
return this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions);
|
|
130
|
+
}
|
|
131
|
+
get disconnectSignal() {
|
|
132
|
+
return this.#disconnect.signal;
|
|
133
|
+
}
|
|
134
|
+
get updateComplete() {
|
|
135
|
+
return this.#updatePromise;
|
|
136
|
+
}
|
|
137
|
+
connectedCallback() {
|
|
138
|
+
if (this.#disconnect.signal.aborted) this.#disconnect = new AbortController();
|
|
139
|
+
if (!this.#connected) {
|
|
140
|
+
this.#connected = true;
|
|
141
|
+
this.#upgradeProperties();
|
|
142
|
+
for (const name of finalize(this.constructor).properties.keys()) this.requestUpdate(name, void 0);
|
|
143
|
+
this.#enableUpdating(true);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
disconnectedCallback() {
|
|
147
|
+
this.#disconnect.abort();
|
|
148
|
+
}
|
|
149
|
+
attributeChangedCallback(name, _oldValue, value) {
|
|
150
|
+
const info = finalize(this.constructor);
|
|
151
|
+
const property = info.attributes.get(name);
|
|
152
|
+
if (property === void 0 || this.#reflectingProperty === property) return;
|
|
153
|
+
const declaration = info.properties.get(property);
|
|
154
|
+
const fromAttribute = declaration.converter?.fromAttribute ?? defaultConverter.fromAttribute;
|
|
155
|
+
this.#reflectingProperty = property;
|
|
156
|
+
this[property] = fromAttribute(value, declaration.type);
|
|
157
|
+
this.#reflectingProperty = void 0;
|
|
158
|
+
}
|
|
159
|
+
requestUpdate(name, oldValue) {
|
|
160
|
+
if (name !== void 0) {
|
|
161
|
+
const declaration = finalize(this.constructor).properties.get(name) ?? {};
|
|
162
|
+
if (!(declaration.hasChanged ?? notEqual)(this[name], oldValue)) return;
|
|
163
|
+
if (!this.#changed.has(name)) this.#changed.set(name, oldValue);
|
|
164
|
+
if (declaration.reflect && this.#reflectingProperty !== name) this.#reflecting.add(name);
|
|
165
|
+
}
|
|
166
|
+
if (!this.isUpdatePending) {
|
|
167
|
+
this.isUpdatePending = true;
|
|
168
|
+
this.#updatePromise = this.#enqueueUpdate(this.#updatePromise);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
performUpdate() {
|
|
172
|
+
if (!this.isUpdatePending) return;
|
|
173
|
+
const changed = this.#changed;
|
|
174
|
+
let shouldUpdate = false;
|
|
175
|
+
try {
|
|
176
|
+
shouldUpdate = this.shouldUpdate(changed);
|
|
177
|
+
if (shouldUpdate) {
|
|
178
|
+
this.willUpdate(changed);
|
|
179
|
+
this.update(changed);
|
|
180
|
+
}
|
|
181
|
+
} finally {
|
|
182
|
+
this.#changed = /* @__PURE__ */ new Map();
|
|
183
|
+
this.isUpdatePending = false;
|
|
184
|
+
}
|
|
185
|
+
if (shouldUpdate) {
|
|
186
|
+
if (!this.hasUpdated) {
|
|
187
|
+
this.hasUpdated = true;
|
|
188
|
+
this.firstUpdated(changed);
|
|
189
|
+
}
|
|
190
|
+
this.updated(changed);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
shouldUpdate(_changed) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
willUpdate(_changed) {}
|
|
197
|
+
update(_changed) {
|
|
198
|
+
for (const name of this.#reflecting) this.#propertyToAttribute(name);
|
|
199
|
+
this.#reflecting.clear();
|
|
200
|
+
const result = this.render();
|
|
201
|
+
if (result !== noChange) {
|
|
202
|
+
render(result, this.renderRoot, { host: this });
|
|
203
|
+
if (this.#styleText !== void 0) {
|
|
204
|
+
const style = document.createElement("style");
|
|
205
|
+
style.textContent = this.#styleText;
|
|
206
|
+
this.renderRoot.prepend(style);
|
|
207
|
+
this.#styleText = void 0;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
render() {
|
|
212
|
+
return noChange;
|
|
213
|
+
}
|
|
214
|
+
firstUpdated(_changed) {}
|
|
215
|
+
updated(_changed) {}
|
|
216
|
+
async #enqueueUpdate(previous) {
|
|
217
|
+
await previous;
|
|
218
|
+
try {
|
|
219
|
+
this.performUpdate();
|
|
220
|
+
} catch (error) {
|
|
221
|
+
Promise.reject(error);
|
|
222
|
+
}
|
|
223
|
+
return !this.isUpdatePending;
|
|
224
|
+
}
|
|
225
|
+
#upgradeProperties() {
|
|
226
|
+
const prototype = Object.getPrototypeOf(this);
|
|
227
|
+
const properties = this;
|
|
228
|
+
for (const name of Object.getOwnPropertyNames(this)) if (name in prototype) {
|
|
229
|
+
const value = properties[name];
|
|
230
|
+
delete properties[name];
|
|
231
|
+
properties[name] = value;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
#adoptStyles() {
|
|
235
|
+
const root = this.renderRoot;
|
|
236
|
+
const styles = getStyles(this.constructor);
|
|
237
|
+
if (!(root instanceof ShadowRoot) || styles.length === 0) return;
|
|
238
|
+
if ("adoptedStyleSheets" in root) root.adoptedStyleSheets = styles.map(toStyleSheet);
|
|
239
|
+
else this.#styleText = styles.map(toCSSText).join("\n");
|
|
240
|
+
}
|
|
241
|
+
#propertyToAttribute(name) {
|
|
242
|
+
const declaration = finalize(this.constructor).properties.get(name);
|
|
243
|
+
const attribute = attributeName(name, declaration);
|
|
244
|
+
if (attribute === void 0) return;
|
|
245
|
+
const value = (declaration.converter?.toAttribute ?? defaultConverter.toAttribute)(this[name], declaration.type);
|
|
246
|
+
this.#reflectingProperty = name;
|
|
247
|
+
if (value === null) this.removeAttribute(attribute);
|
|
248
|
+
else this.setAttribute(attribute, value);
|
|
249
|
+
this.#reflectingProperty = void 0;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
//#endregion
|
|
254
|
+
export { GluonElement, css, define };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2026 Goffert van Gool
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The type of a declared property, used by the default converter to read and write attributes
|
|
8
|
+
*/
|
|
9
|
+
export type PropertyType = StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor;
|
|
10
|
+
/**
|
|
11
|
+
* Converts between a property value and its attribute value
|
|
12
|
+
*
|
|
13
|
+
* `fromAttribute` is called with `null` when the attribute is removed
|
|
14
|
+
* `toAttribute` returns `null` to remove the attribute
|
|
15
|
+
*/
|
|
16
|
+
export interface AttributeConverter {
|
|
17
|
+
fromAttribute?: (value: string | null, type?: PropertyType) => unknown;
|
|
18
|
+
toAttribute?: (value: unknown, type?: PropertyType) => string | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The declaration of a reactive property in the static `properties` map of an element class
|
|
22
|
+
*/
|
|
23
|
+
export interface PropertyDeclaration {
|
|
24
|
+
/** The type of the property, as a hint for the default converter. Defaults to `String` */
|
|
25
|
+
type?: PropertyType;
|
|
26
|
+
/** Whether the property is set from the attribute with its kebab-cased name. Defaults to `false` */
|
|
27
|
+
attribute?: boolean;
|
|
28
|
+
/** Whether the property value is written to its attribute when it changes, with `attribute`. Defaults to `false` */
|
|
29
|
+
reflect?: boolean;
|
|
30
|
+
/** A converter between the property value and its attribute. Defaults to the built-in converter */
|
|
31
|
+
converter?: AttributeConverter;
|
|
32
|
+
/** Decides whether a new value counts as a change. Defaults to `!Object.is(value, oldValue)` */
|
|
33
|
+
hasChanged?: (value: unknown, oldValue: unknown) => boolean;
|
|
34
|
+
}
|
|
35
|
+
export type PropertyDeclarations = Record<string, PropertyDeclaration>;
|
|
36
|
+
/**
|
|
37
|
+
* A map of the changed properties to their previous values, passed to the update lifecycle methods
|
|
38
|
+
*
|
|
39
|
+
* The keys are typed as the properties of the element, so `changed.has('count')` is checked
|
|
40
|
+
*/
|
|
41
|
+
export interface PropertyValues<T = unknown> extends Map<PropertyKey, unknown> {
|
|
42
|
+
get<K extends keyof T>(key: K): T[K] | undefined;
|
|
43
|
+
has<K extends keyof T>(key: K): boolean;
|
|
44
|
+
set<K extends keyof T>(key: K, value: T[K]): this;
|
|
45
|
+
delete<K extends keyof T>(key: K): boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The converter that is used for properties without their own
|
|
49
|
+
*
|
|
50
|
+
* `String` properties are copied, `Number` properties are parsed with `Number()`,
|
|
51
|
+
* `Boolean` properties are `true` when the attribute is present, and `Array` and `Object` properties are JSON
|
|
52
|
+
* A property that is `null` or `undefined` removes its attribute
|
|
53
|
+
*/
|
|
54
|
+
export declare const defaultConverter: Required<AttributeConverter>;
|
|
55
|
+
/**
|
|
56
|
+
* The change detection that is used for properties without their own
|
|
57
|
+
*/
|
|
58
|
+
export declare const notEqual: (value: unknown, oldValue: unknown) => boolean;
|
|
59
|
+
export declare const camelToKebab: (camel: string) => string;
|
|
60
|
+
export declare const attributeName: (name: string, { attribute }: PropertyDeclaration) => string | undefined;
|
package/dist/styles.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2026 Goffert van Gool
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* A stylesheet for an element: CSS text, or a constructed `CSSStyleSheet`
|
|
8
|
+
*/
|
|
9
|
+
export type StyleSheet = string | CSSStyleSheet;
|
|
10
|
+
export type Styles = StyleSheet | readonly StyleSheet[];
|
|
11
|
+
/**
|
|
12
|
+
* Tagging function for CSS in template string literals
|
|
13
|
+
*
|
|
14
|
+
* Returns the CSS as a string, for the static `styles` of an element. The tag marks the string as CSS
|
|
15
|
+
* for editors, formatters, and linters. Interpolated values are converted to strings, so other styles
|
|
16
|
+
* and numbers can be composed into it.
|
|
17
|
+
*/
|
|
18
|
+
export declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => string;
|
|
19
|
+
/**
|
|
20
|
+
* Returns a constructed stylesheet for a style, sharing one sheet per CSS string
|
|
21
|
+
*/
|
|
22
|
+
export declare const toStyleSheet: (style: StyleSheet) => CSSStyleSheet;
|
|
23
|
+
/**
|
|
24
|
+
* Returns the CSS text of a style, for a `<style>` element
|
|
25
|
+
*/
|
|
26
|
+
export declare const toCSSText: (style: StyleSheet) => string;
|
package/package.json
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gluon/gluon",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "A tiny
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"module": "gluon.js",
|
|
8
|
-
"jsnext:main": "gluon.js",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "A tiny base class for Web Components, rendered with lite-html",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
9
7
|
"files": [
|
|
10
|
-
"
|
|
8
|
+
"dist",
|
|
9
|
+
"CHANGELOG.md"
|
|
11
10
|
],
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"unpkg": "dist/gluon.min.js",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"dev": "vite serve dev",
|
|
25
|
+
"build": "vite build && vite build --mode bundle && tsc -p tsconfig.build.json",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:watch": "vitest",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"format": "prettier --write .",
|
|
30
|
+
"format:check": "prettier --check .",
|
|
31
|
+
"check": "npm run typecheck && npm run format:check && npm test",
|
|
32
|
+
"prepublishOnly": "npm run check && npm run build"
|
|
33
|
+
},
|
|
12
34
|
"repository": {
|
|
13
35
|
"type": "git",
|
|
14
36
|
"url": "git+https://github.com/ruphin/gluonjs.git"
|
|
15
37
|
},
|
|
38
|
+
"homepage": "https://github.com/ruphin/gluonjs",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/ruphin/gluonjs/issues"
|
|
41
|
+
},
|
|
16
42
|
"author": "Goffert van Gool <ruphin@ruphin.net>",
|
|
17
43
|
"keywords": [
|
|
18
44
|
"web-components",
|
|
19
45
|
"webcomponents",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
46
|
+
"lite-html",
|
|
47
|
+
"custom-elements"
|
|
22
48
|
],
|
|
23
|
-
"license": "MIT",
|
|
24
49
|
"publishConfig": {
|
|
25
50
|
"access": "public"
|
|
26
51
|
},
|
|
27
|
-
"
|
|
28
|
-
"
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=20"
|
|
29
54
|
},
|
|
30
|
-
"scripts": {
|
|
31
|
-
"build": "rollup -c && cp src/gluon.js gluon.js",
|
|
32
|
-
"dev": "browser-sync start --config browsersync.config.js",
|
|
33
|
-
"release": "np",
|
|
34
|
-
"test": "karma start",
|
|
35
|
-
"guard": "karma start --no-single-run"
|
|
36
|
-
},
|
|
37
|
-
"homepage": "https://github.com/ruphin/gluonjs",
|
|
38
55
|
"dependencies": {
|
|
39
|
-
"
|
|
56
|
+
"lite-html": "^1.2.0"
|
|
40
57
|
},
|
|
41
58
|
"devDependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"chai": "4.2.0",
|
|
49
|
-
"karma": "3.0.0",
|
|
50
|
-
"karma-chai": "0.1.0",
|
|
51
|
-
"karma-chrome-launcher": "2.2.0",
|
|
52
|
-
"karma-firefox-launcher": "1.1.0",
|
|
53
|
-
"karma-mocha": "1.3.0",
|
|
54
|
-
"karma-mocha-reporter": "2.2.5",
|
|
55
|
-
"mocha": "6.0.0",
|
|
56
|
-
"np": "4.0.2",
|
|
57
|
-
"rollup": "1.2.2",
|
|
58
|
-
"rollup-plugin-babel": "4.3.2",
|
|
59
|
-
"rollup-plugin-commonjs": "9.2.0",
|
|
60
|
-
"rollup-plugin-cleanup": "3.1.1",
|
|
61
|
-
"rollup-plugin-filesize": "6.0.1",
|
|
62
|
-
"rollup-plugin-includepaths": "0.2.3",
|
|
63
|
-
"rollup-plugin-node-resolve": "4.0.0",
|
|
64
|
-
"rollup-plugin-terser": "4.0.4"
|
|
59
|
+
"@types/node": "^26.6.4",
|
|
60
|
+
"jsdom": "^30.1.2",
|
|
61
|
+
"prettier": "^3.9.9",
|
|
62
|
+
"typescript": "^7.0.2",
|
|
63
|
+
"vite": "^8.3.2",
|
|
64
|
+
"vitest": "^5.0.3"
|
|
65
65
|
}
|
|
66
66
|
}
|