diff --git a/CHANGELOG.md b/CHANGELOG.md index c146256a..4ce62f06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,10 @@ * Upgrade dependent packages to the latest version ([#380](https://github.com/marp-team/marp-core/pull/380)) * Switch package manager from yarn to npm ([#379](https://github.com/marp-team/marp-core/pull/379)) +### Fixed + +- Suppress uncaught `DOMException` error while upgrading `` Web Component elements in Firefox ([#370](https://github.com/marp-team/marp-core/issues/370), [#384](https://github.com/marp-team/marp-core/pull/384)) + ## v3.9.0 - 2023-10-15 ### Added diff --git a/jest.config.js b/jest.config.mjs similarity index 70% rename from jest.config.js rename to jest.config.mjs index 6c8e0711..ee8f9b5e 100644 --- a/jest.config.js +++ b/jest.config.mjs @@ -1,6 +1,9 @@ -const { defaults: tsjPreset } = require('ts-jest/presets') +import { createDefaultPreset } from 'ts-jest' -module.exports = { +const tsjPreset = createDefaultPreset() + +/** @type {import('jest').Config} */ +const config = { collectCoverageFrom: ['src/**/*.{j,t}s'], coveragePathIgnorePatterns: ['/node_modules/', '.*\\.d\\.ts'], coverageThreshold: { global: { lines: 95 } }, @@ -12,3 +15,5 @@ module.exports = { }, prettierPath: null, } + +export default config diff --git a/src/custom-elements/browser/marp-custom-element.ts b/src/custom-elements/browser/marp-custom-element.ts index 020ca74d..91521844 100644 --- a/src/custom-elements/browser/marp-custom-element.ts +++ b/src/custom-elements/browser/marp-custom-element.ts @@ -5,7 +5,7 @@ export const createMarpCustomElement = >( { attrs = {}, style }: { attrs?: Record; style?: string }, ) => class MarpCustomElement extends Base { - declare shadowRoot: ShadowRoot + declare readonly shadowRoot: ShadowRoot | null constructor(...args: any[]) { super(...args) @@ -14,7 +14,7 @@ export const createMarpCustomElement = >( if (!this.hasAttribute(key)) this.setAttribute(key, value) } - this.attachShadow({ mode: 'open' }) + this._shadow() } static get observedAttributes() { @@ -29,19 +29,34 @@ export const createMarpCustomElement = >( this._update() } + _shadow() { + if (!this.shadowRoot) { + try { + this.attachShadow({ mode: 'open' }) + } catch (e) { + if (!(e instanceof Error && e.name === 'NotSupportedError')) throw e + } + } + return this.shadowRoot + } + _update() { - const styleTag = style ? `` : '' - let slotTag = '' + const shadowRoot = this._shadow() - const { autoScaling } = this.dataset + if (shadowRoot) { + const styleTag = style ? `` : '' + let slotTag = '' - if (autoScaling !== undefined) { - const downscale = - autoScaling === 'downscale-only' ? 'data-downscale-only' : '' + const { autoScaling } = this.dataset - slotTag = `${slotTag}` - } + if (autoScaling !== undefined) { + const downscale = + autoScaling === 'downscale-only' ? 'data-downscale-only' : '' + + slotTag = `${slotTag}` + } - this.shadowRoot.innerHTML = styleTag + slotTag + shadowRoot.innerHTML = styleTag + slotTag + } } } diff --git a/test/custom-elements/browser.ts b/test/custom-elements/browser.ts index ebb35b92..0176c5a0 100644 --- a/test/custom-elements/browser.ts +++ b/test/custom-elements/browser.ts @@ -84,6 +84,34 @@ describe('The hydration script for custom elements', () => { ) }) + it('does not throw known DOMException error while upgrading
 to  (for Firefox)', () => {
+      document.body.innerHTML = '
1
' + + jest + .spyOn(HTMLElement.prototype, 'attachShadow') + .mockImplementationOnce(() => { + throw new DOMException( + 'Element.attachShadow: Unable to attach ShadowDOM', + 'NotSupportedError', + ) + }) + + expect(() => browser.applyCustomElements()).not.toThrow() + }) + + it.skip('throws error if unknown error occured while upgrading
 to ', () => {
+      document.body.innerHTML = '
1
' + + jest.spyOn(console, 'error').mockImplementation(() => {}) + jest + .spyOn(HTMLElement.prototype, 'attachShadow') + .mockImplementationOnce(() => { + throw new Error('Unknown error while attaching shadow') + }) + + expect(() => browser.applyCustomElements()).toThrow() + }) + it('does not replace

to ', () => { const html = '

test

' document.body.innerHTML = html