@symbo.ls/brender 3.14.676 → 3.14.677

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @symbo.ls/brender
2
2
 
3
+ ## 3.14.677
4
+
5
+ ### Patch Changes
6
+
7
+ - 0d2a8e3: A server render keeps a CSS custom property's name as it is written. linkedom's style object turned every name given to `setProperty`, `removeProperty` and `getPropertyValue` into kebab case, so `--coverBoxLeft` went into the HTML as `--cover-box-left`. That is another property (custom property names are case-sensitive): the first paint used the `var()` fallback, and the content moved when the browser wrote `--coverBoxLeft` (XMA's hero text moved 30px on the desktop home, CLS 0.0003). A `--*` name now keeps its case in the server environment; every other name goes through linkedom as before.
8
+
3
9
  ## 3.14.676
4
10
 
5
11
  ### Patch Changes
package/dist/esm/env.js CHANGED
@@ -1 +1 @@
1
- import{parseHTML as h}from"linkedom";const a=new WeakSet;class u{static[Symbol.hasInstance](t){return a.has(t)}}const b=o=>{let t=Object.getPrototypeOf(o.createElement("select")),e;for(;t&&!(e=Object.getOwnPropertyDescriptor(t,"value"));)t=Object.getPrototypeOf(t);!t||!e||!e.get||e.set||Object.defineProperty(t,"value",{configurable:!0,enumerable:e.enumerable,get:e.get,set(l){const r=l==null?"":String(l);let s=!1;for(const n of this.querySelectorAll("option")){const c=n.hasAttribute("value")?n.getAttribute("value"):n.textContent;!s&&c===r?(n.setAttribute("selected",""),s=!0):n.removeAttribute("selected")}}})},i=()=>{const o={};return{getItem:t=>o[t]??null,setItem:(t,e)=>{o[t]=String(e)},removeItem:t=>{delete o[t]},clear:()=>{for(const t in o)delete o[t]},get length(){return Object.keys(o).length},key:t=>Object.keys(o)[t]??null}},m=(o="<!DOCTYPE html><html><head></head><body></body></html>")=>{const t={location:{pathname:"/",search:"",hash:"",origin:"http://localhost",protocol:"http:",host:"localhost",hostname:"localhost",port:"",href:"http://localhost/"},localStorage:i(),sessionStorage:i(),history:{pushState:()=>{},replaceState:()=>{},state:null}},{window:e,document:l}=h(o,t);return e.requestAnimationFrame||(e.requestAnimationFrame=r=>{const s=setTimeout(r,0);return s?.unref&&s.unref(),s}),e.cancelAnimationFrame||(e.cancelAnimationFrame=r=>clearTimeout(r)),e.URL||(e.URL=URL),e.scrollTo||(e.scrollTo=()=>{}),globalThis.localStorage=t.localStorage,globalThis.sessionStorage=t.sessionStorage,globalThis.history||(globalThis.history=t.history),b(l),e.MutationObserver||(e.MutationObserver=class{constructor(){}observe(){}disconnect(){}takeRecords(){return[]}}),globalThis.window=e,globalThis.document=l,globalThis.Node=e.Node||globalThis.Node,globalThis.Element=e.Element||globalThis.Element,globalThis.HTMLElement=e.HTMLElement||globalThis.HTMLElement,globalThis.MutationObserver=e.MutationObserver,a.add(e),e.constructor&&e.constructor!==Object?globalThis.Window=e.constructor:(typeof globalThis.Window!="function"||globalThis.Window===Object)&&(globalThis.Window=u),{window:e,document:l}};export{m as createEnv};
1
+ import{parseHTML as f}from"linkedom";const p=new WeakSet;class y{static[Symbol.hasInstance](e){return p.has(e)}}const m=o=>{let e=Object.getPrototypeOf(o.createElement("select")),t;for(;e&&!(t=Object.getOwnPropertyDescriptor(e,"value"));)e=Object.getPrototypeOf(e);!e||!t||!t.get||t.set||Object.defineProperty(e,"value",{configurable:!0,enumerable:t.enumerable,get:t.get,set(l){const s=l==null?"":String(l);let n=!1;for(const i of this.querySelectorAll("option")){const r=i.hasAttribute("value")?i.getAttribute("value"):i.textContent;!n&&r===s?(i.setAttribute("selected",""),n=!0):i.removeAttribute("selected")}}})},h=Symbol.for("@symbo.ls/brender:styleMap"),b=o=>typeof o=="string"&&o.charCodeAt(0)===45&&o.charCodeAt(1)===45,d=o=>{let e=Object.getPrototypeOf(o.createElement("div").style);for(;e&&!Object.prototype.hasOwnProperty.call(e,"setProperty");)e=Object.getPrototypeOf(e);if(!e||Object.prototype.hasOwnProperty.call(e,h))return;const{setProperty:t,removeProperty:l,getPropertyValue:s}=e;if(typeof t!="function"||typeof l!="function"||typeof s!="function")return;Object.defineProperty(e,h,{configurable:!0,get(){return this}});const n=(r,a)=>{let c="";for(;a.has(c);)c+=" ";l.call(r,c)},i=(r,a,c)=>{const u=r[h];n(r,u),c==null?u.delete(a):u.set(a,c),n(r,u)};Object.defineProperties(e,{setProperty:{configurable:!0,writable:!0,value:function(r,a){if(!b(r))return t.apply(this,arguments);i(this,r,a)}},removeProperty:{configurable:!0,writable:!0,value:function(r){if(!b(r))return l.apply(this,arguments);i(this,r,null)}},getPropertyValue:{configurable:!0,writable:!0,value:function(r){return b(r)?(s.call(this,r),this[h].get(r)):s.apply(this,arguments)}}})},g=()=>{const o={};return{getItem:e=>o[e]??null,setItem:(e,t)=>{o[e]=String(t)},removeItem:e=>{delete o[e]},clear:()=>{for(const e in o)delete o[e]},get length(){return Object.keys(o).length},key:e=>Object.keys(o)[e]??null}},T=(o="<!DOCTYPE html><html><head></head><body></body></html>")=>{const e={location:{pathname:"/",search:"",hash:"",origin:"http://localhost",protocol:"http:",host:"localhost",hostname:"localhost",port:"",href:"http://localhost/"},localStorage:g(),sessionStorage:g(),history:{pushState:()=>{},replaceState:()=>{},state:null}},{window:t,document:l}=f(o,e);return t.requestAnimationFrame||(t.requestAnimationFrame=s=>{const n=setTimeout(s,0);return n?.unref&&n.unref(),n}),t.cancelAnimationFrame||(t.cancelAnimationFrame=s=>clearTimeout(s)),t.URL||(t.URL=URL),t.scrollTo||(t.scrollTo=()=>{}),globalThis.localStorage=e.localStorage,globalThis.sessionStorage=e.sessionStorage,globalThis.history||(globalThis.history=e.history),m(l),d(l),t.MutationObserver||(t.MutationObserver=class{constructor(){}observe(){}disconnect(){}takeRecords(){return[]}}),globalThis.window=t,globalThis.document=l,globalThis.Node=t.Node||globalThis.Node,globalThis.Element=t.Element||globalThis.Element,globalThis.HTMLElement=t.HTMLElement||globalThis.HTMLElement,globalThis.MutationObserver=t.MutationObserver,p.add(t),t.constructor&&t.constructor!==Object?globalThis.Window=t.constructor:(typeof globalThis.Window!="function"||globalThis.Window===Object)&&(globalThis.Window=y),{window:t,document:l}};export{T as createEnv};
package/env.js CHANGED
@@ -33,6 +33,74 @@ const patchSelectValue = (document) => {
33
33
  })
