Last active
August 2, 2022 14:01
-
-
Save samthor/ee78b434b0f9aa525c5d235979b830aa to your computer and use it in GitHub Desktop.
Revisions
-
samthor revised this gist
Aug 3, 2019 . 1 changed file with 60 additions and 39 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,21 +1,18 @@ /** * @fileoverview Polyfill for adoptedStyleSheets and friends on ShadowRoot only. * * This ensures that <style> nodes are prepended to a ShadowRoot for corresponding CSSStyleSheet * instances set under adoptedStyleSheets. It doesn't hide these <style> nodes from introspection * in any way (i.e., patching DOM methods), but is just useful when .innerHTML is updated. */ const attachedTo = Symbol('attachedTo'); // on CSSStyleSheet, set of live nodes const styleNode = Symbol('styleNode'); // backing <style> for CSSStyleSheet const adoptedStyleSheets = Symbol('adopted'); // backing for array const styleSheetOwner = Symbol('sso'); // which CSSStyleSheet owns <style> const instantiatedAdoptedStyleSheets = Symbol('real'); // sheets created for this node const adoptedStyleSheetsObserver = Symbol('observer'); let hasConstructableStyleSheets = false; try { @@ -24,31 +21,45 @@ try { } catch (e) { const realCSSStyleSheet = window.CSSStyleSheet; // This isn't a constructor, just wraps to return a real CSSStyleSheet. window.CSSStyleSheet = function interceptCSSStyleSheet(css) { const style = document.createElement('style'); style.textContent = css; document.documentElement.appendChild(style); const sheet = style.sheet; sheet[attachedTo] = new Set(); sheet[styleNode] = style; style.remove(); return sheet; }; // Polyfill methods on real CSSStyleSheet. realCSSStyleSheet.prototype.replaceSync = function(css) { this[styleNode].textContent = css; this[attachedTo].forEach((node) => { node.textContent = css; // update all live instances }); }; // TODO: we'd need .replace() and friends too } if (!('adoptedStyleSheets' in ShadowRoot.prototype)) { function rectifyAdoptedStyleSheets() { const expected = this[adoptedStyleSheets]; // Check that nodes [0,n] point back to the expected CSSStyleSheet. // nb. this doesn't guard against users making local changes to textContent let ok = true; for (let i = 0; i < expected.length; ++i) { const check = this.childNodes[i]; if (!check) { ok = false; break; } if (check[styleSheetOwner] !== expected[i]) { ok = false; break; } @@ -57,23 +68,24 @@ if (!('adoptedStyleSheets' in ShadowRoot.prototype)) { return; } // Nuke all previous instantiated sheets. const previousAdopted = this[instantiatedAdoptedStyleSheets] || []; previousAdopted.forEach((node) => { const sheet = node[styleSheetOwner]; sheet[attachedTo].delete(node); node.remove(); }); // Prepare clones of target CSSStyleSheet instances. const toPrepend = expected.map((sheet) => { const node = sheet[styleNode].cloneNode(true); sheet[attachedTo].add(node); node[styleSheetOwner] = sheet; return node; }); // Save for next update, prepend to Node. this[instantiatedAdoptedStyleSheets] = toPrepend; this.prepend(...toPrepend); } @@ -82,13 +94,22 @@ if (!('adoptedStyleSheets' in ShadowRoot.prototype)) { return this[adoptedStyleSheets] || []; }, set(v) { this[adoptedStyleSheets] = Object.freeze((v || []).slice()); let observer = this[adoptedStyleSheetsObserver]; if (v && v.length) { // insert MutationObserver if required, to detect .innerHTML changes etc if (!observer) { observer = new MutationObserver(rectifyAdoptedStyleSheets.bind(this)); observer.observe(this, {childList: true}); this[adoptedStyleSheetsObserver] = observer; } } else { // clear MutationObserver if no longer required if (observer) { observer.disconnect(); this[adoptedStyleSheetsObserver] = null; } } rectifyAdoptedStyleSheets.call(this); -
samthor revised this gist
Aug 3, 2019 . 1 changed file with 98 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,98 @@ const attachedTo = Symbol('attachedTo'); // on CSSStyleSheet, set of live nodes const styleNode = Symbol('styleNode'); // backing <style> for CSSStyleSheet const styleSheet = Symbol('styleSheet'); // live node pointing to CSSStyleSheet const adoptedStyleSheets = Symbol('adoptedStyleSheets'); // backing for array function interceptCSSStyleSheet(css) { const style = document.createElement('style'); style.textContent = css; document.documentElement.appendChild(style); const sheet = style.sheet; sheet[attachedTo] = new Set(); sheet[styleNode] = style; style.remove(); return sheet; } let hasConstructableStyleSheets = false; try { new CSSStyleSheet(); hasConstructableStyleSheets = true; } catch (e) { const realCSSStyleSheet = window.CSSStyleSheet; // not really a ctor, just but returns a real CSSStyleSheet window.CSSStyleSheet = interceptCSSStyleSheet; // polyfill methods on real CSSStyleSheet realCSSStyleSheet.prototype.replaceSync = function(css) { this[styleNode].textContent = css; this[attachedTo].forEach((node) => { node.textContent = css; }); }; } if (!('adoptedStyleSheets' in ShadowRoot.prototype)) { function rectifyAdoptedStyleSheets() { const expected = this[adoptedStyleSheets]; // check that nodes [0,n] are our stylesheets let ok = true; for (let i = 0; i < expected.length; ++i) { const check = this.childNodes[i]; if (!check) { ok = false; break; } if (check[styleSheet] !== expected[i]) { ok = false; break; } } if (ok) { return; } // nuke all old stylesheets Array.from(this.childNodes).forEach((node) => { if (node[styleSheet]) { const sheet = node[styleSheet]; sheet[attachedTo].remove(this); node.remove(); // clear all old nodes since we recreate them } }); // prepare clones of targeted CSSStyleSheet nodes const toPrepend = expected.map((sheet) => { const clone = sheet[styleNode].cloneNode(true); clone[styleSheet] = sheet; sheet[attachedTo].add(clone); return clone; }); this.prepend(...toPrepend); } Object.defineProperty(ShadowRoot.prototype, 'adoptedStyleSheets', { get() { return this[adoptedStyleSheets] || []; }, set(v) { const prev = this[adoptedStyleSheets]; this[adoptedStyleSheets] = v; if (!prev) { // TODO: Clear MutationObserer if adoptedStyleSheets are removed const mo = new MutationObserver(rectifyAdoptedStyleSheets.bind(this)); mo.observe(this, {childList: true}); } rectifyAdoptedStyleSheets.call(this); } }); } -
samthor created this gist
Aug 2, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,21 @@ import fs from 'fs'; // as per https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/master/CSSModules/v1Explainer.md export default function cssModules() { return { name: 'css-modules', async load(id) { if (!id.endsWith('.css')) { return; } const raw = await fs.promises.readFile(id, 'utf-8'); const encoded = JSON.stringify(raw); return ` const sheet = new CSSStyleSheet(); sheet.replaceSync(${encoded}); export default sheet; `; } }; }