diff --git a/.changeset/slimy-moles-call.md b/.changeset/slimy-moles-call.md new file mode 100644 index 000000000..d546c516f --- /dev/null +++ b/.changeset/slimy-moles-call.md @@ -0,0 +1,5 @@ +--- +'@web/dev-server-core': patch +--- + +WebSocketsPlugin: polyfill `structuredClone` to support older browsers diff --git a/packages/dev-server-core/src/web-sockets/webSocketsPlugin.ts b/packages/dev-server-core/src/web-sockets/webSocketsPlugin.ts index ce760d4db..42d77a628 100644 --- a/packages/dev-server-core/src/web-sockets/webSocketsPlugin.ts +++ b/packages/dev-server-core/src/web-sockets/webSocketsPlugin.ts @@ -20,6 +20,14 @@ export function webSocketsPlugin(): Plugin { // this code is inlined because TS compiles to CJS but we need this to be ESM return ` + /** + * StructuredJSON copied as-is from https://github.com/ungap/structured-clone/blob/v1.2.0/structured-json.js + */ + var StructuredJSON=function(e){"use strict";const r="object"==typeof self?self:globalThis,t=e=>((e,t)=>{const s=(r,t)=>(e.set(t,r),r),n=c=>{if(e.has(c))return e.get(c);const[o,a]=t[c];switch(o){case 0:case-1:return s(a,c);case 1:{const e=s([],c);for(const r of a)e.push(n(r));return e}case 2:{const e=s({},c);for(const[r,t]of a)e[n(r)]=n(t);return e}case 3:return s(new Date(a),c);case 4:{const{source:e,flags:r}=a;return s(new RegExp(e,r),c)}case 5:{const e=s(new Map,c);for(const[r,t]of a)e.set(n(r),n(t));return e}case 6:{const e=s(new Set,c);for(const r of a)e.add(n(r));return e}case 7:{const{name:e,message:t}=a;return s(new r[e](t),c)}case 8:return s(BigInt(a),c);case"BigInt":return s(Object(BigInt(a)),c)}return s(new r[o](a),c)};return n})(new Map,e)(0),s="",{toString:n}={},{keys:c}=Object,o=e=>{const r=typeof e;if("object"!==r||!e)return[0,r];const t=n.call(e).slice(8,-1);switch(t){case"Array":return[1,s];case"Object":return[2,s];case"Date":return[3,s];case"RegExp":return[4,s];case"Map":return[5,s];case"Set":return[6,s]}return t.includes("Array")?[1,t]:t.includes("Error")?[7,t]:[2,t]},a=([e,r])=>0===e&&("function"===r||"symbol"===r),u=(e,{json:r,lossy:t}={})=>{const s=[];return((e,r,t,s)=>{const n=(e,r)=>{const n=s.push(e)-1;return t.set(r,n),n},u=s=>{if(t.has(s))return t.get(s);let[i,f]=o(s);switch(i){case 0:{let r=s;switch(f){case"bigint":i=8,r=s.toString();break;case"function":case"symbol":if(e)throw new TypeError("unable to serialize "+f);r=null;break;case"undefined":return n([-1],s)}return n([i,r],s)}case 1:{if(f)return n([f,[...s]],s);const e=[],r=n([i,e],s);for(const r of s)e.push(u(r));return r}case 2:{if(f)switch(f){case"BigInt":return n([f,s.toString()],s);case"Boolean":case"Number":case"String":return n([f,s.valueOf()],s)}if(r&&"toJSON"in s)return u(s.toJSON());const t=[],l=n([i,t],s);for(const r of c(s))!e&&a(o(s[r]))||t.push([u(r),u(s[r])]);return l}case 3:return n([i,s.toISOString()],s);case 4:{const{source:e,flags:r}=s;return n([i,{source:e,flags:r}],s)}case 5:{const r=[],t=n([i,r],s);for(const[t,n]of s)(e||!a(o(t))&&!a(o(n)))&&r.push([u(t),u(n)]);return t}case 6:{const r=[],t=n([i,r],s);for(const t of s)!e&&a(o(t))||r.push(u(t));return t}}const{message:l}=s;return n([i,{name:f,message:l}],s)};return u})(!(r||t),!!r,new Map,s)(e),s},{parse:i,stringify:f}=JSON,l={json:!0,lossy:!0};return e.parse=e=>t(i(e)),e.stringify=e=>f(u(e,l)),e}({}); + + // Not the exact same behaviour as structuredClone, but good enough for the basic cases. + const structuredClonePolyfill = (obj) => StructuredJSON.parse(StructuredJSON.stringify(obj)); + /** * Code at this indent adapted from fast-safe-stringify by David Mark Clements * @license MIT @@ -40,7 +48,7 @@ export function webSocketsPlugin(): Plugin { } export function stable (obj, replacer, spacer) { - var target = structuredClone(obj) + var target = ('structuredClone' in globalThis) ? structuredClone(obj) : structuredClonePolyfill(obj) var tmp = deterministicDecirc(target, '', [], undefined) || target var res if (replacerStack.length === 0) {