34
34
  }
35
35
 
36
+ // Keep a custom property's name as it is in linkedom's CSSStyleDeclaration
37
+ // (see createEnv). The prototype is linkedom's, shared by every document it
38
+ // parses: patched once, like select.value above. The marker is a registered
39
+ // symbol, so two copies of this module in one process patch it once too.
40
+ const STYLE_MAP = Symbol.for('@symbo.ls/brender:styleMap')
41
+ const isCustomProperty = (name) =>
42
+ typeof name === 'string' && name.charCodeAt(0) === 45 && name.charCodeAt(1) === 45
43
+
44
+ const patchCustomPropertyNames = (document) => {
45
+ let proto = Object.getPrototypeOf(document.createElement('div').style)
46
+ while (proto && !Object.prototype.hasOwnProperty.call(proto, 'setProperty')) proto = Object.getPrototypeOf(proto)
47
+ if (!proto || Object.prototype.hasOwnProperty.call(proto, STYLE_MAP)) return
48
+ const { setProperty, removeProperty, getPropertyValue } = proto
49
+ if (typeof setProperty !== 'function' || typeof removeProperty !== 'function' || typeof getPropertyValue !== 'function') return
50
+ // linkedom's style object is a Proxy over a Map of the declarations, and it
51
+ // reads a name its prototype has from that Map: this getter gives the Map.
52
+ Object.defineProperty(proto, STYLE_MAP, { configurable: true, get () { return this } })
53
+ // A linkedom write that changes no declaration (it removes a name no
54
+ // declaration has). linkedom parses the attribute again when it changed,
55
+ // makes the style attribute when there is none, and writes the attribute
56
+ // from the Map. Before an edit of the Map it makes the Map current — a parse
57
+ // after the edit would lose it; after the edit it puts it in the attribute.
58
+ // The attribute text is never parsed again for the edit, so a value with a
59
+ // `;` in it (a data URI) stays whole, as with linkedom's own writes.
60
+ const settle = (style, map) => {
61
+ let none = ''
62
+ while (map.has(none)) none += ' '
63
+ removeProperty.call(style, none)
64
+ }
65
+ // linkedom's own write with the name as it is: null removes, any other
66
+ // value is set as it is given.
67
+ const write = (style, name, value) => {
68
+ const map = style[STYLE_MAP]
69
+ settle(style, map)
70
+ if (value == null) map.delete(name)
71
+ else map.set(name, value)
72
+ settle(style, map)
73
+ }
74
+ Object.defineProperties(proto, {
75
+ setProperty: {
76
+ configurable: true,
77
+ writable: true,
78
+ value: function (name, value) {
79
+ if (!isCustomProperty(name)) return setProperty.apply(this, arguments)
80
+ write(this, name, value)
81
+ }
82
+ },
83
+ removeProperty: {
84
+ configurable: true,
85
+ writable: true,
86
+ value: function (name) {
87
+ if (!isCustomProperty(name)) return removeProperty.apply(this, arguments)
88
+ write(this, name, null)
89
+ }
90
+ },
91
+ getPropertyValue: {
92
+ configurable: true,
93
+ writable: true,
94
+ value: function (name) {
95
+ if (!isCustomProperty(name)) return getPropertyValue.apply(this, arguments)
96
+ // linkedom's read parses the attribute again when it changed
97
+ getPropertyValue.call(this, name)
98
+ return this[STYLE_MAP].get(name)
99
+ }
100
+ }
101
+ })
102
+ }
103
+
36
104
  // Web Storage for one environment.
