From 0cc97206a77f47c887f79962ebfde9aed0e9e77f Mon Sep 17 00:00:00 2001 From: Alexander Brandon Coles Date: Thu, 18 Jun 2026 00:52:41 +0200 Subject: [PATCH 1/3] Use @callback to preserve Morph/NoOp signatures @typedef {Function} collapsed these to bare Function in generated declarations, losing the morph() call signature. @callback keeps it. This also surfaced that morph() actually returns Promise | Node[] (the restoreFocus path is async), not undefined | Node[]; @returns fixed. --- src/idiomorph.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/idiomorph.js b/src/idiomorph.js index 257c040..c314a09 100644 --- a/src/idiomorph.js +++ b/src/idiomorph.js @@ -34,7 +34,7 @@ */ /** - * @typedef {function} NoOp + * @callback NoOp * * @returns {void} */ @@ -81,12 +81,12 @@ */ /** - * @typedef {Function} Morph + * @callback Morph * * @param {Element | Document} oldNode * @param {Element | Node | HTMLCollection | Node[] | string | null} newContent * @param {Config} [config] - * @returns {undefined | Node[]} + * @returns {Promise | Node[]} */ // base IIFE to define idiomorph From 7e4362ead19f1ba05f9e1c14678027060df0c8b6 Mon Sep 17 00:00:00 2001 From: Alexander Brandon Coles Date: Thu, 18 Jun 2026 00:52:41 +0200 Subject: [PATCH 2/3] Generate and publish TypeScript declarations JSDoc types lived in src but were never shipped, forcing consumers to hand-maintain their own idiomorph.d.ts. - Add a 'types' script emitting module-shaped .d.ts from the ESM bundles (compiling src directly yields a global 'declare var' instead). - Wire it into 'dist'; expose via package.json 'types' + export conditions and ship dist/*.d.ts. - Scope the dist copy to src/*.js and gitignore src/*.d.ts so stray declarations can't leak; revert tsconfig to noEmit. --- .gitignore | 1 + dist/idiomorph-ext.d.ts | 156 ++++++++++++++++++++++++++++++++++++++ dist/idiomorph-ext.esm.js | 21 +++-- dist/idiomorph-ext.js | 21 +++-- dist/idiomorph-ext.min.js | 2 +- dist/idiomorph.amd.js | 21 +++-- dist/idiomorph.cjs.js | 21 +++-- dist/idiomorph.d.ts | 156 ++++++++++++++++++++++++++++++++++++++ dist/idiomorph.esm.js | 21 +++-- dist/idiomorph.js | 21 +++-- dist/idiomorph.min.js | 2 +- dist/idiomorph.min.js.gz | Bin 3350 -> 3353 bytes package.json | 13 +++- 13 files changed, 409 insertions(+), 47 deletions(-) create mode 100644 dist/idiomorph-ext.d.ts create mode 100644 dist/idiomorph.d.ts diff --git a/.gitignore b/.gitignore index fee84c5..0b65fb1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /tmp/* !tmp/.gitkeep .idea +/src/*.d.ts diff --git a/dist/idiomorph-ext.d.ts b/dist/idiomorph-ext.d.ts new file mode 100644 index 0000000..e3c979b --- /dev/null +++ b/dist/idiomorph-ext.d.ts @@ -0,0 +1,156 @@ +export type ConfigHead = { + style?: "merge" | "append" | "morph" | "none"; + block?: boolean; + ignore?: boolean; + shouldPreserve?: (arg0: Element) => boolean; + shouldReAppend?: (arg0: Element) => boolean; + shouldRemove?: (arg0: Element) => boolean; + afterHeadMorphed?: (arg0: Element, arg1: { + added: Node[]; + kept: Element[]; + removed: Element[]; + }) => void; +}; +export type ConfigCallbacks = { + beforeNodeAdded?: (arg0: Node) => boolean; + afterNodeAdded?: (arg0: Node) => void; + beforeNodeMorphed?: (arg0: Element, arg1: Node) => boolean; + afterNodeMorphed?: (arg0: Element, arg1: Node) => void; + beforeNodeRemoved?: (arg0: Element) => boolean; + afterNodeRemoved?: (arg0: Element) => void; + beforeAttributeUpdated?: (arg0: string, arg1: Element, arg2: "update" | "remove") => boolean; +}; +export type Config = { + morphStyle?: "outerHTML" | "innerHTML"; + ignoreActive?: boolean; + ignoreActiveValue?: boolean; + restoreFocus?: boolean; + callbacks?: ConfigCallbacks; + head?: ConfigHead; +}; +export type NoOp = () => void; +export type ConfigHeadInternal = { + style: "merge" | "append" | "morph" | "none"; + block?: boolean; + ignore?: boolean; + shouldPreserve: ((arg0: Element) => boolean) | NoOp; + shouldReAppend: ((arg0: Element) => boolean) | NoOp; + shouldRemove: ((arg0: Element) => boolean) | NoOp; + afterHeadMorphed: ((arg0: Element, arg1: { + added: Node[]; + kept: Element[]; + removed: Element[]; + }) => void) | NoOp; +}; +export type ConfigCallbacksInternal = { + beforeNodeAdded: ((arg0: Node) => boolean) | NoOp; + afterNodeAdded: ((arg0: Node) => void) | NoOp; + beforeNodeMorphed: ((arg0: Node, arg1: Node) => boolean) | NoOp; + afterNodeMorphed: ((arg0: Node, arg1: Node) => void) | NoOp; + beforeNodeRemoved: ((arg0: Node) => boolean) | NoOp; + afterNodeRemoved: ((arg0: Node) => void) | NoOp; + beforeAttributeUpdated: ((arg0: string, arg1: Element, arg2: "update" | "remove") => boolean) | NoOp; +}; +export type ConfigInternal = { + morphStyle: "outerHTML" | "innerHTML"; + ignoreActive?: boolean; + ignoreActiveValue?: boolean; + restoreFocus?: boolean; + callbacks: ConfigCallbacksInternal; + head: ConfigHeadInternal; +}; +export type IdSets = { + persistentIds: Set; + idMap: Map>; +}; +export type Morph = (oldNode: Element | Document, newContent: Element | Node | HTMLCollection | Node[] | string | null, config?: Config) => Promise | Node[]; +/** + * @typedef {object} ConfigHead + * + * @property {'merge' | 'append' | 'morph' | 'none'} [style] + * @property {boolean} [block] + * @property {boolean} [ignore] + * @property {function(Element): boolean} [shouldPreserve] + * @property {function(Element): boolean} [shouldReAppend] + * @property {function(Element): boolean} [shouldRemove] + * @property {function(Element, {added: Node[], kept: Element[], removed: Element[]}): void} [afterHeadMorphed] + */ +/** + * @typedef {object} ConfigCallbacks + * + * @property {function(Node): boolean} [beforeNodeAdded] + * @property {function(Node): void} [afterNodeAdded] + * @property {function(Element, Node): boolean} [beforeNodeMorphed] + * @property {function(Element, Node): void} [afterNodeMorphed] + * @property {function(Element): boolean} [beforeNodeRemoved] + * @property {function(Element): void} [afterNodeRemoved] + * @property {function(string, Element, "update" | "remove"): boolean} [beforeAttributeUpdated] + */ +/** + * @typedef {object} Config + * + * @property {'outerHTML' | 'innerHTML'} [morphStyle] + * @property {boolean} [ignoreActive] + * @property {boolean} [ignoreActiveValue] + * @property {boolean} [restoreFocus] + * @property {ConfigCallbacks} [callbacks] + * @property {ConfigHead} [head] + */ +/** + * @callback NoOp + * + * @returns {void} + */ +/** + * @typedef {object} ConfigHeadInternal + * + * @property {'merge' | 'append' | 'morph' | 'none'} style + * @property {boolean} [block] + * @property {boolean} [ignore] + * @property {(function(Element): boolean) | NoOp} shouldPreserve + * @property {(function(Element): boolean) | NoOp} shouldReAppend + * @property {(function(Element): boolean) | NoOp} shouldRemove + * @property {(function(Element, {added: Node[], kept: Element[], removed: Element[]}): void) | NoOp} afterHeadMorphed + */ +/** + * @typedef {object} ConfigCallbacksInternal + * + * @property {(function(Node): boolean) | NoOp} beforeNodeAdded + * @property {(function(Node): void) | NoOp} afterNodeAdded + * @property {(function(Node, Node): boolean) | NoOp} beforeNodeMorphed + * @property {(function(Node, Node): void) | NoOp} afterNodeMorphed + * @property {(function(Node): boolean) | NoOp} beforeNodeRemoved + * @property {(function(Node): void) | NoOp} afterNodeRemoved + * @property {(function(string, Element, "update" | "remove"): boolean) | NoOp} beforeAttributeUpdated + */ +/** + * @typedef {object} ConfigInternal + * + * @property {'outerHTML' | 'innerHTML'} morphStyle + * @property {boolean} [ignoreActive] + * @property {boolean} [ignoreActiveValue] + * @property {boolean} [restoreFocus] + * @property {ConfigCallbacksInternal} callbacks + * @property {ConfigHeadInternal} head + */ +/** + * @typedef {Object} IdSets + * @property {Set} persistentIds + * @property {Map>} idMap + */ +/** + * @callback Morph + * + * @param {Element | Document} oldNode + * @param {Element | Node | HTMLCollection | Node[] | string | null} newContent + * @param {Config} [config] + * @returns {Promise | Node[]} + */ +/** + * + * @type {{defaults: ConfigInternal, morph: Morph}} + */ +export var Idiomorph: { + defaults: ConfigInternal; + morph: Morph; +}; diff --git a/dist/idiomorph-ext.esm.js b/dist/idiomorph-ext.esm.js index 0fd86a5..c395099 100644 --- a/dist/idiomorph-ext.esm.js +++ b/dist/idiomorph-ext.esm.js @@ -36,7 +36,7 @@ import htmx from "htmx.org"; */ /** - * @typedef {function} NoOp + * @callback NoOp * * @returns {void} */ @@ -83,12 +83,12 @@ import htmx from "htmx.org"; */ /** - * @typedef {Function} Morph + * @callback Morph * * @param {Element | Document} oldNode * @param {Element | Node | HTMLCollection | Node[] | string | null} newContent * @param {Config} [config] - * @returns {undefined | Node[]} + * @returns {Promise | Node[]} */ // base IIFE to define idiomorph @@ -236,7 +236,13 @@ var Idiomorph = (function () { activeElement?.focus(); } if (activeElement && !activeElement.selectionEnd && selectionEnd) { - activeElement.setSelectionRange(selectionStart, selectionEnd); + try { + activeElement.setSelectionRange(selectionStart, selectionEnd); + } catch { + // the element may not support setSelectionRange: it's no longer an + // input/textarea after the morph, or it's an input type (number, + // email, date, ...) that doesn't support text selection + } } return results; @@ -678,12 +684,13 @@ var Idiomorph = (function () { const oldAttributes = oldElt.attributes; const newAttributes = newElt.attributes; for (const newAttribute of newAttributes) { - if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + if (oldElt.getAttribute(newAttribute.name) === newAttribute.value) { continue; } - if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) { - oldElt.setAttribute(newAttribute.name, newAttribute.value); + if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + continue; } + oldElt.setAttribute(newAttribute.name, newAttribute.value); } // iterate backwards to avoid skipping over items when a delete occurs for (let i = oldAttributes.length - 1; 0 <= i; i--) { diff --git a/dist/idiomorph-ext.js b/dist/idiomorph-ext.js index f2243a0..60782aa 100644 --- a/dist/idiomorph-ext.js +++ b/dist/idiomorph-ext.js @@ -34,7 +34,7 @@ */ /** - * @typedef {function} NoOp + * @callback NoOp * * @returns {void} */ @@ -81,12 +81,12 @@ */ /** - * @typedef {Function} Morph + * @callback Morph * * @param {Element | Document} oldNode * @param {Element | Node | HTMLCollection | Node[] | string | null} newContent * @param {Config} [config] - * @returns {undefined | Node[]} + * @returns {Promise | Node[]} */ // base IIFE to define idiomorph @@ -234,7 +234,13 @@ var Idiomorph = (function () { activeElement?.focus(); } if (activeElement && !activeElement.selectionEnd && selectionEnd) { - activeElement.setSelectionRange(selectionStart, selectionEnd); + try { + activeElement.setSelectionRange(selectionStart, selectionEnd); + } catch { + // the element may not support setSelectionRange: it's no longer an + // input/textarea after the morph, or it's an input type (number, + // email, date, ...) that doesn't support text selection + } } return results; @@ -676,12 +682,13 @@ var Idiomorph = (function () { const oldAttributes = oldElt.attributes; const newAttributes = newElt.attributes; for (const newAttribute of newAttributes) { - if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + if (oldElt.getAttribute(newAttribute.name) === newAttribute.value) { continue; } - if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) { - oldElt.setAttribute(newAttribute.name, newAttribute.value); + if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + continue; } + oldElt.setAttribute(newAttribute.name, newAttribute.value); } // iterate backwards to avoid skipping over items when a delete occurs for (let i = oldAttributes.length - 1; 0 <= i; i--) { diff --git a/dist/idiomorph-ext.min.js b/dist/idiomorph-ext.min.js index 1cb391d..758f128 100644 --- a/dist/idiomorph-ext.min.js +++ b/dist/idiomorph-ext.min.js @@ -1 +1 @@ -var Idiomorph=function(){"use strict";const e=()=>{};const n={morphStyle:"outerHTML",callbacks:{beforeNodeAdded:e,afterNodeAdded:e,beforeNodeMorphed:e,afterNodeMorphed:e,beforeNodeRemoved:e,afterNodeRemoved:e,beforeAttributeUpdated:e},head:{style:"merge",shouldPreserve:e=>e.getAttribute("im-preserve")==="true",shouldReAppend:e=>e.getAttribute("im-re-append")==="true",shouldRemove:e,afterHeadMorphed:e},restoreFocus:true};function t(t,e,n={}){t=d(t);const r=f(e);const i=c(t,r,n);const o=a(i,()=>{return u(i,t,r,e=>{if(e.morphStyle==="innerHTML"){s(e,t,r);return Array.from(t.childNodes)}else{return l(e,t,r)}})});i.pantry.remove();return o}function l(e,t,n){const r=f(t);s(e,r,n,t,t.nextSibling);return Array.from(r.childNodes)}function a(e,t){if(!e.config.restoreFocus)return t();let n=document.activeElement;if(!(n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement)){return t()}const{id:r,selectionStart:i,selectionEnd:o}=n;const l=t();if(r&&r!==document.activeElement?.getAttribute("id")){n=e.target.querySelector(`[id="${r}"]`);n?.focus()}if(n&&!n.selectionEnd&&o){n.setSelectionRange(i,o)}return l}const s=function(){function e(e,t,n,r=null,i=null){if(t instanceof HTMLTemplateElement&&n instanceof HTMLTemplateElement){t=t.content;n=n.content}r||=t.firstChild;for(const o of n.childNodes){if(r&&r!=i){const a=f(e,o,r,i);if(a){if(a!==r){h(e,r,a)}b(a,o,e);r=a.nextSibling;continue}}if(o instanceof Element){const s=o.getAttribute("id");if(e.persistentIds.has(s)){const u=p(t,s,r,e);b(u,o,e);r=u.nextSibling;continue}}const l=d(t,o,r,e);if(l){r=l.nextSibling}}while(r&&r!=i){const c=r;r=r.nextSibling;m(e,c)}}function d(e,t,n,r){if(r.callbacks.beforeNodeAdded(t)===false)return null;if(r.idMap.has(t)){const i=document.createElement(t.tagName);e.insertBefore(i,n);b(i,t,r);r.callbacks.afterNodeAdded(i);return i}else{const o=document.importNode(t,true);e.insertBefore(o,n);r.callbacks.afterNodeAdded(o);return o}}const f=function(){function e(e,t,n,r){let i=null;let o=t.nextSibling;let l=0;let a=n;while(a&&a!=r){if(u(a,t)){if(s(e,a,t)){return a}if(i===null){if(!e.idMap.has(a)){i=a}}}if(i===null&&o&&u(a,o)){l++;o=o.nextSibling;if(l>=2){i=undefined}}if(e.activeElementAndParents.includes(a))break;a=a.nextSibling}return i||null}function s(e,t,n){let r=e.idMap.get(t);let i=e.idMap.get(n);if(!i||!r)return false;for(const o of r){if(i.has(o)){return true}}return false}function u(e,t){const n=e;const r=t;return n.nodeType===r.nodeType&&n.tagName===r.tagName&&(!n.getAttribute?.("id")||n.getAttribute?.("id")===r.getAttribute?.("id"))}return e}();function m(e,t){if(e.idMap.has(t)){l(e.pantry,t,null)}else{if(e.callbacks.beforeNodeRemoved(t)===false)return;t.parentNode?.removeChild(t);e.callbacks.afterNodeRemoved(t)}}function h(t,e,n){let r=e;while(r&&r!==n){let e=r;r=r.nextSibling;m(t,e)}return r}function p(e,t,n,r){const i=r.target.getAttribute?.("id")===t&&r.target||r.target.querySelector(`[id="${t}"]`)||r.pantry.querySelector(`[id="${t}"]`);o(i,r);l(e,i,n);return i}function o(t,n){const r=t.getAttribute("id");while(t=t.parentNode){let e=n.idMap.get(t);if(e){e.delete(r);if(!e.size){n.idMap.delete(t)}}}}function l(t,n,r){if(t.moveBefore){try{t.moveBefore(n,r)}catch(e){t.insertBefore(n,r)}}else{t.insertBefore(n,r)}}return e}();const b=function(){function e(e,t,n){if(n.ignoreActive&&e===document.activeElement){return null}if(n.callbacks.beforeNodeMorphed(e,t)===false){return e}if(e instanceof HTMLHeadElement&&n.head.ignore){}else if(e instanceof HTMLHeadElement&&n.head.style!=="morph"){m(e,t,n)}else{r(e,t,n);if(!f(e,n)){s(n,e,t)}}n.callbacks.afterNodeMorphed(e,t);return e}function r(e,t,n){let r=t.nodeType;if(r===1){const i=e;const o=t;const l=i.attributes;const a=o.attributes;for(const s of a){if(d(s.name,i,"update",n)){continue}if(i.getAttribute(s.name)!==s.value){i.setAttribute(s.name,s.value)}}for(let e=l.length-1;0<=e;e--){const u=l[e];if(!u)continue;if(!o.hasAttribute(u.name)){if(d(u.name,i,"remove",n)){continue}i.removeAttribute(u.name)}}if(!f(i,n)){c(i,o,n)}}if(r===8||r===3){if(e.nodeValue!==t.nodeValue){e.nodeValue=t.nodeValue}}}function c(n,r,i){if(n instanceof HTMLInputElement&&r instanceof HTMLInputElement&&r.type!=="file"){let e=r.value;let t=n.value;o(n,r,"checked",i);o(n,r,"disabled",i);if(!r.hasAttribute("value")){if(!d("value",n,"remove",i)){n.value="";n.removeAttribute("value")}}else if(t!==e){if(!d("value",n,"update",i)){n.setAttribute("value",e);n.value=e}}}else if(n instanceof HTMLOptionElement&&r instanceof HTMLOptionElement){o(n,r,"selected",i)}else if(n instanceof HTMLTextAreaElement&&r instanceof HTMLTextAreaElement){let e=r.value;let t=n.value;if(d("value",n,"update",i)){return}if(e!==t){n.value=e}if(n.firstChild&&n.firstChild.nodeValue!==e){n.firstChild.nodeValue=e}}}function o(e,t,n,r){const i=t[n],o=e[n];if(i!==o){const l=d(n,e,"update",r);if(!l){e[n]=t[n]}if(i){if(!l){e.setAttribute(n,"")}}else{if(!d(n,e,"remove",r)){e.removeAttribute(n)}}}}function d(e,t,n,r){if(e==="value"&&r.ignoreActiveValue&&t===document.activeElement){return true}return r.callbacks.beforeAttributeUpdated(e,t,n)===false}function f(e,t){return!!t.ignoreActiveValue&&e===document.activeElement&&e!==document.body}return e}();function u(t,e,n,r){if(t.head.block){const i=e.querySelector("head");const o=n.querySelector("head");if(i&&o){const l=m(i,o,t);return Promise.all(l).then(()=>{const e=Object.assign(t,{head:{block:false,ignore:true}});return r(e)})}}return r(t)}function m(e,t,r){let i=[];let o=[];let l=[];let a=[];let s=new Map;for(const n of t.children){s.set(n.outerHTML,n)}for(const c of e.children){let e=s.has(c.outerHTML);let t=r.head.shouldReAppend(c);let n=r.head.shouldPreserve(c);if(e||n){if(t){o.push(c)}else{s.delete(c.outerHTML);l.push(c)}}else{if(r.head.style==="append"){if(t){o.push(c);a.push(c)}}else{if(r.head.shouldRemove(c)!==false){o.push(c)}}}}a.push(...s.values());let u=[];for(const d of a){let n=document.createRange().createContextualFragment(d.outerHTML).firstChild;if(r.callbacks.beforeNodeAdded(n)!==false){if("href"in n&&n.href||"src"in n&&n.src){let t;let e=new Promise(function(e){t=e});n.addEventListener("load",function(){t()});u.push(e)}e.appendChild(n);r.callbacks.afterNodeAdded(n);i.push(n)}}for(const f of o){if(r.callbacks.beforeNodeRemoved(f)!==false){e.removeChild(f);r.callbacks.afterNodeRemoved(f)}}r.head.afterHeadMorphed(e,{added:i,kept:l,removed:o});return u}const c=function(){function e(e,t,n){const{persistentIds:r,idMap:i}=f(e,t);const o=a(n);const l=o.morphStyle||"outerHTML";if(!["innerHTML","outerHTML"].includes(l)){throw`Do not understand how to morph style ${l}`}return{target:e,newContent:t,config:o,morphStyle:l,ignoreActive:o.ignoreActive,ignoreActiveValue:o.ignoreActiveValue,restoreFocus:o.restoreFocus,idMap:i,persistentIds:r,pantry:s(),activeElementAndParents:u(e),callbacks:o.callbacks,head:o.head}}function a(e){let t=Object.assign({},n);Object.assign(t,e);t.callbacks=Object.assign({},n.callbacks,e.callbacks);t.head=Object.assign({},n.head,e.head);return t}function s(){const e=document.createElement("div");e.hidden=true;document.body.insertAdjacentElement("afterend",e);return e}function u(e){let t=[];let n=document.activeElement;if(n?.tagName!=="BODY"&&e.contains(n)){while(n){t.push(n);if(n===e)break;n=n.parentElement}}return t}function c(e){let t=Array.from(e.querySelectorAll("[id]"));if(e.getAttribute?.("id")){t.push(e)}return t}function d(n,e,r,t){for(const i of t){const o=i.getAttribute("id");if(e.has(o)){let t=i;while(t){let e=n.get(t);if(e==null){e=new Set;n.set(t,e)}e.add(o);if(t===r)break;t=t.parentElement}}}}function f(e,t){const n=c(e);const r=c(t);const i=m(n,r);let o=new Map;d(o,i,e,n);const l=t.__idiomorphRoot||t;d(o,i,l,r);return{persistentIds:i,idMap:o}}function m(e,t){let n=new Set;let r=new Map;for(const{id:o,tagName:l}of e){if(r.has(o)){n.add(o)}else{r.set(o,l)}}let i=new Set;for(const{id:o,tagName:l}of t){if(i.has(o)){n.add(o)}else if(r.get(o)===l){i.add(o)}}for(const o of n){i.delete(o)}return i}return e}();const{normalizeElement:d,normalizeParent:f}=function(){const i=new WeakSet;function e(e){if(e instanceof Document){return e.documentElement}else{return e}}function r(e){if(e==null){return document.createElement("div")}else if(typeof e==="string"){return r(l(e))}else if(i.has(e)){return e}else if(e instanceof Node){if(e.parentNode){return new o(e)}else{const t=document.createElement("div");t.append(e);return t}}else{const t=document.createElement("div");for(const n of[...e]){t.append(n)}return t}}class o{constructor(e){this.originalNode=e;this.realParentNode=e.parentNode;this.previousSibling=e.previousSibling;this.nextSibling=e.nextSibling}get childNodes(){const e=[];let t=this.previousSibling?this.previousSibling.nextSibling:this.realParentNode.firstChild;while(t&&t!=this.nextSibling){e.push(t);t=t.nextSibling}return e}querySelectorAll(r){return this.childNodes.reduce((t,e)=>{if(e instanceof Element){if(e.matches(r))t.push(e);const n=e.querySelectorAll(r);for(let e=0;e]*>|>)([\s\S]*?)<\/svg>/gim,"");if(e.match(/<\/html>/)||e.match(/<\/head>/)||e.match(/<\/body>/)){let t=r.parseFromString(n,"text/html");if(e.match(/<\/html>/)){i.add(t);return t}else{let e=t.firstChild;if(e){i.add(e)}return e}}else{let e=r.parseFromString("","text/html");let t=e.body.querySelector("template").content;i.add(t);return t}}return{normalizeElement:e,normalizeParent:r}}();return{morph:t,defaults:n}}();(function(){function i(e){if(e==="morph"||e==="morph:outerHTML"){return{morphStyle:"outerHTML"}}else if(e==="morph:innerHTML"){return{morphStyle:"innerHTML"}}else if(e.startsWith("morph:")){return Function("return ("+e.slice(6)+")")()}}htmx.defineExtension("morph",{isInlineSwap:function(e){let t=i(e);return t?.morphStyle==="outerHTML"||t?.morphStyle==null},handleSwap:function(e,t,n){let r=i(e);if(r){return Idiomorph.morph(t,n.children,r)}}})})(); \ No newline at end of file +var Idiomorph=function(){"use strict";const e=()=>{};const n={morphStyle:"outerHTML",callbacks:{beforeNodeAdded:e,afterNodeAdded:e,beforeNodeMorphed:e,afterNodeMorphed:e,beforeNodeRemoved:e,afterNodeRemoved:e,beforeAttributeUpdated:e},head:{style:"merge",shouldPreserve:e=>e.getAttribute("im-preserve")==="true",shouldReAppend:e=>e.getAttribute("im-re-append")==="true",shouldRemove:e,afterHeadMorphed:e},restoreFocus:true};function t(t,e,n={}){t=d(t);const r=f(e);const i=c(t,r,n);const o=a(i,()=>{return u(i,t,r,e=>{if(e.morphStyle==="innerHTML"){s(e,t,r);return Array.from(t.childNodes)}else{return l(e,t,r)}})});i.pantry.remove();return o}function l(e,t,n){const r=f(t);s(e,r,n,t,t.nextSibling);return Array.from(r.childNodes)}function a(e,t){if(!e.config.restoreFocus)return t();let n=document.activeElement;if(!(n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement)){return t()}const{id:r,selectionStart:i,selectionEnd:o}=n;const l=t();if(r&&r!==document.activeElement?.getAttribute("id")){n=e.target.querySelector(`[id="${r}"]`);n?.focus()}if(n&&!n.selectionEnd&&o){try{n.setSelectionRange(i,o)}catch{}}return l}const s=function(){function e(e,t,n,r=null,i=null){if(t instanceof HTMLTemplateElement&&n instanceof HTMLTemplateElement){t=t.content;n=n.content}r||=t.firstChild;for(const o of n.childNodes){if(r&&r!=i){const a=f(e,o,r,i);if(a){if(a!==r){h(e,r,a)}b(a,o,e);r=a.nextSibling;continue}}if(o instanceof Element){const s=o.getAttribute("id");if(e.persistentIds.has(s)){const u=p(t,s,r,e);b(u,o,e);r=u.nextSibling;continue}}const l=d(t,o,r,e);if(l){r=l.nextSibling}}while(r&&r!=i){const c=r;r=r.nextSibling;m(e,c)}}function d(e,t,n,r){if(r.callbacks.beforeNodeAdded(t)===false)return null;if(r.idMap.has(t)){const i=document.createElement(t.tagName);e.insertBefore(i,n);b(i,t,r);r.callbacks.afterNodeAdded(i);return i}else{const o=document.importNode(t,true);e.insertBefore(o,n);r.callbacks.afterNodeAdded(o);return o}}const f=function(){function e(e,t,n,r){let i=null;let o=t.nextSibling;let l=0;let a=n;while(a&&a!=r){if(u(a,t)){if(s(e,a,t)){return a}if(i===null){if(!e.idMap.has(a)){i=a}}}if(i===null&&o&&u(a,o)){l++;o=o.nextSibling;if(l>=2){i=undefined}}if(e.activeElementAndParents.includes(a))break;a=a.nextSibling}return i||null}function s(e,t,n){let r=e.idMap.get(t);let i=e.idMap.get(n);if(!i||!r)return false;for(const o of r){if(i.has(o)){return true}}return false}function u(e,t){const n=e;const r=t;return n.nodeType===r.nodeType&&n.tagName===r.tagName&&(!n.getAttribute?.("id")||n.getAttribute?.("id")===r.getAttribute?.("id"))}return e}();function m(e,t){if(e.idMap.has(t)){l(e.pantry,t,null)}else{if(e.callbacks.beforeNodeRemoved(t)===false)return;t.parentNode?.removeChild(t);e.callbacks.afterNodeRemoved(t)}}function h(t,e,n){let r=e;while(r&&r!==n){let e=r;r=r.nextSibling;m(t,e)}return r}function p(e,t,n,r){const i=r.target.getAttribute?.("id")===t&&r.target||r.target.querySelector(`[id="${t}"]`)||r.pantry.querySelector(`[id="${t}"]`);o(i,r);l(e,i,n);return i}function o(t,n){const r=t.getAttribute("id");while(t=t.parentNode){let e=n.idMap.get(t);if(e){e.delete(r);if(!e.size){n.idMap.delete(t)}}}}function l(t,n,r){if(t.moveBefore){try{t.moveBefore(n,r)}catch(e){t.insertBefore(n,r)}}else{t.insertBefore(n,r)}}return e}();const b=function(){function e(e,t,n){if(n.ignoreActive&&e===document.activeElement){return null}if(n.callbacks.beforeNodeMorphed(e,t)===false){return e}if(e instanceof HTMLHeadElement&&n.head.ignore){}else if(e instanceof HTMLHeadElement&&n.head.style!=="morph"){m(e,t,n)}else{r(e,t,n);if(!f(e,n)){s(n,e,t)}}n.callbacks.afterNodeMorphed(e,t);return e}function r(e,t,n){let r=t.nodeType;if(r===1){const i=e;const o=t;const l=i.attributes;const a=o.attributes;for(const s of a){if(i.getAttribute(s.name)===s.value){continue}if(d(s.name,i,"update",n)){continue}i.setAttribute(s.name,s.value)}for(let e=l.length-1;0<=e;e--){const u=l[e];if(!u)continue;if(!o.hasAttribute(u.name)){if(d(u.name,i,"remove",n)){continue}i.removeAttribute(u.name)}}if(!f(i,n)){c(i,o,n)}}if(r===8||r===3){if(e.nodeValue!==t.nodeValue){e.nodeValue=t.nodeValue}}}function c(n,r,i){if(n instanceof HTMLInputElement&&r instanceof HTMLInputElement&&r.type!=="file"){let e=r.value;let t=n.value;o(n,r,"checked",i);o(n,r,"disabled",i);if(!r.hasAttribute("value")){if(!d("value",n,"remove",i)){n.value="";n.removeAttribute("value")}}else if(t!==e){if(!d("value",n,"update",i)){n.setAttribute("value",e);n.value=e}}}else if(n instanceof HTMLOptionElement&&r instanceof HTMLOptionElement){o(n,r,"selected",i)}else if(n instanceof HTMLTextAreaElement&&r instanceof HTMLTextAreaElement){let e=r.value;let t=n.value;if(d("value",n,"update",i)){return}if(e!==t){n.value=e}if(n.firstChild&&n.firstChild.nodeValue!==e){n.firstChild.nodeValue=e}}}function o(e,t,n,r){const i=t[n],o=e[n];if(i!==o){const l=d(n,e,"update",r);if(!l){e[n]=t[n]}if(i){if(!l){e.setAttribute(n,"")}}else{if(!d(n,e,"remove",r)){e.removeAttribute(n)}}}}function d(e,t,n,r){if(e==="value"&&r.ignoreActiveValue&&t===document.activeElement){return true}return r.callbacks.beforeAttributeUpdated(e,t,n)===false}function f(e,t){return!!t.ignoreActiveValue&&e===document.activeElement&&e!==document.body}return e}();function u(t,e,n,r){if(t.head.block){const i=e.querySelector("head");const o=n.querySelector("head");if(i&&o){const l=m(i,o,t);return Promise.all(l).then(()=>{const e=Object.assign(t,{head:{block:false,ignore:true}});return r(e)})}}return r(t)}function m(e,t,r){let i=[];let o=[];let l=[];let a=[];let s=new Map;for(const n of t.children){s.set(n.outerHTML,n)}for(const c of e.children){let e=s.has(c.outerHTML);let t=r.head.shouldReAppend(c);let n=r.head.shouldPreserve(c);if(e||n){if(t){o.push(c)}else{s.delete(c.outerHTML);l.push(c)}}else{if(r.head.style==="append"){if(t){o.push(c);a.push(c)}}else{if(r.head.shouldRemove(c)!==false){o.push(c)}}}}a.push(...s.values());let u=[];for(const d of a){let n=document.createRange().createContextualFragment(d.outerHTML).firstChild;if(r.callbacks.beforeNodeAdded(n)!==false){if("href"in n&&n.href||"src"in n&&n.src){let t;let e=new Promise(function(e){t=e});n.addEventListener("load",function(){t()});u.push(e)}e.appendChild(n);r.callbacks.afterNodeAdded(n);i.push(n)}}for(const f of o){if(r.callbacks.beforeNodeRemoved(f)!==false){e.removeChild(f);r.callbacks.afterNodeRemoved(f)}}r.head.afterHeadMorphed(e,{added:i,kept:l,removed:o});return u}const c=function(){function e(e,t,n){const{persistentIds:r,idMap:i}=f(e,t);const o=a(n);const l=o.morphStyle||"outerHTML";if(!["innerHTML","outerHTML"].includes(l)){throw`Do not understand how to morph style ${l}`}return{target:e,newContent:t,config:o,morphStyle:l,ignoreActive:o.ignoreActive,ignoreActiveValue:o.ignoreActiveValue,restoreFocus:o.restoreFocus,idMap:i,persistentIds:r,pantry:s(),activeElementAndParents:u(e),callbacks:o.callbacks,head:o.head}}function a(e){let t=Object.assign({},n);Object.assign(t,e);t.callbacks=Object.assign({},n.callbacks,e.callbacks);t.head=Object.assign({},n.head,e.head);return t}function s(){const e=document.createElement("div");e.hidden=true;document.body.insertAdjacentElement("afterend",e);return e}function u(e){let t=[];let n=document.activeElement;if(n?.tagName!=="BODY"&&e.contains(n)){while(n){t.push(n);if(n===e)break;n=n.parentElement}}return t}function c(e){let t=Array.from(e.querySelectorAll("[id]"));if(e.getAttribute?.("id")){t.push(e)}return t}function d(n,e,r,t){for(const i of t){const o=i.getAttribute("id");if(e.has(o)){let t=i;while(t){let e=n.get(t);if(e==null){e=new Set;n.set(t,e)}e.add(o);if(t===r)break;t=t.parentElement}}}}function f(e,t){const n=c(e);const r=c(t);const i=m(n,r);let o=new Map;d(o,i,e,n);const l=t.__idiomorphRoot||t;d(o,i,l,r);return{persistentIds:i,idMap:o}}function m(e,t){let n=new Set;let r=new Map;for(const{id:o,tagName:l}of e){if(r.has(o)){n.add(o)}else{r.set(o,l)}}let i=new Set;for(const{id:o,tagName:l}of t){if(i.has(o)){n.add(o)}else if(r.get(o)===l){i.add(o)}}for(const o of n){i.delete(o)}return i}return e}();const{normalizeElement:d,normalizeParent:f}=function(){const i=new WeakSet;function e(e){if(e instanceof Document){return e.documentElement}else{return e}}function r(e){if(e==null){return document.createElement("div")}else if(typeof e==="string"){return r(l(e))}else if(i.has(e)){return e}else if(e instanceof Node){if(e.parentNode){return new o(e)}else{const t=document.createElement("div");t.append(e);return t}}else{const t=document.createElement("div");for(const n of[...e]){t.append(n)}return t}}class o{constructor(e){this.originalNode=e;this.realParentNode=e.parentNode;this.previousSibling=e.previousSibling;this.nextSibling=e.nextSibling}get childNodes(){const e=[];let t=this.previousSibling?this.previousSibling.nextSibling:this.realParentNode.firstChild;while(t&&t!=this.nextSibling){e.push(t);t=t.nextSibling}return e}querySelectorAll(r){return this.childNodes.reduce((t,e)=>{if(e instanceof Element){if(e.matches(r))t.push(e);const n=e.querySelectorAll(r);for(let e=0;e]*>|>)([\s\S]*?)<\/svg>/gim,"");if(e.match(/<\/html>/)||e.match(/<\/head>/)||e.match(/<\/body>/)){let t=r.parseFromString(n,"text/html");if(e.match(/<\/html>/)){i.add(t);return t}else{let e=t.firstChild;if(e){i.add(e)}return e}}else{let e=r.parseFromString("","text/html");let t=e.body.querySelector("template").content;i.add(t);return t}}return{normalizeElement:e,normalizeParent:r}}();return{morph:t,defaults:n}}();(function(){function i(e){if(e==="morph"||e==="morph:outerHTML"){return{morphStyle:"outerHTML"}}else if(e==="morph:innerHTML"){return{morphStyle:"innerHTML"}}else if(e.startsWith("morph:")){return Function("return ("+e.slice(6)+")")()}}htmx.defineExtension("morph",{isInlineSwap:function(e){let t=i(e);return t?.morphStyle==="outerHTML"||t?.morphStyle==null},handleSwap:function(e,t,n){let r=i(e);if(r){return Idiomorph.morph(t,n.children,r)}}})})(); \ No newline at end of file diff --git a/dist/idiomorph.amd.js b/dist/idiomorph.amd.js index d421071..f4d6a39 100644 --- a/dist/idiomorph.amd.js +++ b/dist/idiomorph.amd.js @@ -36,7 +36,7 @@ define(() => { */ /** - * @typedef {function} NoOp + * @callback NoOp * * @returns {void} */ @@ -83,12 +83,12 @@ define(() => { */ /** - * @typedef {Function} Morph + * @callback Morph * * @param {Element | Document} oldNode * @param {Element | Node | HTMLCollection | Node[] | string | null} newContent * @param {Config} [config] - * @returns {undefined | Node[]} + * @returns {Promise | Node[]} */ // base IIFE to define idiomorph @@ -236,7 +236,13 @@ var Idiomorph = (function () { activeElement?.focus(); } if (activeElement && !activeElement.selectionEnd && selectionEnd) { - activeElement.setSelectionRange(selectionStart, selectionEnd); + try { + activeElement.setSelectionRange(selectionStart, selectionEnd); + } catch { + // the element may not support setSelectionRange: it's no longer an + // input/textarea after the morph, or it's an input type (number, + // email, date, ...) that doesn't support text selection + } } return results; @@ -678,12 +684,13 @@ var Idiomorph = (function () { const oldAttributes = oldElt.attributes; const newAttributes = newElt.attributes; for (const newAttribute of newAttributes) { - if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + if (oldElt.getAttribute(newAttribute.name) === newAttribute.value) { continue; } - if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) { - oldElt.setAttribute(newAttribute.name, newAttribute.value); + if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + continue; } + oldElt.setAttribute(newAttribute.name, newAttribute.value); } // iterate backwards to avoid skipping over items when a delete occurs for (let i = oldAttributes.length - 1; 0 <= i; i--) { diff --git a/dist/idiomorph.cjs.js b/dist/idiomorph.cjs.js index a716583..183fc39 100644 --- a/dist/idiomorph.cjs.js +++ b/dist/idiomorph.cjs.js @@ -34,7 +34,7 @@ */ /** - * @typedef {function} NoOp + * @callback NoOp * * @returns {void} */ @@ -81,12 +81,12 @@ */ /** - * @typedef {Function} Morph + * @callback Morph * * @param {Element | Document} oldNode * @param {Element | Node | HTMLCollection | Node[] | string | null} newContent * @param {Config} [config] - * @returns {undefined | Node[]} + * @returns {Promise | Node[]} */ // base IIFE to define idiomorph @@ -234,7 +234,13 @@ var Idiomorph = (function () { activeElement?.focus(); } if (activeElement && !activeElement.selectionEnd && selectionEnd) { - activeElement.setSelectionRange(selectionStart, selectionEnd); + try { + activeElement.setSelectionRange(selectionStart, selectionEnd); + } catch { + // the element may not support setSelectionRange: it's no longer an + // input/textarea after the morph, or it's an input type (number, + // email, date, ...) that doesn't support text selection + } } return results; @@ -676,12 +682,13 @@ var Idiomorph = (function () { const oldAttributes = oldElt.attributes; const newAttributes = newElt.attributes; for (const newAttribute of newAttributes) { - if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + if (oldElt.getAttribute(newAttribute.name) === newAttribute.value) { continue; } - if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) { - oldElt.setAttribute(newAttribute.name, newAttribute.value); + if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + continue; } + oldElt.setAttribute(newAttribute.name, newAttribute.value); } // iterate backwards to avoid skipping over items when a delete occurs for (let i = oldAttributes.length - 1; 0 <= i; i--) { diff --git a/dist/idiomorph.d.ts b/dist/idiomorph.d.ts new file mode 100644 index 0000000..e3c979b --- /dev/null +++ b/dist/idiomorph.d.ts @@ -0,0 +1,156 @@ +export type ConfigHead = { + style?: "merge" | "append" | "morph" | "none"; + block?: boolean; + ignore?: boolean; + shouldPreserve?: (arg0: Element) => boolean; + shouldReAppend?: (arg0: Element) => boolean; + shouldRemove?: (arg0: Element) => boolean; + afterHeadMorphed?: (arg0: Element, arg1: { + added: Node[]; + kept: Element[]; + removed: Element[]; + }) => void; +}; +export type ConfigCallbacks = { + beforeNodeAdded?: (arg0: Node) => boolean; + afterNodeAdded?: (arg0: Node) => void; + beforeNodeMorphed?: (arg0: Element, arg1: Node) => boolean; + afterNodeMorphed?: (arg0: Element, arg1: Node) => void; + beforeNodeRemoved?: (arg0: Element) => boolean; + afterNodeRemoved?: (arg0: Element) => void; + beforeAttributeUpdated?: (arg0: string, arg1: Element, arg2: "update" | "remove") => boolean; +}; +export type Config = { + morphStyle?: "outerHTML" | "innerHTML"; + ignoreActive?: boolean; + ignoreActiveValue?: boolean; + restoreFocus?: boolean; + callbacks?: ConfigCallbacks; + head?: ConfigHead; +}; +export type NoOp = () => void; +export type ConfigHeadInternal = { + style: "merge" | "append" | "morph" | "none"; + block?: boolean; + ignore?: boolean; + shouldPreserve: ((arg0: Element) => boolean) | NoOp; + shouldReAppend: ((arg0: Element) => boolean) | NoOp; + shouldRemove: ((arg0: Element) => boolean) | NoOp; + afterHeadMorphed: ((arg0: Element, arg1: { + added: Node[]; + kept: Element[]; + removed: Element[]; + }) => void) | NoOp; +}; +export type ConfigCallbacksInternal = { + beforeNodeAdded: ((arg0: Node) => boolean) | NoOp; + afterNodeAdded: ((arg0: Node) => void) | NoOp; + beforeNodeMorphed: ((arg0: Node, arg1: Node) => boolean) | NoOp; + afterNodeMorphed: ((arg0: Node, arg1: Node) => void) | NoOp; + beforeNodeRemoved: ((arg0: Node) => boolean) | NoOp; + afterNodeRemoved: ((arg0: Node) => void) | NoOp; + beforeAttributeUpdated: ((arg0: string, arg1: Element, arg2: "update" | "remove") => boolean) | NoOp; +}; +export type ConfigInternal = { + morphStyle: "outerHTML" | "innerHTML"; + ignoreActive?: boolean; + ignoreActiveValue?: boolean; + restoreFocus?: boolean; + callbacks: ConfigCallbacksInternal; + head: ConfigHeadInternal; +}; +export type IdSets = { + persistentIds: Set; + idMap: Map>; +}; +export type Morph = (oldNode: Element | Document, newContent: Element | Node | HTMLCollection | Node[] | string | null, config?: Config) => Promise | Node[]; +/** + * @typedef {object} ConfigHead + * + * @property {'merge' | 'append' | 'morph' | 'none'} [style] + * @property {boolean} [block] + * @property {boolean} [ignore] + * @property {function(Element): boolean} [shouldPreserve] + * @property {function(Element): boolean} [shouldReAppend] + * @property {function(Element): boolean} [shouldRemove] + * @property {function(Element, {added: Node[], kept: Element[], removed: Element[]}): void} [afterHeadMorphed] + */ +/** + * @typedef {object} ConfigCallbacks + * + * @property {function(Node): boolean} [beforeNodeAdded] + * @property {function(Node): void} [afterNodeAdded] + * @property {function(Element, Node): boolean} [beforeNodeMorphed] + * @property {function(Element, Node): void} [afterNodeMorphed] + * @property {function(Element): boolean} [beforeNodeRemoved] + * @property {function(Element): void} [afterNodeRemoved] + * @property {function(string, Element, "update" | "remove"): boolean} [beforeAttributeUpdated] + */ +/** + * @typedef {object} Config + * + * @property {'outerHTML' | 'innerHTML'} [morphStyle] + * @property {boolean} [ignoreActive] + * @property {boolean} [ignoreActiveValue] + * @property {boolean} [restoreFocus] + * @property {ConfigCallbacks} [callbacks] + * @property {ConfigHead} [head] + */ +/** + * @callback NoOp + * + * @returns {void} + */ +/** + * @typedef {object} ConfigHeadInternal + * + * @property {'merge' | 'append' | 'morph' | 'none'} style + * @property {boolean} [block] + * @property {boolean} [ignore] + * @property {(function(Element): boolean) | NoOp} shouldPreserve + * @property {(function(Element): boolean) | NoOp} shouldReAppend + * @property {(function(Element): boolean) | NoOp} shouldRemove + * @property {(function(Element, {added: Node[], kept: Element[], removed: Element[]}): void) | NoOp} afterHeadMorphed + */ +/** + * @typedef {object} ConfigCallbacksInternal + * + * @property {(function(Node): boolean) | NoOp} beforeNodeAdded + * @property {(function(Node): void) | NoOp} afterNodeAdded + * @property {(function(Node, Node): boolean) | NoOp} beforeNodeMorphed + * @property {(function(Node, Node): void) | NoOp} afterNodeMorphed + * @property {(function(Node): boolean) | NoOp} beforeNodeRemoved + * @property {(function(Node): void) | NoOp} afterNodeRemoved + * @property {(function(string, Element, "update" | "remove"): boolean) | NoOp} beforeAttributeUpdated + */ +/** + * @typedef {object} ConfigInternal + * + * @property {'outerHTML' | 'innerHTML'} morphStyle + * @property {boolean} [ignoreActive] + * @property {boolean} [ignoreActiveValue] + * @property {boolean} [restoreFocus] + * @property {ConfigCallbacksInternal} callbacks + * @property {ConfigHeadInternal} head + */ +/** + * @typedef {Object} IdSets + * @property {Set} persistentIds + * @property {Map>} idMap + */ +/** + * @callback Morph + * + * @param {Element | Document} oldNode + * @param {Element | Node | HTMLCollection | Node[] | string | null} newContent + * @param {Config} [config] + * @returns {Promise | Node[]} + */ +/** + * + * @type {{defaults: ConfigInternal, morph: Morph}} + */ +export var Idiomorph: { + defaults: ConfigInternal; + morph: Morph; +}; diff --git a/dist/idiomorph.esm.js b/dist/idiomorph.esm.js index a3d6306..7a87aac 100644 --- a/dist/idiomorph.esm.js +++ b/dist/idiomorph.esm.js @@ -34,7 +34,7 @@ */ /** - * @typedef {function} NoOp + * @callback NoOp * * @returns {void} */ @@ -81,12 +81,12 @@ */ /** - * @typedef {Function} Morph + * @callback Morph * * @param {Element | Document} oldNode * @param {Element | Node | HTMLCollection | Node[] | string | null} newContent * @param {Config} [config] - * @returns {undefined | Node[]} + * @returns {Promise | Node[]} */ // base IIFE to define idiomorph @@ -234,7 +234,13 @@ var Idiomorph = (function () { activeElement?.focus(); } if (activeElement && !activeElement.selectionEnd && selectionEnd) { - activeElement.setSelectionRange(selectionStart, selectionEnd); + try { + activeElement.setSelectionRange(selectionStart, selectionEnd); + } catch { + // the element may not support setSelectionRange: it's no longer an + // input/textarea after the morph, or it's an input type (number, + // email, date, ...) that doesn't support text selection + } } return results; @@ -676,12 +682,13 @@ var Idiomorph = (function () { const oldAttributes = oldElt.attributes; const newAttributes = newElt.attributes; for (const newAttribute of newAttributes) { - if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + if (oldElt.getAttribute(newAttribute.name) === newAttribute.value) { continue; } - if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) { - oldElt.setAttribute(newAttribute.name, newAttribute.value); + if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + continue; } + oldElt.setAttribute(newAttribute.name, newAttribute.value); } // iterate backwards to avoid skipping over items when a delete occurs for (let i = oldAttributes.length - 1; 0 <= i; i--) { diff --git a/dist/idiomorph.js b/dist/idiomorph.js index 2994d51..c314a09 100644 --- a/dist/idiomorph.js +++ b/dist/idiomorph.js @@ -34,7 +34,7 @@ */ /** - * @typedef {function} NoOp + * @callback NoOp * * @returns {void} */ @@ -81,12 +81,12 @@ */ /** - * @typedef {Function} Morph + * @callback Morph * * @param {Element | Document} oldNode * @param {Element | Node | HTMLCollection | Node[] | string | null} newContent * @param {Config} [config] - * @returns {undefined | Node[]} + * @returns {Promise | Node[]} */ // base IIFE to define idiomorph @@ -234,7 +234,13 @@ var Idiomorph = (function () { activeElement?.focus(); } if (activeElement && !activeElement.selectionEnd && selectionEnd) { - activeElement.setSelectionRange(selectionStart, selectionEnd); + try { + activeElement.setSelectionRange(selectionStart, selectionEnd); + } catch { + // the element may not support setSelectionRange: it's no longer an + // input/textarea after the morph, or it's an input type (number, + // email, date, ...) that doesn't support text selection + } } return results; @@ -676,12 +682,13 @@ var Idiomorph = (function () { const oldAttributes = oldElt.attributes; const newAttributes = newElt.attributes; for (const newAttribute of newAttributes) { - if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + if (oldElt.getAttribute(newAttribute.name) === newAttribute.value) { continue; } - if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) { - oldElt.setAttribute(newAttribute.name, newAttribute.value); + if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) { + continue; } + oldElt.setAttribute(newAttribute.name, newAttribute.value); } // iterate backwards to avoid skipping over items when a delete occurs for (let i = oldAttributes.length - 1; 0 <= i; i--) { diff --git a/dist/idiomorph.min.js b/dist/idiomorph.min.js index 3ec483f..734969c 100644 --- a/dist/idiomorph.min.js +++ b/dist/idiomorph.min.js @@ -1 +1 @@ -var Idiomorph=function(){"use strict";const e=()=>{};const n={morphStyle:"outerHTML",callbacks:{beforeNodeAdded:e,afterNodeAdded:e,beforeNodeMorphed:e,afterNodeMorphed:e,beforeNodeRemoved:e,afterNodeRemoved:e,beforeAttributeUpdated:e},head:{style:"merge",shouldPreserve:e=>e.getAttribute("im-preserve")==="true",shouldReAppend:e=>e.getAttribute("im-re-append")==="true",shouldRemove:e,afterHeadMorphed:e},restoreFocus:true};function t(t,e,n={}){t=d(t);const r=f(e);const i=u(t,r,n);const o=a(i,()=>{return c(i,t,r,e=>{if(e.morphStyle==="innerHTML"){s(e,t,r);return Array.from(t.childNodes)}else{return l(e,t,r)}})});i.pantry.remove();return o}function l(e,t,n){const r=f(t);s(e,r,n,t,t.nextSibling);return Array.from(r.childNodes)}function a(e,t){if(!e.config.restoreFocus)return t();let n=document.activeElement;if(!(n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement)){return t()}const{id:r,selectionStart:i,selectionEnd:o}=n;const l=t();if(r&&r!==document.activeElement?.getAttribute("id")){n=e.target.querySelector(`[id="${r}"]`);n?.focus()}if(n&&!n.selectionEnd&&o){n.setSelectionRange(i,o)}return l}const s=function(){function e(e,t,n,r=null,i=null){if(t instanceof HTMLTemplateElement&&n instanceof HTMLTemplateElement){t=t.content;n=n.content}r||=t.firstChild;for(const o of n.childNodes){if(r&&r!=i){const a=f(e,o,r,i);if(a){if(a!==r){h(e,r,a)}b(a,o,e);r=a.nextSibling;continue}}if(o instanceof Element){const s=o.getAttribute("id");if(e.persistentIds.has(s)){const c=p(t,s,r,e);b(c,o,e);r=c.nextSibling;continue}}const l=d(t,o,r,e);if(l){r=l.nextSibling}}while(r&&r!=i){const u=r;r=r.nextSibling;m(e,u)}}function d(e,t,n,r){if(r.callbacks.beforeNodeAdded(t)===false)return null;if(r.idMap.has(t)){const i=document.createElement(t.tagName);e.insertBefore(i,n);b(i,t,r);r.callbacks.afterNodeAdded(i);return i}else{const o=document.importNode(t,true);e.insertBefore(o,n);r.callbacks.afterNodeAdded(o);return o}}const f=function(){function e(e,t,n,r){let i=null;let o=t.nextSibling;let l=0;let a=n;while(a&&a!=r){if(c(a,t)){if(s(e,a,t)){return a}if(i===null){if(!e.idMap.has(a)){i=a}}}if(i===null&&o&&c(a,o)){l++;o=o.nextSibling;if(l>=2){i=undefined}}if(e.activeElementAndParents.includes(a))break;a=a.nextSibling}return i||null}function s(e,t,n){let r=e.idMap.get(t);let i=e.idMap.get(n);if(!i||!r)return false;for(const o of r){if(i.has(o)){return true}}return false}function c(e,t){const n=e;const r=t;return n.nodeType===r.nodeType&&n.tagName===r.tagName&&(!n.getAttribute?.("id")||n.getAttribute?.("id")===r.getAttribute?.("id"))}return e}();function m(e,t){if(e.idMap.has(t)){l(e.pantry,t,null)}else{if(e.callbacks.beforeNodeRemoved(t)===false)return;t.parentNode?.removeChild(t);e.callbacks.afterNodeRemoved(t)}}function h(t,e,n){let r=e;while(r&&r!==n){let e=r;r=r.nextSibling;m(t,e)}return r}function p(e,t,n,r){const i=r.target.getAttribute?.("id")===t&&r.target||r.target.querySelector(`[id="${t}"]`)||r.pantry.querySelector(`[id="${t}"]`);o(i,r);l(e,i,n);return i}function o(t,n){const r=t.getAttribute("id");while(t=t.parentNode){let e=n.idMap.get(t);if(e){e.delete(r);if(!e.size){n.idMap.delete(t)}}}}function l(t,n,r){if(t.moveBefore){try{t.moveBefore(n,r)}catch(e){t.insertBefore(n,r)}}else{t.insertBefore(n,r)}}return e}();const b=function(){function e(e,t,n){if(n.ignoreActive&&e===document.activeElement){return null}if(n.callbacks.beforeNodeMorphed(e,t)===false){return e}if(e instanceof HTMLHeadElement&&n.head.ignore){}else if(e instanceof HTMLHeadElement&&n.head.style!=="morph"){m(e,t,n)}else{r(e,t,n);if(!f(e,n)){s(n,e,t)}}n.callbacks.afterNodeMorphed(e,t);return e}function r(e,t,n){let r=t.nodeType;if(r===1){const i=e;const o=t;const l=i.attributes;const a=o.attributes;for(const s of a){if(d(s.name,i,"update",n)){continue}if(i.getAttribute(s.name)!==s.value){i.setAttribute(s.name,s.value)}}for(let e=l.length-1;0<=e;e--){const c=l[e];if(!c)continue;if(!o.hasAttribute(c.name)){if(d(c.name,i,"remove",n)){continue}i.removeAttribute(c.name)}}if(!f(i,n)){u(i,o,n)}}if(r===8||r===3){if(e.nodeValue!==t.nodeValue){e.nodeValue=t.nodeValue}}}function u(n,r,i){if(n instanceof HTMLInputElement&&r instanceof HTMLInputElement&&r.type!=="file"){let e=r.value;let t=n.value;o(n,r,"checked",i);o(n,r,"disabled",i);if(!r.hasAttribute("value")){if(!d("value",n,"remove",i)){n.value="";n.removeAttribute("value")}}else if(t!==e){if(!d("value",n,"update",i)){n.setAttribute("value",e);n.value=e}}}else if(n instanceof HTMLOptionElement&&r instanceof HTMLOptionElement){o(n,r,"selected",i)}else if(n instanceof HTMLTextAreaElement&&r instanceof HTMLTextAreaElement){let e=r.value;let t=n.value;if(d("value",n,"update",i)){return}if(e!==t){n.value=e}if(n.firstChild&&n.firstChild.nodeValue!==e){n.firstChild.nodeValue=e}}}function o(e,t,n,r){const i=t[n],o=e[n];if(i!==o){const l=d(n,e,"update",r);if(!l){e[n]=t[n]}if(i){if(!l){e.setAttribute(n,"")}}else{if(!d(n,e,"remove",r)){e.removeAttribute(n)}}}}function d(e,t,n,r){if(e==="value"&&r.ignoreActiveValue&&t===document.activeElement){return true}return r.callbacks.beforeAttributeUpdated(e,t,n)===false}function f(e,t){return!!t.ignoreActiveValue&&e===document.activeElement&&e!==document.body}return e}();function c(t,e,n,r){if(t.head.block){const i=e.querySelector("head");const o=n.querySelector("head");if(i&&o){const l=m(i,o,t);return Promise.all(l).then(()=>{const e=Object.assign(t,{head:{block:false,ignore:true}});return r(e)})}}return r(t)}function m(e,t,r){let i=[];let o=[];let l=[];let a=[];let s=new Map;for(const n of t.children){s.set(n.outerHTML,n)}for(const u of e.children){let e=s.has(u.outerHTML);let t=r.head.shouldReAppend(u);let n=r.head.shouldPreserve(u);if(e||n){if(t){o.push(u)}else{s.delete(u.outerHTML);l.push(u)}}else{if(r.head.style==="append"){if(t){o.push(u);a.push(u)}}else{if(r.head.shouldRemove(u)!==false){o.push(u)}}}}a.push(...s.values());let c=[];for(const d of a){let n=document.createRange().createContextualFragment(d.outerHTML).firstChild;if(r.callbacks.beforeNodeAdded(n)!==false){if("href"in n&&n.href||"src"in n&&n.src){let t;let e=new Promise(function(e){t=e});n.addEventListener("load",function(){t()});c.push(e)}e.appendChild(n);r.callbacks.afterNodeAdded(n);i.push(n)}}for(const f of o){if(r.callbacks.beforeNodeRemoved(f)!==false){e.removeChild(f);r.callbacks.afterNodeRemoved(f)}}r.head.afterHeadMorphed(e,{added:i,kept:l,removed:o});return c}const u=function(){function e(e,t,n){const{persistentIds:r,idMap:i}=f(e,t);const o=a(n);const l=o.morphStyle||"outerHTML";if(!["innerHTML","outerHTML"].includes(l)){throw`Do not understand how to morph style ${l}`}return{target:e,newContent:t,config:o,morphStyle:l,ignoreActive:o.ignoreActive,ignoreActiveValue:o.ignoreActiveValue,restoreFocus:o.restoreFocus,idMap:i,persistentIds:r,pantry:s(),activeElementAndParents:c(e),callbacks:o.callbacks,head:o.head}}function a(e){let t=Object.assign({},n);Object.assign(t,e);t.callbacks=Object.assign({},n.callbacks,e.callbacks);t.head=Object.assign({},n.head,e.head);return t}function s(){const e=document.createElement("div");e.hidden=true;document.body.insertAdjacentElement("afterend",e);return e}function c(e){let t=[];let n=document.activeElement;if(n?.tagName!=="BODY"&&e.contains(n)){while(n){t.push(n);if(n===e)break;n=n.parentElement}}return t}function u(e){let t=Array.from(e.querySelectorAll("[id]"));if(e.getAttribute?.("id")){t.push(e)}return t}function d(n,e,r,t){for(const i of t){const o=i.getAttribute("id");if(e.has(o)){let t=i;while(t){let e=n.get(t);if(e==null){e=new Set;n.set(t,e)}e.add(o);if(t===r)break;t=t.parentElement}}}}function f(e,t){const n=u(e);const r=u(t);const i=m(n,r);let o=new Map;d(o,i,e,n);const l=t.__idiomorphRoot||t;d(o,i,l,r);return{persistentIds:i,idMap:o}}function m(e,t){let n=new Set;let r=new Map;for(const{id:o,tagName:l}of e){if(r.has(o)){n.add(o)}else{r.set(o,l)}}let i=new Set;for(const{id:o,tagName:l}of t){if(i.has(o)){n.add(o)}else if(r.get(o)===l){i.add(o)}}for(const o of n){i.delete(o)}return i}return e}();const{normalizeElement:d,normalizeParent:f}=function(){const i=new WeakSet;function e(e){if(e instanceof Document){return e.documentElement}else{return e}}function r(e){if(e==null){return document.createElement("div")}else if(typeof e==="string"){return r(l(e))}else if(i.has(e)){return e}else if(e instanceof Node){if(e.parentNode){return new o(e)}else{const t=document.createElement("div");t.append(e);return t}}else{const t=document.createElement("div");for(const n of[...e]){t.append(n)}return t}}class o{constructor(e){this.originalNode=e;this.realParentNode=e.parentNode;this.previousSibling=e.previousSibling;this.nextSibling=e.nextSibling}get childNodes(){const e=[];let t=this.previousSibling?this.previousSibling.nextSibling:this.realParentNode.firstChild;while(t&&t!=this.nextSibling){e.push(t);t=t.nextSibling}return e}querySelectorAll(r){return this.childNodes.reduce((t,e)=>{if(e instanceof Element){if(e.matches(r))t.push(e);const n=e.querySelectorAll(r);for(let e=0;e]*>|>)([\s\S]*?)<\/svg>/gim,"");if(e.match(/<\/html>/)||e.match(/<\/head>/)||e.match(/<\/body>/)){let t=r.parseFromString(n,"text/html");if(e.match(/<\/html>/)){i.add(t);return t}else{let e=t.firstChild;if(e){i.add(e)}return e}}else{let e=r.parseFromString("","text/html");let t=e.body.querySelector("template").content;i.add(t);return t}}return{normalizeElement:e,normalizeParent:r}}();return{morph:t,defaults:n}}(); \ No newline at end of file +var Idiomorph=function(){"use strict";const e=()=>{};const n={morphStyle:"outerHTML",callbacks:{beforeNodeAdded:e,afterNodeAdded:e,beforeNodeMorphed:e,afterNodeMorphed:e,beforeNodeRemoved:e,afterNodeRemoved:e,beforeAttributeUpdated:e},head:{style:"merge",shouldPreserve:e=>e.getAttribute("im-preserve")==="true",shouldReAppend:e=>e.getAttribute("im-re-append")==="true",shouldRemove:e,afterHeadMorphed:e},restoreFocus:true};function t(t,e,n={}){t=d(t);const r=f(e);const i=u(t,r,n);const o=a(i,()=>{return c(i,t,r,e=>{if(e.morphStyle==="innerHTML"){s(e,t,r);return Array.from(t.childNodes)}else{return l(e,t,r)}})});i.pantry.remove();return o}function l(e,t,n){const r=f(t);s(e,r,n,t,t.nextSibling);return Array.from(r.childNodes)}function a(e,t){if(!e.config.restoreFocus)return t();let n=document.activeElement;if(!(n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement)){return t()}const{id:r,selectionStart:i,selectionEnd:o}=n;const l=t();if(r&&r!==document.activeElement?.getAttribute("id")){n=e.target.querySelector(`[id="${r}"]`);n?.focus()}if(n&&!n.selectionEnd&&o){try{n.setSelectionRange(i,o)}catch{}}return l}const s=function(){function e(e,t,n,r=null,i=null){if(t instanceof HTMLTemplateElement&&n instanceof HTMLTemplateElement){t=t.content;n=n.content}r||=t.firstChild;for(const o of n.childNodes){if(r&&r!=i){const a=f(e,o,r,i);if(a){if(a!==r){h(e,r,a)}b(a,o,e);r=a.nextSibling;continue}}if(o instanceof Element){const s=o.getAttribute("id");if(e.persistentIds.has(s)){const c=p(t,s,r,e);b(c,o,e);r=c.nextSibling;continue}}const l=d(t,o,r,e);if(l){r=l.nextSibling}}while(r&&r!=i){const u=r;r=r.nextSibling;m(e,u)}}function d(e,t,n,r){if(r.callbacks.beforeNodeAdded(t)===false)return null;if(r.idMap.has(t)){const i=document.createElement(t.tagName);e.insertBefore(i,n);b(i,t,r);r.callbacks.afterNodeAdded(i);return i}else{const o=document.importNode(t,true);e.insertBefore(o,n);r.callbacks.afterNodeAdded(o);return o}}const f=function(){function e(e,t,n,r){let i=null;let o=t.nextSibling;let l=0;let a=n;while(a&&a!=r){if(c(a,t)){if(s(e,a,t)){return a}if(i===null){if(!e.idMap.has(a)){i=a}}}if(i===null&&o&&c(a,o)){l++;o=o.nextSibling;if(l>=2){i=undefined}}if(e.activeElementAndParents.includes(a))break;a=a.nextSibling}return i||null}function s(e,t,n){let r=e.idMap.get(t);let i=e.idMap.get(n);if(!i||!r)return false;for(const o of r){if(i.has(o)){return true}}return false}function c(e,t){const n=e;const r=t;return n.nodeType===r.nodeType&&n.tagName===r.tagName&&(!n.getAttribute?.("id")||n.getAttribute?.("id")===r.getAttribute?.("id"))}return e}();function m(e,t){if(e.idMap.has(t)){l(e.pantry,t,null)}else{if(e.callbacks.beforeNodeRemoved(t)===false)return;t.parentNode?.removeChild(t);e.callbacks.afterNodeRemoved(t)}}function h(t,e,n){let r=e;while(r&&r!==n){let e=r;r=r.nextSibling;m(t,e)}return r}function p(e,t,n,r){const i=r.target.getAttribute?.("id")===t&&r.target||r.target.querySelector(`[id="${t}"]`)||r.pantry.querySelector(`[id="${t}"]`);o(i,r);l(e,i,n);return i}function o(t,n){const r=t.getAttribute("id");while(t=t.parentNode){let e=n.idMap.get(t);if(e){e.delete(r);if(!e.size){n.idMap.delete(t)}}}}function l(t,n,r){if(t.moveBefore){try{t.moveBefore(n,r)}catch(e){t.insertBefore(n,r)}}else{t.insertBefore(n,r)}}return e}();const b=function(){function e(e,t,n){if(n.ignoreActive&&e===document.activeElement){return null}if(n.callbacks.beforeNodeMorphed(e,t)===false){return e}if(e instanceof HTMLHeadElement&&n.head.ignore){}else if(e instanceof HTMLHeadElement&&n.head.style!=="morph"){m(e,t,n)}else{r(e,t,n);if(!f(e,n)){s(n,e,t)}}n.callbacks.afterNodeMorphed(e,t);return e}function r(e,t,n){let r=t.nodeType;if(r===1){const i=e;const o=t;const l=i.attributes;const a=o.attributes;for(const s of a){if(i.getAttribute(s.name)===s.value){continue}if(d(s.name,i,"update",n)){continue}i.setAttribute(s.name,s.value)}for(let e=l.length-1;0<=e;e--){const c=l[e];if(!c)continue;if(!o.hasAttribute(c.name)){if(d(c.name,i,"remove",n)){continue}i.removeAttribute(c.name)}}if(!f(i,n)){u(i,o,n)}}if(r===8||r===3){if(e.nodeValue!==t.nodeValue){e.nodeValue=t.nodeValue}}}function u(n,r,i){if(n instanceof HTMLInputElement&&r instanceof HTMLInputElement&&r.type!=="file"){let e=r.value;let t=n.value;o(n,r,"checked",i);o(n,r,"disabled",i);if(!r.hasAttribute("value")){if(!d("value",n,"remove",i)){n.value="";n.removeAttribute("value")}}else if(t!==e){if(!d("value",n,"update",i)){n.setAttribute("value",e);n.value=e}}}else if(n instanceof HTMLOptionElement&&r instanceof HTMLOptionElement){o(n,r,"selected",i)}else if(n instanceof HTMLTextAreaElement&&r instanceof HTMLTextAreaElement){let e=r.value;let t=n.value;if(d("value",n,"update",i)){return}if(e!==t){n.value=e}if(n.firstChild&&n.firstChild.nodeValue!==e){n.firstChild.nodeValue=e}}}function o(e,t,n,r){const i=t[n],o=e[n];if(i!==o){const l=d(n,e,"update",r);if(!l){e[n]=t[n]}if(i){if(!l){e.setAttribute(n,"")}}else{if(!d(n,e,"remove",r)){e.removeAttribute(n)}}}}function d(e,t,n,r){if(e==="value"&&r.ignoreActiveValue&&t===document.activeElement){return true}return r.callbacks.beforeAttributeUpdated(e,t,n)===false}function f(e,t){return!!t.ignoreActiveValue&&e===document.activeElement&&e!==document.body}return e}();function c(t,e,n,r){if(t.head.block){const i=e.querySelector("head");const o=n.querySelector("head");if(i&&o){const l=m(i,o,t);return Promise.all(l).then(()=>{const e=Object.assign(t,{head:{block:false,ignore:true}});return r(e)})}}return r(t)}function m(e,t,r){let i=[];let o=[];let l=[];let a=[];let s=new Map;for(const n of t.children){s.set(n.outerHTML,n)}for(const u of e.children){let e=s.has(u.outerHTML);let t=r.head.shouldReAppend(u);let n=r.head.shouldPreserve(u);if(e||n){if(t){o.push(u)}else{s.delete(u.outerHTML);l.push(u)}}else{if(r.head.style==="append"){if(t){o.push(u);a.push(u)}}else{if(r.head.shouldRemove(u)!==false){o.push(u)}}}}a.push(...s.values());let c=[];for(const d of a){let n=document.createRange().createContextualFragment(d.outerHTML).firstChild;if(r.callbacks.beforeNodeAdded(n)!==false){if("href"in n&&n.href||"src"in n&&n.src){let t;let e=new Promise(function(e){t=e});n.addEventListener("load",function(){t()});c.push(e)}e.appendChild(n);r.callbacks.afterNodeAdded(n);i.push(n)}}for(const f of o){if(r.callbacks.beforeNodeRemoved(f)!==false){e.removeChild(f);r.callbacks.afterNodeRemoved(f)}}r.head.afterHeadMorphed(e,{added:i,kept:l,removed:o});return c}const u=function(){function e(e,t,n){const{persistentIds:r,idMap:i}=f(e,t);const o=a(n);const l=o.morphStyle||"outerHTML";if(!["innerHTML","outerHTML"].includes(l)){throw`Do not understand how to morph style ${l}`}return{target:e,newContent:t,config:o,morphStyle:l,ignoreActive:o.ignoreActive,ignoreActiveValue:o.ignoreActiveValue,restoreFocus:o.restoreFocus,idMap:i,persistentIds:r,pantry:s(),activeElementAndParents:c(e),callbacks:o.callbacks,head:o.head}}function a(e){let t=Object.assign({},n);Object.assign(t,e);t.callbacks=Object.assign({},n.callbacks,e.callbacks);t.head=Object.assign({},n.head,e.head);return t}function s(){const e=document.createElement("div");e.hidden=true;document.body.insertAdjacentElement("afterend",e);return e}function c(e){let t=[];let n=document.activeElement;if(n?.tagName!=="BODY"&&e.contains(n)){while(n){t.push(n);if(n===e)break;n=n.parentElement}}return t}function u(e){let t=Array.from(e.querySelectorAll("[id]"));if(e.getAttribute?.("id")){t.push(e)}return t}function d(n,e,r,t){for(const i of t){const o=i.getAttribute("id");if(e.has(o)){let t=i;while(t){let e=n.get(t);if(e==null){e=new Set;n.set(t,e)}e.add(o);if(t===r)break;t=t.parentElement}}}}function f(e,t){const n=u(e);const r=u(t);const i=m(n,r);let o=new Map;d(o,i,e,n);const l=t.__idiomorphRoot||t;d(o,i,l,r);return{persistentIds:i,idMap:o}}function m(e,t){let n=new Set;let r=new Map;for(const{id:o,tagName:l}of e){if(r.has(o)){n.add(o)}else{r.set(o,l)}}let i=new Set;for(const{id:o,tagName:l}of t){if(i.has(o)){n.add(o)}else if(r.get(o)===l){i.add(o)}}for(const o of n){i.delete(o)}return i}return e}();const{normalizeElement:d,normalizeParent:f}=function(){const i=new WeakSet;function e(e){if(e instanceof Document){return e.documentElement}else{return e}}function r(e){if(e==null){return document.createElement("div")}else if(typeof e==="string"){return r(l(e))}else if(i.has(e)){return e}else if(e instanceof Node){if(e.parentNode){return new o(e)}else{const t=document.createElement("div");t.append(e);return t}}else{const t=document.createElement("div");for(const n of[...e]){t.append(n)}return t}}class o{constructor(e){this.originalNode=e;this.realParentNode=e.parentNode;this.previousSibling=e.previousSibling;this.nextSibling=e.nextSibling}get childNodes(){const e=[];let t=this.previousSibling?this.previousSibling.nextSibling:this.realParentNode.firstChild;while(t&&t!=this.nextSibling){e.push(t);t=t.nextSibling}return e}querySelectorAll(r){return this.childNodes.reduce((t,e)=>{if(e instanceof Element){if(e.matches(r))t.push(e);const n=e.querySelectorAll(r);for(let e=0;e]*>|>)([\s\S]*?)<\/svg>/gim,"");if(e.match(/<\/html>/)||e.match(/<\/head>/)||e.match(/<\/body>/)){let t=r.parseFromString(n,"text/html");if(e.match(/<\/html>/)){i.add(t);return t}else{let e=t.firstChild;if(e){i.add(e)}return e}}else{let e=r.parseFromString("","text/html");let t=e.body.querySelector("template").content;i.add(t);return t}}return{normalizeElement:e,normalizeParent:r}}();return{morph:t,defaults:n}}(); \ No newline at end of file diff --git a/dist/idiomorph.min.js.gz b/dist/idiomorph.min.js.gz index 581998f1868e378397d0c9f6641cad190f74039d..cf0a7dff871f202766476cd6087646d22b12186e 100644 GIT binary patch literal 3353 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 literal 3350 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 dist/idiomorph.cjs.js", "esm": "(cat src/idiomorph.js && echo \"\nexport {Idiomorph};\") > dist/idiomorph.esm.js && (echo \"import htmx from \\\"htmx.org\\\";\n\" && cat dist/idiomorph-ext.js && echo \"\nexport {Idiomorph};\") > dist/idiomorph-ext.esm.js", "gen-modules": "npm run-script amd && npm run-script cjs && npm run-script esm", - "dist": "cp -r src/* dist/ && cat src/idiomorph.js src/idiomorph-htmx.js > dist/idiomorph-ext.js && npm run-script gen-modules && npm run-script uglify && gzip -9 -k -f dist/idiomorph.min.js > dist/idiomorph.min.js.gz && exit", + "types": "tsc dist/idiomorph.esm.js dist/idiomorph-ext.esm.js --declaration --emitDeclarationOnly --allowJs --skipLibCheck --outDir dist && mv dist/idiomorph.esm.d.ts dist/idiomorph.d.ts && mv dist/idiomorph-ext.esm.d.ts dist/idiomorph-ext.d.ts", + "dist": "cp -r src/*.js dist/ && cat src/idiomorph.js src/idiomorph-htmx.js > dist/idiomorph-ext.js && npm run-script gen-modules && npm run-script types && npm run-script uglify && gzip -9 -k -f dist/idiomorph.min.js > dist/idiomorph.min.js.gz && exit", "uglify": "uglifyjs -m eval -o dist/idiomorph.min.js dist/idiomorph.js && uglifyjs -m eval -o dist/idiomorph-ext.min.js dist/idiomorph-ext.js", "format": "prettier --write .", "format:check": "prettier --check .", From e9995488d8b5af18ed35a0c8b29bf4bfa59a8de3 Mon Sep 17 00:00:00 2001 From: Alexander Brandon Coles Date: Thu, 18 Jun 2026 01:23:09 +0200 Subject: [PATCH 3/3] Ship CJS-flavored declarations for the require() path The single ESM-shaped .d.ts described a named 'Idiomorph' export, but the CJS bundle does 'module.exports = Idiomorph' (require() returns the object directly). CJS/node16 consumers got types that passed yet crashed at runtime. Generate a separate idiomorph.d.cts ('export = Idiomorph', with a namespace merge exposing Config et al) from the CJS bundle, and split the '.' export into per-condition types: idiomorph.d.ts for import, idiomorph.d.cts for require. Add dist/*.d.cts to files. Now a CJS named import correctly fails to typecheck (TS2595) instead of compiling to a runtime crash; 'import x = require("idiomorph")' works. --- dist/idiomorph.d.cts | 160 +++++++++++++++++++++++++++++++++++++++ dist/idiomorph.min.js.gz | Bin 3353 -> 3353 bytes package.json | 16 ++-- 3 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 dist/idiomorph.d.cts diff --git a/dist/idiomorph.d.cts b/dist/idiomorph.d.cts new file mode 100644 index 0000000..48da5f9 --- /dev/null +++ b/dist/idiomorph.d.cts @@ -0,0 +1,160 @@ +export = Idiomorph; +/** + * @typedef {object} ConfigHead + * + * @property {'merge' | 'append' | 'morph' | 'none'} [style] + * @property {boolean} [block] + * @property {boolean} [ignore] + * @property {function(Element): boolean} [shouldPreserve] + * @property {function(Element): boolean} [shouldReAppend] + * @property {function(Element): boolean} [shouldRemove] + * @property {function(Element, {added: Node[], kept: Element[], removed: Element[]}): void} [afterHeadMorphed] + */ +/** + * @typedef {object} ConfigCallbacks + * + * @property {function(Node): boolean} [beforeNodeAdded] + * @property {function(Node): void} [afterNodeAdded] + * @property {function(Element, Node): boolean} [beforeNodeMorphed] + * @property {function(Element, Node): void} [afterNodeMorphed] + * @property {function(Element): boolean} [beforeNodeRemoved] + * @property {function(Element): void} [afterNodeRemoved] + * @property {function(string, Element, "update" | "remove"): boolean} [beforeAttributeUpdated] + */ +/** + * @typedef {object} Config + * + * @property {'outerHTML' | 'innerHTML'} [morphStyle] + * @property {boolean} [ignoreActive] + * @property {boolean} [ignoreActiveValue] + * @property {boolean} [restoreFocus] + * @property {ConfigCallbacks} [callbacks] + * @property {ConfigHead} [head] + */ +/** + * @callback NoOp + * + * @returns {void} + */ +/** + * @typedef {object} ConfigHeadInternal + * + * @property {'merge' | 'append' | 'morph' | 'none'} style + * @property {boolean} [block] + * @property {boolean} [ignore] + * @property {(function(Element): boolean) | NoOp} shouldPreserve + * @property {(function(Element): boolean) | NoOp} shouldReAppend + * @property {(function(Element): boolean) | NoOp} shouldRemove + * @property {(function(Element, {added: Node[], kept: Element[], removed: Element[]}): void) | NoOp} afterHeadMorphed + */ +/** + * @typedef {object} ConfigCallbacksInternal + * + * @property {(function(Node): boolean) | NoOp} beforeNodeAdded + * @property {(function(Node): void) | NoOp} afterNodeAdded + * @property {(function(Node, Node): boolean) | NoOp} beforeNodeMorphed + * @property {(function(Node, Node): void) | NoOp} afterNodeMorphed + * @property {(function(Node): boolean) | NoOp} beforeNodeRemoved + * @property {(function(Node): void) | NoOp} afterNodeRemoved + * @property {(function(string, Element, "update" | "remove"): boolean) | NoOp} beforeAttributeUpdated + */ +/** + * @typedef {object} ConfigInternal + * + * @property {'outerHTML' | 'innerHTML'} morphStyle + * @property {boolean} [ignoreActive] + * @property {boolean} [ignoreActiveValue] + * @property {boolean} [restoreFocus] + * @property {ConfigCallbacksInternal} callbacks + * @property {ConfigHeadInternal} head + */ +/** + * @typedef {Object} IdSets + * @property {Set} persistentIds + * @property {Map>} idMap + */ +/** + * @callback Morph + * + * @param {Element | Document} oldNode + * @param {Element | Node | HTMLCollection | Node[] | string | null} newContent + * @param {Config} [config] + * @returns {Promise | Node[]} + */ +/** + * + * @type {{defaults: ConfigInternal, morph: Morph}} + */ +declare var Idiomorph: { + defaults: ConfigInternal; + morph: Morph; +}; +declare namespace Idiomorph { + export { ConfigHead, ConfigCallbacks, Config, NoOp, ConfigHeadInternal, ConfigCallbacksInternal, ConfigInternal, IdSets, Morph }; +} +type ConfigHead = { + style?: "merge" | "append" | "morph" | "none"; + block?: boolean; + ignore?: boolean; + shouldPreserve?: (arg0: Element) => boolean; + shouldReAppend?: (arg0: Element) => boolean; + shouldRemove?: (arg0: Element) => boolean; + afterHeadMorphed?: (arg0: Element, arg1: { + added: Node[]; + kept: Element[]; + removed: Element[]; + }) => void; +}; +type ConfigCallbacks = { + beforeNodeAdded?: (arg0: Node) => boolean; + afterNodeAdded?: (arg0: Node) => void; + beforeNodeMorphed?: (arg0: Element, arg1: Node) => boolean; + afterNodeMorphed?: (arg0: Element, arg1: Node) => void; + beforeNodeRemoved?: (arg0: Element) => boolean; + afterNodeRemoved?: (arg0: Element) => void; + beforeAttributeUpdated?: (arg0: string, arg1: Element, arg2: "update" | "remove") => boolean; +}; +type Config = { + morphStyle?: "outerHTML" | "innerHTML"; + ignoreActive?: boolean; + ignoreActiveValue?: boolean; + restoreFocus?: boolean; + callbacks?: ConfigCallbacks; + head?: ConfigHead; +}; +type NoOp = () => void; +type ConfigHeadInternal = { + style: "merge" | "append" | "morph" | "none"; + block?: boolean; + ignore?: boolean; + shouldPreserve: ((arg0: Element) => boolean) | NoOp; + shouldReAppend: ((arg0: Element) => boolean) | NoOp; + shouldRemove: ((arg0: Element) => boolean) | NoOp; + afterHeadMorphed: ((arg0: Element, arg1: { + added: Node[]; + kept: Element[]; + removed: Element[]; + }) => void) | NoOp; +}; +type ConfigCallbacksInternal = { + beforeNodeAdded: ((arg0: Node) => boolean) | NoOp; + afterNodeAdded: ((arg0: Node) => void) | NoOp; + beforeNodeMorphed: ((arg0: Node, arg1: Node) => boolean) | NoOp; + afterNodeMorphed: ((arg0: Node, arg1: Node) => void) | NoOp; + beforeNodeRemoved: ((arg0: Node) => boolean) | NoOp; + afterNodeRemoved: ((arg0: Node) => void) | NoOp; + beforeAttributeUpdated: ((arg0: string, arg1: Element, arg2: "update" | "remove") => boolean) | NoOp; +}; +type ConfigInternal = { + morphStyle: "outerHTML" | "innerHTML"; + ignoreActive?: boolean; + ignoreActiveValue?: boolean; + restoreFocus?: boolean; + callbacks: ConfigCallbacksInternal; + head: ConfigHeadInternal; +}; +type IdSets = { + persistentIds: Set; + idMap: Map>; +}; +type Morph = (oldNode: Element | Document, newContent: Element | Node | HTMLCollection | Node[] | string | null, config?: Config) => Promise | Node[]; diff --git a/dist/idiomorph.min.js.gz b/dist/idiomorph.min.js.gz index cf0a7dff871f202766476cd6087646d22b12186e..d0663cd2dda606cd1e0d696be854bc2a2cc67c7b 100644 GIT binary patch delta 15 WcmbO!HB*XBzMF%?S!W}gFfRZfxdUwg delta 15 WcmbO!HB*XBzMF%CS#2YmFfRZe8v`c* diff --git a/package.json b/package.json index a056124..e6357bd 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "LICENSE", "README.md", "dist/*.js", - "dist/*.d.ts" + "dist/*.d.ts", + "dist/*.d.cts" ], "main": "dist/idiomorph.js", "module": "dist/idiomorph.esm.js", @@ -22,9 +23,14 @@ "unpkg": "dist/idiomorph.min.js", "exports": { ".": { - "types": "./dist/idiomorph.d.ts", - "require": "./dist/idiomorph.cjs.js", - "import": "./dist/idiomorph.esm.js" + "import": { + "types": "./dist/idiomorph.d.ts", + "default": "./dist/idiomorph.esm.js" + }, + "require": { + "types": "./dist/idiomorph.d.cts", + "default": "./dist/idiomorph.cjs.js" + } }, "./htmx": { "types": "./dist/idiomorph-ext.d.ts", @@ -46,7 +52,7 @@ "cjs": "(cat src/idiomorph.js && echo \"\nmodule.exports = Idiomorph;\") > dist/idiomorph.cjs.js", "esm": "(cat src/idiomorph.js && echo \"\nexport {Idiomorph};\") > dist/idiomorph.esm.js && (echo \"import htmx from \\\"htmx.org\\\";\n\" && cat dist/idiomorph-ext.js && echo \"\nexport {Idiomorph};\") > dist/idiomorph-ext.esm.js", "gen-modules": "npm run-script amd && npm run-script cjs && npm run-script esm", - "types": "tsc dist/idiomorph.esm.js dist/idiomorph-ext.esm.js --declaration --emitDeclarationOnly --allowJs --skipLibCheck --outDir dist && mv dist/idiomorph.esm.d.ts dist/idiomorph.d.ts && mv dist/idiomorph-ext.esm.d.ts dist/idiomorph-ext.d.ts", + "types": "tsc dist/idiomorph.esm.js dist/idiomorph.cjs.js dist/idiomorph-ext.esm.js --declaration --emitDeclarationOnly --allowJs --skipLibCheck --outDir dist && mv dist/idiomorph.esm.d.ts dist/idiomorph.d.ts && mv dist/idiomorph.cjs.d.ts dist/idiomorph.d.cts && mv dist/idiomorph-ext.esm.d.ts dist/idiomorph-ext.d.ts", "dist": "cp -r src/*.js dist/ && cat src/idiomorph.js src/idiomorph-htmx.js > dist/idiomorph-ext.js && npm run-script gen-modules && npm run-script types && npm run-script uglify && gzip -9 -k -f dist/idiomorph.min.js > dist/idiomorph.min.js.gz && exit", "uglify": "uglifyjs -m eval -o dist/idiomorph.min.js dist/idiomorph.js && uglifyjs -m eval -o dist/idiomorph-ext.min.js dist/idiomorph-ext.js", "format": "prettier --write .",