From 6ffb1f93c2f830973059c07033adc66e24f86827 Mon Sep 17 00:00:00 2001 From: Manuel Puyol Date: Mon, 19 May 2025 14:19:40 -0700 Subject: [PATCH 1/5] Send error to event --- src/include-fragment-element.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/include-fragment-element.ts b/src/include-fragment-element.ts index c86b27f..4fb7af0 100644 --- a/src/include-fragment-element.ts +++ b/src/include-fragment-element.ts @@ -220,10 +220,10 @@ export class IncludeFragmentElement extends HTMLElement { } // Functional stand in for the W3 spec "queue a task" paradigm - async #task(eventsToDispatch: string[]): Promise { + async #task(eventsToDispatch: string[], error?: Error): Promise { await new Promise(resolve => setTimeout(resolve, 0)) for (const eventType of eventsToDispatch) { - this.dispatchEvent(new Event(eventType)) + this.dispatchEvent(error ? new CustomEvent(eventType, {detail: {error}}) : new Event(eventType)) } } @@ -258,7 +258,7 @@ export class IncludeFragmentElement extends HTMLElement { // Dispatch `error` and `loadend` async to allow // the `load()` promise to resolve _before_ these // events are fired. - this.#task(['error', 'loadend']) + this.#task(['error', 'loadend'], error) throw error } } From ad2af2d8c5da5bf8bc9464c57d086e1d77979a9d Mon Sep 17 00:00:00 2001 From: Manuel Puyol Date: Mon, 19 May 2025 14:23:35 -0700 Subject: [PATCH 2/5] enforce Error type and add test --- custom-elements.json | 31 ++++++++++++++++--------------- src/include-fragment-element.ts | 2 +- test/test.js | 1 + 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/custom-elements.json b/custom-elements.json index a6143cf..e101c93 100644 --- a/custom-elements.json +++ b/custom-elements.json @@ -9,11 +9,11 @@ { "kind": "variable", "name": "IncludeFragmentElement", - "default": "class extends HTMLElement {\n constructor() {\n super(...arguments);\n _IncludeFragmentElement_instances.add(this);\n _IncludeFragmentElement_busy.set(this, false);\n _IncludeFragmentElement_observer.set(this, new IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const { target } = entry;\n __classPrivateFieldGet(this, _IncludeFragmentElement_observer, \"f\").unobserve(target);\n if (!(target instanceof IncludeFragmentElement))\n return;\n if (target.loading === \"lazy\") {\n __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_handleData).call(this);\n }\n }\n }\n }, {\n rootMargin: \"0px 0px 256px 0px\",\n threshold: 0.01\n }));\n }\n static define(tag = \"include-fragment\", registry = customElements) {\n registry.define(tag, this);\n return this;\n }\n static setCSPTrustedTypesPolicy(policy) {\n cspTrustedTypesPolicyPromise = policy === null ? policy : Promise.resolve(policy);\n }\n static get observedAttributes() {\n return [\"src\", \"loading\"];\n }\n get src() {\n const src = this.getAttribute(\"src\");\n if (src) {\n const link = this.ownerDocument.createElement(\"a\");\n link.href = src;\n return link.href;\n } else {\n return \"\";\n }\n }\n set src(val) {\n this.setAttribute(\"src\", val);\n }\n get loading() {\n if (this.getAttribute(\"loading\") === \"lazy\")\n return \"lazy\";\n return \"eager\";\n }\n set loading(value) {\n this.setAttribute(\"loading\", value);\n }\n get accept() {\n return this.getAttribute(\"accept\") || \"\";\n }\n set accept(val) {\n this.setAttribute(\"accept\", val);\n }\n get data() {\n return __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_getStringOrErrorData).call(this);\n }\n attributeChangedCallback(attribute, oldVal) {\n if (attribute === \"src\") {\n if (this.isConnected && this.loading === \"eager\") {\n __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_handleData).call(this);\n }\n } else if (attribute === \"loading\") {\n if (this.isConnected && oldVal !== \"eager\" && this.loading === \"eager\") {\n __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_handleData).call(this);\n }\n }\n }\n connectedCallback() {\n if (!this.shadowRoot) {\n this.attachShadow({ mode: \"open\" });\n const style = document.createElement(\"style\");\n style.textContent = `:host {display: block;}`;\n this.shadowRoot.append(style, document.createElement(\"slot\"));\n }\n if (this.src && this.loading === \"eager\") {\n __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_handleData).call(this);\n }\n if (this.loading === \"lazy\") {\n __classPrivateFieldGet(this, _IncludeFragmentElement_observer, \"f\").observe(this);\n }\n }\n request() {\n const src = this.src;\n if (!src) {\n throw new Error(\"missing src\");\n }\n return new Request(src, {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: this.accept || \"text/html\"\n }\n });\n }\n load() {\n return __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_getStringOrErrorData).call(this);\n }\n fetch(request) {\n return fetch(request);\n }\n refetch() {\n privateData.delete(this);\n __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_handleData).call(this);\n }\n}" + "default": "class _IncludeFragmentElement extends HTMLElement {\n static" }, { "kind": "variable", - "name": "dist_default", + "name": "index_default", "default": "IncludeFragmentElement" } ], @@ -30,7 +30,7 @@ "kind": "js", "name": "default", "declaration": { - "name": "dist_default", + "name": "index_default", "module": "dist/bundle.js" } } @@ -188,12 +188,6 @@ } ] }, - { - "kind": "javascript-module", - "path": "test/test.js", - "declarations": [], - "exports": [] - }, { "kind": "javascript-module", "path": "src/include-fragment-element-define.ts", @@ -383,6 +377,13 @@ "type": { "text": "string[]" } + }, + { + "name": "error", + "optional": true, + "type": { + "text": "Error" + } } ] }, @@ -408,12 +409,6 @@ "type": { "text": "CustomEvent" } - }, - { - "name": "eventType", - "type": { - "text": "Event" - } } ], "attributes": [ @@ -471,6 +466,12 @@ } } ] + }, + { + "kind": "javascript-module", + "path": "test/test.js", + "declarations": [], + "exports": [] } ] } diff --git a/src/include-fragment-element.ts b/src/include-fragment-element.ts index 4fb7af0..c8e0280 100644 --- a/src/include-fragment-element.ts +++ b/src/include-fragment-element.ts @@ -258,7 +258,7 @@ export class IncludeFragmentElement extends HTMLElement { // Dispatch `error` and `loadend` async to allow // the `load()` promise to resolve _before_ these // events are fired. - this.#task(['error', 'loadend'], error) + this.#task(['error', 'loadend'], error as Error) throw error } } diff --git a/test/test.js b/test/test.js index 7fad9c0..53b9120 100644 --- a/test/test.js +++ b/test/test.js @@ -342,6 +342,7 @@ suite('include-fragment-element', function () { const event = await when(div.firstChild, 'error') assert.equal(event.bubbles, false) assert.equal(event.cancelable, false) + assert.equal(event.detail.error, 'Failed to load resource: the server responded with a status of 500') }) test('adds is-error class on 500 status', async function () { From b439bf613c07febf6a615bc504ea95626c673373 Mon Sep 17 00:00:00 2001 From: Manuel Puyol Date: Mon, 19 May 2025 14:28:15 -0700 Subject: [PATCH 3/5] typo --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index 53b9120..e6de533 100644 --- a/test/test.js +++ b/test/test.js @@ -342,7 +342,7 @@ suite('include-fragment-element', function () { const event = await when(div.firstChild, 'error') assert.equal(event.bubbles, false) assert.equal(event.cancelable, false) - assert.equal(event.detail.error, 'Failed to load resource: the server responded with a status of 500') + assert.equal(event.detail.error, 'Error: Failed to load resource: the server responded with a status of 500') }) test('adds is-error class on 500 status', async function () { From 4d5d1fe2fd220c105e5b0bfecdb7fc9578a62b63 Mon Sep 17 00:00:00 2001 From: Manuel Puyol Date: Mon, 19 May 2025 14:33:09 -0700 Subject: [PATCH 4/5] restore custom-elements --- custom-elements.json | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/custom-elements.json b/custom-elements.json index e101c93..a6143cf 100644 --- a/custom-elements.json +++ b/custom-elements.json @@ -9,11 +9,11 @@ { "kind": "variable", "name": "IncludeFragmentElement", - "default": "class _IncludeFragmentElement extends HTMLElement {\n static" + "default": "class extends HTMLElement {\n constructor() {\n super(...arguments);\n _IncludeFragmentElement_instances.add(this);\n _IncludeFragmentElement_busy.set(this, false);\n _IncludeFragmentElement_observer.set(this, new IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const { target } = entry;\n __classPrivateFieldGet(this, _IncludeFragmentElement_observer, \"f\").unobserve(target);\n if (!(target instanceof IncludeFragmentElement))\n return;\n if (target.loading === \"lazy\") {\n __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_handleData).call(this);\n }\n }\n }\n }, {\n rootMargin: \"0px 0px 256px 0px\",\n threshold: 0.01\n }));\n }\n static define(tag = \"include-fragment\", registry = customElements) {\n registry.define(tag, this);\n return this;\n }\n static setCSPTrustedTypesPolicy(policy) {\n cspTrustedTypesPolicyPromise = policy === null ? policy : Promise.resolve(policy);\n }\n static get observedAttributes() {\n return [\"src\", \"loading\"];\n }\n get src() {\n const src = this.getAttribute(\"src\");\n if (src) {\n const link = this.ownerDocument.createElement(\"a\");\n link.href = src;\n return link.href;\n } else {\n return \"\";\n }\n }\n set src(val) {\n this.setAttribute(\"src\", val);\n }\n get loading() {\n if (this.getAttribute(\"loading\") === \"lazy\")\n return \"lazy\";\n return \"eager\";\n }\n set loading(value) {\n this.setAttribute(\"loading\", value);\n }\n get accept() {\n return this.getAttribute(\"accept\") || \"\";\n }\n set accept(val) {\n this.setAttribute(\"accept\", val);\n }\n get data() {\n return __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_getStringOrErrorData).call(this);\n }\n attributeChangedCallback(attribute, oldVal) {\n if (attribute === \"src\") {\n if (this.isConnected && this.loading === \"eager\") {\n __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_handleData).call(this);\n }\n } else if (attribute === \"loading\") {\n if (this.isConnected && oldVal !== \"eager\" && this.loading === \"eager\") {\n __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_handleData).call(this);\n }\n }\n }\n connectedCallback() {\n if (!this.shadowRoot) {\n this.attachShadow({ mode: \"open\" });\n const style = document.createElement(\"style\");\n style.textContent = `:host {display: block;}`;\n this.shadowRoot.append(style, document.createElement(\"slot\"));\n }\n if (this.src && this.loading === \"eager\") {\n __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_handleData).call(this);\n }\n if (this.loading === \"lazy\") {\n __classPrivateFieldGet(this, _IncludeFragmentElement_observer, \"f\").observe(this);\n }\n }\n request() {\n const src = this.src;\n if (!src) {\n throw new Error(\"missing src\");\n }\n return new Request(src, {\n method: \"GET\",\n credentials: \"same-origin\",\n headers: {\n Accept: this.accept || \"text/html\"\n }\n });\n }\n load() {\n return __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_getStringOrErrorData).call(this);\n }\n fetch(request) {\n return fetch(request);\n }\n refetch() {\n privateData.delete(this);\n __classPrivateFieldGet(this, _IncludeFragmentElement_instances, \"m\", _IncludeFragmentElement_handleData).call(this);\n }\n}" }, { "kind": "variable", - "name": "index_default", + "name": "dist_default", "default": "IncludeFragmentElement" } ], @@ -30,7 +30,7 @@ "kind": "js", "name": "default", "declaration": { - "name": "index_default", + "name": "dist_default", "module": "dist/bundle.js" } } @@ -188,6 +188,12 @@ } ] }, + { + "kind": "javascript-module", + "path": "test/test.js", + "declarations": [], + "exports": [] + }, { "kind": "javascript-module", "path": "src/include-fragment-element-define.ts", @@ -377,13 +383,6 @@ "type": { "text": "string[]" } - }, - { - "name": "error", - "optional": true, - "type": { - "text": "Error" - } } ] }, @@ -409,6 +408,12 @@ "type": { "text": "CustomEvent" } + }, + { + "name": "eventType", + "type": { + "text": "Event" + } } ], "attributes": [ @@ -466,12 +471,6 @@ } } ] - }, - { - "kind": "javascript-module", - "path": "test/test.js", - "declarations": [], - "exports": [] } ] } From 45014a6576b1ae5d726846be26668b2f40cb6643 Mon Sep 17 00:00:00 2001 From: Manuel Puyol Date: Mon, 19 May 2025 14:33:44 -0700 Subject: [PATCH 5/5] Update test/test.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- test/test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index e6de533..19066e1 100644 --- a/test/test.js +++ b/test/test.js @@ -342,7 +342,8 @@ suite('include-fragment-element', function () { const event = await when(div.firstChild, 'error') assert.equal(event.bubbles, false) assert.equal(event.cancelable, false) - assert.equal(event.detail.error, 'Error: Failed to load resource: the server responded with a status of 500') + assert.instanceOf(event.detail.error, Error) + assert.equal(event.detail.error.message, 'Failed to load resource: the server responded with a status of 500') }) test('adds is-error class on 500 status', async function () {