37
105
  const createStorage = () => {
38
106
  const store = {}
@@ -123,6 +191,17 @@ export const createEnv = (html = '<!DOCTYPE html><html><head></head><body></body
123
191
  // setter: the option with that value becomes the selected one.
124
192
  patchSelectValue(document)
125
193
 
194
+ // linkedom's CSSStyleDeclaration sends every name through `uhyphen`
195
+ // (setProperty, removeProperty, getPropertyValue): a custom property
196
+ // `--coverBoxLeft` went into the markup as `--cover-box-left`. That is
197
+ // another property — a custom property's name is case-sensitive — so the
198
+ // first paint read the `var()` fallback, and the browser's own write of
199
+ // `--coverBoxLeft` moved the content when the adoption came (XMA's hero
200
+ // text, 30px to the right on the desktop home: CLS 0.0003; r11 smoke check).
201
+ // A `--*` name is now kept as it is; every other name goes through linkedom
202
+ // as before.
203
+ patchCustomPropertyNames(document)
204
+
126
205
  // Stub MutationObserver for SSR (not provided by linkedom)
127
206
  if (!window.MutationObserver) {
128
207
  window.MutationObserver = class MutationObserver {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/brender",
3
- "version": "3.14.676",
3
+ "version": "3.14.677",
4
4
  "license": "CC-BY-NC-4.0",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.js",