wrec 0.9.0 → 0.10.3

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 (2) hide show
  1. package/dist/wrec.min.js +1 -1
  2. package/package.json +14 -4
package/dist/wrec.min.js CHANGED
@@ -1 +1 @@
1
- const FIRST_CHAR="a-zA-Z_$",OTHER_CHAR=FIRST_CHAR+"0-9",IDENTIFIER=`[${FIRST_CHAR}][${OTHER_CHAR}]*`,REFERENCE_RE=new RegExp(`^this.${IDENTIFIER}$`),REFERENCES_RE=new RegExp(`this.${IDENTIFIER}`,"g"),SKIP=5;export function createElement(t,e,s){const r=document.createElement(t);if(e)for(const[t,s]of Object.entries(e))r.setAttribute(t,s);return s&&(r.innerHTML=s),r}const defaultForType=t=>t===Number?0:t!==Boolean&&"";function isPrimitive(t){const e=typeof t;return"string"===e||"number"===e||"boolean"===e}function updateAttribute(t,e,s){if(!isPrimitive(s))return;const r=t.getAttribute(e);"boolean"==typeof s?s?r!==e&&t.setAttribute(e,e):t.removeAttribute(e):r!==s&&t.setAttribute(e,s)}function updateValue(t,e,s){t instanceof CSSRule?t.style.setProperty(e,s):updateAttribute(t,e,s)}class Wrec extends HTMLElement{static#t=new Map;static#e=new Map;#s=new Map;#r;#o;#i=new Map;propToParentPropMap=new Map;constructor(){super(),this.attachShadow({mode:"open"}),this.constructor.properties||(this.constructor.properties={}),this.constructor["#propToExprsMap"]||(this.constructor["#propToExprsMap"]=new Map),this.constructor.formAssociated&&(this.#o=this.attachInternals(),this.#r=new FormData,this.#o.setFormValue(this.#r))}attributeChangedCallback(t,e,s){const r=Wrec.getPropName(t),o=this.#n(r,s);this[r]=o,this.#a(r,o)}#c(t,e,s,r){t.addEventListener(r,t=>{this[e]=t.target.value});let o=this.#i.get(e);o||(o=[],this.#i.set(e,o)),o.push(s?{element:t,attrName:s}:t)}#p(){const t=this.constructor;let{_template:e}=t;if(!e){e=t.template=document.createElement("template");let s=t.css?`<style>${t.css}</style>`:"";s+=t.html,e.innerHTML=s}this.shadowRoot.replaceChildren(e.content.cloneNode(!0))}connectedCallback(){this.#u(),this.#l(),this.#p(),requestAnimationFrame(()=>{this.#h(this.shadowRoot),this.#m(this.shadowRoot),this.constructor.processed=!0,this.#f()})}#f(){const{properties:t}=this.constructor;for(const[e,{computed:s}]of Object.entries(t))s&&(this[e]=this.#d(s))}#l(){const{observedAttributes:t,properties:e}=this.constructor;for(const[s,r]of Object.entries(e))this.#E(s,r,t)}#E(t,e,s){const r=Wrec.getAttrName(t);e.required&&!this.hasAttribute(r)&&this.#b(this,t,"is a required attribute");const{type:o,value:i}=e,n=s.includes(r)&&this.hasAttribute(r)?this.#g(t,r):i||defaultForType(o),a="#"+t;this[a]=n,e.computed&&this.#R(t,e),Object.defineProperty(this,t,{enumerable:!0,get(){return this[a]},set(s){const o=this[a];if(s===o)return;this[a]=s;let i=this.constructor["#propToComputedMap"];if(i){const e=i.get(t)||[];for(const[t,s]of e)this[t]=this.#d(s)}if(isPrimitive(s)&&this.hasAttribute(r)){s!==this.#g(t,r)&&updateAttribute(this,t,s)}this.#v(t);const n=this.propToParentPropMap.get(t);if(n){this.getRootNode().host[n]=s}isPrimitive(s)&&this.#a(t,s),this.propertyChangedCallback&&this.propertyChangedCallback(t,o,s),e.dispatch&&this.dispatchEvent(new CustomEvent("change",{bubbles:!0,composed:!0,detail:{propName:t}}))}})}static elementName(){return this.name.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}#N(t){const e=t.localName.includes("-");for(const s of t.getAttributeNames()){const r=t.getAttribute(s),o=this.#A(t,r);if(o){const r=this[o];void 0===r&&this.#C(t,s,o),t[o]=r;let[i,n]=s.split(":");"value"===i&&(n?t.setAttribute(i,this[o]):n="change",this.#c(t,o,i,n)),e&&t.propToParentPropMap.set(Wrec.getPropName(i),o)}this.#T(r,t,s)}}#d(expr){return(()=>eval(expr)).call(this)}#w(t){const{localName:e}=t;if("style"===e){for(const e of t.sheet.cssRules)if(e.type===CSSRule.STYLE_RULE){const t=[...e.style];for(const s of t)if(s.startsWith("--")){const t=e.style.getPropertyValue(s);this.#T(t,e,s)}}}else{let s=t.textContent.trim();s||t.childNodes.forEach(t=>{t.nodeType===Node.COMMENT_NODE&&(s+=t.textContent.trim())});const r=this.#A(t,s);"textarea"===e&&r?(this.#c(t,r,null,"change"),t.textContent=this[r]):this.#T(s,t)}}static getAttrName(t){let e=Wrec.#t.get(t);return e||(e=t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),Wrec.#t.set(t,e)),e}static getPropName(t){let e=Wrec.#e.get(t);return e||(e=t.replace(/-([a-z])/g,(t,e)=>e.toUpperCase()),Wrec.#e.set(t,e)),e}#m(t){const e=t.querySelectorAll("*");for(const t of e)this.#N(t),t.firstElementChild||this.#w(t)}static get observedAttributes(){return Object.keys(this.properties||{}).map(Wrec.getAttrName)}#A(t,e){if(!REFERENCE_RE.test(e))return;const s=e.substring(SKIP);return void 0===this[s]&&this.#C(t,null,s),s}#v(t){const e=this.constructor["#propToExprsMap"].get(t)||[];for(const t of e){const e=this.#d(t),s=this.#s.get(t)||[];for(const t of s)t instanceof Element?this.#M(t,e):updateValue(t.element,t.attrName,e)}requestAnimationFrame(()=>{this.#x(t)})}static register(){const t=this.elementName();customElements.get(t)||customElements.define(t,this)}#R(t,e){const{computed:s,uses:r}=e;let o=this.constructor["#propToComputedMap"];function i(e,s){let r=o.get(e);r||(r=[],o.set(e,r)),r.push([t,s])}o||(o=this.constructor["#propToComputedMap"]=new Map);const n=s.match(REFERENCES_RE)||[];for(const e of n){const r=e.substring(SKIP);void 0===this[r]&&this.#C(null,t,r),"function"!=typeof this[r]&&i(r,s)}if(r)for(const t of r.split(","))i(t,s)}#T(t,e,s){const r=this.#P(e,s,t);if(!r){const r=t.replaceAll("this..","this.");return void(s?updateValue(e,s,r):e.textContent=r)}this.constructor.processed||r.forEach(e=>{const s=e.substring(SKIP),r=this.constructor["#propToExprsMap"];let o=r.get(s);o||(o=[],r.set(s,o)),o.includes(t)||o.push(t)});let o=this.#s.get(t);o||(o=[],this.#s.set(t,o)),o.push(s?{element:e,attrName:s}:e);const i=this.#d(t);s?updateValue(e,s,i):this.#M(e,i)}#a(t,e){this.#r&&(this.#r.set(t,e),this.#o.setFormValue(this.#r))}#b(t,e,s){throw new Error(`component ${this.constructor.elementName()}`+(t?`, element "${t.localName}"`:"")+(e?`, attribute "${e}"`:"")+` ${s}`)}#C(t,e,s){this.#b(t,e,`refers to missing property "${s}"`)}#g(t,e){return this.#n(t,this.getAttribute(e))}#n(t,e){if(e?.match(REFERENCES_RE))return e;const{type:s}=this.constructor.properties[t];if(s===String)return e;if(s===Number){const s=Number(e);if(!isNaN(s))return s;this.#b(null,t,`must be a number, but was "${e}"`)}if(s===Boolean)return"true"===e||"false"!==e&&(e&&e!==t&&this.#b(null,t,"is a Boolean attribute, so its value must match attribute name or be missing"),e===t);this.#b(null,t,"does not specify its type")}#x(t){const e=this[t],s=this.#i.get(t)||[];for(const t of s)if(t instanceof Element)"textarea"===t.localName?t.value=e:t.textContent=e;else{const{element:s,attrName:r}=t;updateAttribute(s,r,e),s[r]=e}}#M(t,e){const{localName:s}=t,r=typeof e;"string"!==r&&"number"!==r&&this.#b(t,null," computed content is not a string or number"),"textarea"===s?t.value=e:"string"===r&&e.trim().startsWith("<")?(t.innerHTML=e,this.#h(t),this.#m(t)):t.textContent=e}#u(){const t=new Set(Object.keys(this.constructor.properties));for(const e of this.getAttributeNames())"id"!==e&&(e.startsWith("on")||t.has(Wrec.getPropName(e))||this.#b(null,e,"is not a supported attribute"))}#P(t,e,s){const r=s.match(REFERENCES_RE);if(r)return r.forEach(s=>{const r=s.substring(SKIP);void 0===this[r]&&this.#C(t,e,r)}),r}#h(root){for(const element of root.querySelectorAll("*")){const attributesToRemove=[];for(const attr of element.attributes){const attrName=attr.name;if(attrName.startsWith("on")){const eventName=attrName.slice(2).toLowerCase(),attrValue=attr.value;let fn;this.#P(element,attrName,attrValue),"function"==typeof this[attrValue]?fn=t=>this[attrValue](t):(this.#P(element,attrName,attrValue),fn=()=>eval(attrValue)),element.addEventListener(eventName,fn),attributesToRemove.push(attrName)}}for(const t of attributesToRemove)element.removeAttribute(t)}}}export default Wrec;export const css=String.raw;export const html=String.raw;
1
+ const FIRST_CHAR="a-zA-Z_$",OTHER_CHAR=FIRST_CHAR+"0-9",IDENTIFIER=`[${FIRST_CHAR}][${OTHER_CHAR}]*`,REFERENCE_RE=new RegExp(`^this.${IDENTIFIER}$`),REFERENCES_RE=new RegExp(`this.${IDENTIFIER}`,"g"),SKIP=5;export function createElement(t,e,s){const o=document.createElement(t);if(e)for(const[t,s]of Object.entries(e))o.setAttribute(t,s);return s&&(o.innerHTML=s),o}const defaultForType=t=>t===Number?0:t!==Boolean&&"";function isPrimitive(t){const e=typeof t;return"string"===e||"number"===e||"boolean"===e}function updateAttribute(t,e,s){if(!isPrimitive(s))return;const o=t.getAttribute(e);"boolean"==typeof s?s?o!==e&&t.setAttribute(e,e):t.removeAttribute(e):o!==s&&t.setAttribute(e,String(s))}function updateValue(t,e,s){t instanceof CSSRule?t.style.setProperty(e,s):updateAttribute(t,e,s)}class Wrec extends HTMLElement{static#t=new Map;static#e=new Map;static _template=null;static css="";static html="";static formAssociated=!1;static processed=!1;static properties={};static propToComputedMap=null;static propToExprsMap=null;#s=this.constructor;#o=new Map;#r;#i=null;#n=new Map;propToParentPropMap=new Map;constructor(){super(),this.attachShadow({mode:"open"});const t=this.#s;t.properties||(t.properties={}),t.propToComputedMap||(t.propToComputedMap=new Map),t.propToExprsMap||(t.propToExprsMap=new Map),t.formAssociated&&(this.#i=this.attachInternals(),this.#r=new FormData,this.#i.setFormValue(this.#r))}attributeChangedCallback(t,e,s){const o=Wrec.getPropName(t),r=this.#a(o,String(s));this[o]=r,this.#p(o,String(r))}#c(t,e,s,o){t.addEventListener(o,t=>{const s=t.target;this[e]=s.value});let r=this.#n.get(e);r||(r=[],this.#n.set(e,r)),r.push(s?{element:t,attrName:s}:t)}#h(){const t=this.#s;let e=t._template;if(!e){e=t._template=document.createElement("template");let s=t.css?`<style>${t.css}</style>`:"";s+=t.html,e.innerHTML=s}this.shadowRoot?.replaceChildren(e.content.cloneNode(!0))}connectedCallback(){this.#l(),this.#u(),this.#h(),requestAnimationFrame(()=>{this.shadowRoot&&(this.#m(this.shadowRoot),this.#f(this.shadowRoot));this.#s.processed=!0,this.#d()})}#d(){const t=this.#s,{properties:e}=t;for(const[t,{computed:s}]of Object.entries(e))s&&(this[t]=this.#E(s))}#u(){const t=this.#s,{observedAttributes:e,properties:s}=t;for(const[t,o]of Object.entries(s))this.#b(t,o,e)}#b(t,e,s){const o=Wrec.getAttrName(t);e.required&&!this.hasAttribute(o)&&this.#g(this,t,"is a required attribute");const{type:r,value:i}=e,n=r===Boolean?i||this.hasAttribute(o):s.includes(o)&&this.hasAttribute(o)?this.#R(t,o):i||defaultForType(r),a="#"+t;this[a]=n,e.computed&&this.#A(t,e),Object.defineProperty(this,t,{enumerable:!0,get(){return this[a]},set(s){const i=this[a];if(s===i)return;this[a]=s;const n=this.#s.propToComputedMap.get(t)||[];for(const[t,e]of n)this[t]=this.#E(e);if(isPrimitive(s)&&this.hasAttribute(o)){s!==(r===Boolean?this.hasAttribute(o):this.#R(t,o))&&updateAttribute(this,t,s)}this.#v(t);const p=this.propToParentPropMap.get(t);if(p){this.getRootNode().host[p]=s}isPrimitive(s)&&this.#p(t,s),this.propertyChangedCallback&&this.propertyChangedCallback(t,i,s),e.dispatch&&this.dispatchEvent(new CustomEvent("change",{bubbles:!0,composed:!0,detail:{propName:t}}))}})}static elementName(){return this.name.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}#C(t){const e=t instanceof Wrec;for(const s of t.getAttributeNames()){const o=t.getAttribute(s),r=this.#T(t,o);if(r){const o=this[r];void 0===o&&this.#M(t,s,r),t[r]=o;let[i,n]=s.split(":");"value"===i&&(n?t.setAttribute(i,this[r]):n="change",this.#c(t,r,i,n)),e&&t.propToParentPropMap.set(Wrec.getPropName(i),r)}this.#N(o,t,s)}}#E(expr){return(()=>eval(expr)).call(this)}#w(t){const{localName:e}=t;if("style"===e){const{sheet:e}=t,s=e?.cssRules??[],o=Array.from(s);for(const t of o)if(t.constructor===CSSStyleRule){const e=Array.from(t.style);for(const s of e)if(s.startsWith("--")){const e=t.style.getPropertyValue(s);this.#N(e,t,s)}}}else{let s=t.textContent?.trim()??"";s||t.childNodes.forEach(t=>{t.nodeType===Node.COMMENT_NODE&&(s+=t?.textContent?.trim()??"")});const o=this.#T(t,s);"textarea"===e&&o?(this.#c(t,o,null,"change"),t.textContent=this[o]):this.#N(s,t)}}static getAttrName(t){let e=Wrec.#t.get(t);return e||(e=t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),Wrec.#t.set(t,e)),e}static getPropName(t){let e=Wrec.#e.get(t);return e||(e=t.replace(/-([a-z])/g,(t,e)=>e.toUpperCase()),Wrec.#e.set(t,e)),e}#f(t){const e=Array.from(t.querySelectorAll("*"));for(const t of e)this.#C(t),t.firstElementChild||this.#w(t)}static get observedAttributes(){return Object.keys(this.properties||{}).map(Wrec.getAttrName)}#T(t,e){if(!e||!REFERENCE_RE.test(e))return;const s=e.substring(SKIP);return void 0===this[s]&&this.#M(t,"",s),s}#v(t){const e=this.#s.propToExprsMap.get(t)||[];for(const t of e){const e=this.#E(t),s=this.#o.get(t)||[];for(const t of s)t instanceof HTMLElement?this.#y(t,e):updateValue(t.element,t.attrName,e)}requestAnimationFrame(()=>{this.#x(t)})}static register(){const t=this.elementName();customElements.get(t)||customElements.define(t,this)}#A(t,e){const{computed:s,uses:o}=e,r=this.#s.propToComputedMap;function i(e,s){let o=r.get(e);o||(o=[],r.set(e,o)),o.push([t,s])}const n=s.match(REFERENCES_RE)||[];for(const e of n){const o=e.substring(SKIP);void 0===this[o]&&this.#M(null,t,o),"function"!=typeof this[o]&&i(o,s)}if(o)for(const t of o.split(","))i(t,s)}#N(t,e,s=void 0){if(!t)return;const o=this.#P(e,s,t);if(!o){const o=t.replaceAll("this..","this.");return void(s?updateValue(e,s,o):"textContent"in e&&(e.textContent=o))}const r=this.#s;r.processed||o.forEach(e=>{const s=e.substring(SKIP);if("function"==typeof this[s])return;const o=r.propToExprsMap;let i=o.get(s);i||(i=[],o.set(s,i)),i.includes(t)||i.push(t)});let i=this.#o.get(t);i||(i=[],this.#o.set(t,i)),i.push(s?{element:e,attrName:s}:e);const n=this.#E(t);s?updateValue(e,s,n):this.#y(e,n)}#p(t,e){this.#r&&(this.#r.set(t,e),this.#i?.setFormValue(this.#r))}#g(t,e,s){const o=this.#s,r=t instanceof HTMLElement?t.localName:"CSS rule";throw new Error(`component ${o.elementName()}`+(t?`, element "${r}"`:"")+(e?`, attribute "${e}"`:"")+` ${s}`)}#M(t,e,s){this.#g(t,e,`refers to missing property "${s}"`)}#R(t,e){return this.#a(t,this.getAttribute(e))}#a(t,e){if(e?.match(REFERENCES_RE))return e;const s=this.#s,{type:o}=s.properties[t];if(o===String)return e;if(o===Number){const s=Number(e);if(!isNaN(s))return s;this.#g(null,t,`must be a number, but was "${e}"`)}if(o===Boolean)return"true"===e||"false"!==e&&"null"!==e&&(e&&e!==t&&this.#g(null,t,"is a Boolean attribute, so its value must match attribute name or be missing"),e===t);this.#g(null,t,"does not specify its type")}#x(t){const e=this[t],s=this.#n.get(t)||[];for(const t of s)if(t instanceof HTMLElement)"textarea"===t.localName?t.value=e:t.textContent=e;else{const{element:s,attrName:o}=t;updateAttribute(s,o,e),s[o]=e}}#y(t,e){const s=t instanceof HTMLElement,o=s?t.localName:"",r=typeof e;"string"!==r&&"number"!==r&&this.#g(t,void 0," computed content is not a string or number"),"textarea"===o?t.value=e:s&&"string"===r&&e.trim().startsWith("<")?(t.innerHTML=e,this.#m(t),this.#f(t)):s&&(t.textContent=e)}#l(){const t=this.#s,e=new Set(Object.keys(t.properties));for(const t of this.getAttributeNames())"id"!==t&&(t.startsWith("on")||e.has(Wrec.getPropName(t))||this.#g(null,t,"is not a supported attribute"))}#P(t,e,s){const o=s.match(REFERENCES_RE);if(o)return o.forEach(s=>{const o=s.substring(SKIP);void 0===this[o]&&this.#M(t,e,o)}),o}#m(t){const e=Array.from(t.querySelectorAll("*"));for(const t of e){const e=[];for(const s of Array.from(t.attributes)){const o=s.name;if(o.startsWith("on")){const r=o.slice(2).toLowerCase(),i=s.value;let n;this.#P(t,o,i),"function"==typeof this[i]?n=t=>this[i](t):(this.#P(t,o,i),n=()=>this.#E(i)),t.addEventListener(r,n),e.push(o)}}for(const s of e)t.removeAttribute(s)}}}export default Wrec;export const css=String.raw;export const html=String.raw;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "wrec",
3
3
  "description": "a small library that greatly simplifies building web components",
4
4
  "author": "R. Mark Volkmann",
5
- "version": "0.9.0",
5
+ "version": "0.10.3",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
@@ -22,12 +22,22 @@
22
22
  "README.md"
23
23
  ],
24
24
  "scripts": {
25
+ "dev": "vite",
26
+ "build": "vite build",
27
+ "clean": "rm -rf dist",
28
+ "preview": "vite preview",
25
29
  "lint": "oxlint",
26
- "minify": "terser wrec.js -c -m -o dist/wrec.min.js",
27
- "prepare": "npm run minify"
30
+ "minify": "tsc && terser dist/wrec.js -c -m -o dist/wrec.min.js",
31
+ "reinstall": "rm -rf node_modules package-lock.json && npm install",
32
+ "test": "npx playwright test",
33
+ "testui": "npx playwright test --ui"
28
34
  },
29
35
  "devDependencies": {
36
+ "@playwright/test": "^1.54.1",
37
+ "@types/node": "^24.1.0",
30
38
  "oxlint": "^1.5.0",
31
- "terser": "^5.43.1"
39
+ "terser": "^5.43.1",
40
+ "typescript": "^5.8.3",
41
+ "vite": "^7.0.6"
32
42
  }
33
43
  }