@garfish/loader
Advanced tools
Comparing version
@@ -38,3 +38,3 @@ 'use strict'; | ||
} | ||
// 数组变为对象 `['a'] => { a: true }` | ||
// Array to Object `['a'] => { a: true }` | ||
function makeMap(list) { | ||
@@ -92,27 +92,26 @@ const map = Object.create(null); | ||
} | ||
// 深度合并两个对象,能处理循环引用,后面的覆盖前面的,可选数组去重 | ||
// Deeply merge two objects, can handle circular references, the latter overwrite the previous | ||
function deepMerge(o, n, dp) { | ||
const lRecord = new WeakMap(); | ||
const rRecord = new WeakMap(); | ||
const vRecord = new WeakMap(); | ||
const leftRecord = new WeakMap(); | ||
const rightRecord = new WeakMap(); | ||
const valueRecord = new WeakMap(); | ||
const isArray = Array.isArray; | ||
const isAllRefs = (a, b) => { | ||
// 判断 merge 左右两边,不需要用到 vRecord | ||
if (lRecord.has(a) || rRecord.has(a)) { | ||
return lRecord.has(b) || rRecord.has(b); | ||
if (leftRecord.has(a) || rightRecord.has(a)) { | ||
return leftRecord.has(b) || rightRecord.has(b); | ||
} | ||
}; | ||
const clone = (v) => { | ||
// 深拷贝 | ||
// Deep clone | ||
if (isPrimitive(v) || typeof v === 'function') { | ||
return v; | ||
} | ||
else if (vRecord.has(v)) { | ||
return vRecord.get(v); | ||
else if (valueRecord.has(v)) { | ||
return valueRecord.get(v); | ||
} | ||
else if (lRecord.has(v)) { | ||
return lRecord.get(v); | ||
else if (leftRecord.has(v)) { | ||
return leftRecord.get(v); | ||
} | ||
else if (rRecord.has(v)) { | ||
return rRecord.get(v); | ||
else if (rightRecord.has(v)) { | ||
return rightRecord.get(v); | ||
} | ||
@@ -122,15 +121,15 @@ else if (isArray(v)) { | ||
v = unique(v); | ||
const res = []; | ||
vRecord.set(v, res); | ||
const arr = []; | ||
valueRecord.set(v, arr); | ||
for (let i = 0, len = v.length; i < len; i++) { | ||
res[i] = clone(v[i]); | ||
arr[i] = clone(v[i]); | ||
} | ||
return res; | ||
return arr; | ||
} | ||
else if (typeof v === 'object') { | ||
const res = {}; | ||
vRecord.set(v, res); | ||
const obj = {}; | ||
valueRecord.set(v, obj); | ||
const keys = Reflect.ownKeys(v); | ||
keys.forEach((key) => (res[key] = clone(v[key]))); | ||
return res; | ||
keys.forEach((key) => (obj[key] = clone(v[key]))); | ||
return obj; | ||
} | ||
@@ -152,7 +151,7 @@ }; | ||
const res = {}; | ||
const lkeys = Reflect.ownKeys(l); | ||
const rkeys = Reflect.ownKeys(r); | ||
lRecord.set(l, res); | ||
rRecord.set(r, res); | ||
lkeys.forEach((key) => { | ||
const leftKeys = Reflect.ownKeys(l); | ||
const rightKeys = Reflect.ownKeys(r); | ||
leftRecord.set(l, res); | ||
rightRecord.set(r, res); | ||
leftKeys.forEach((key) => { | ||
const lv = l[key]; | ||
@@ -167,17 +166,17 @@ const rv = r[key]; | ||
res[key] = isAllRefs(lv, rv) | ||
? lRecord.get(lv) // 左边右边同一个值,取哪个都行 | ||
? leftRecord.get(lv) // The same value on the left and right, whichever is OK | ||
: mergeObject(lv, rv); | ||
} | ||
else { | ||
res[key] = setValue(rRecord, rv); | ||
res[key] = setValue(rightRecord, rv); | ||
} | ||
} | ||
else { | ||
res[key] = setValue(lRecord, lv); | ||
res[key] = setValue(leftRecord, lv); | ||
} | ||
}); | ||
rkeys.forEach((key) => { | ||
rightKeys.forEach((key) => { | ||
if (hasOwn(res, key)) | ||
return; | ||
res[key] = setValue(rRecord, r[key]); | ||
res[key] = setValue(rightRecord, r[key]); | ||
}); | ||
@@ -188,14 +187,2 @@ return res; | ||
} | ||
// copy from https://github.com/getsentry/sentry-javascript/blob/6.4.0/packages/browser/src/tracekit.ts | ||
const sourceListTags = [ | ||
'link', | ||
'img', | ||
'script', | ||
'video', | ||
'audio', | ||
'style', | ||
]; | ||
makeMap(sourceListTags); | ||
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1 | ||
@@ -218,2 +205,149 @@ // Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3 | ||
// copy from https://github.com/getsentry/sentry-javascript/blob/6.4.0/packages/browser/src/tracekit.ts | ||
const sourceListTags = [ | ||
'link', | ||
'img', | ||
'script', | ||
'video', | ||
'audio', | ||
'style', | ||
]; | ||
makeMap(sourceListTags); | ||
const xChar = 120; // "x" char | ||
const colonChar = 58; // ":" char | ||
const ns = 'http://www.w3.org/2000/svg'; | ||
const xlinkNS = 'http://www.w3.org/1999/xlink'; // xmlns:xlink | ||
const xmlNS = 'http://www.w3.org/XML/1998/namespace'; // xmlns | ||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element | ||
const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + | ||
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + | ||
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + | ||
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + | ||
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + | ||
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + | ||
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + | ||
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + | ||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + | ||
'text,textPath,title,tspan,unknown,use,view'; | ||
const isSVG = makeMap(SVG_TAGS.split(',')); | ||
function attributesString(attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return ''; | ||
return attributes.reduce((total, { key, value }) => { | ||
return total + (value ? `${key}="${value}" ` : key); | ||
}, ''); | ||
} | ||
const DOMApis = { | ||
isText(node) { | ||
return node && node.type === 'text'; | ||
}, | ||
isNode(node) { | ||
return node && node.type === 'element'; | ||
}, | ||
isCommentNode(node) { | ||
return node && node.type === 'comment'; | ||
}, | ||
isCssLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'stylesheet'); | ||
} | ||
return false; | ||
}, | ||
isIconLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'icon'); | ||
} | ||
return false; | ||
}, | ||
isPrefetchJsLinkNode(node) { | ||
if (!this.isNode(node) || node.tagName !== 'link') | ||
return false; | ||
let hasRelAttr, hasAsAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'rel') { | ||
hasRelAttr = true; | ||
if (value !== 'preload' && value !== 'prefetch') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'as') { | ||
hasAsAttr = true; | ||
if (value !== 'script') | ||
return false; | ||
} | ||
} | ||
return Boolean(hasRelAttr && hasAsAttr); | ||
}, | ||
removeElement(el) { | ||
const parentNode = el && el.parentNode; | ||
if (parentNode) { | ||
parentNode.removeChild(el); | ||
} | ||
}, | ||
createElement(node) { | ||
const { tagName, attributes } = node; | ||
const el = isSVG(tagName) | ||
? document.createElementNS(ns, tagName) | ||
: document.createElement(tagName); | ||
this.applyAttributes(el, attributes); | ||
return el; | ||
}, | ||
createTextNode(node) { | ||
return document.createTextNode(node.content); | ||
}, | ||
createStyleNode(content) { | ||
const el = document.createElement('style'); | ||
content && (el.textContent = content); | ||
this.applyAttributes(el, [{ key: 'type', value: 'text/css' }]); | ||
return el; | ||
}, | ||
createLinkCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const ps = attributesString(node.attributes); | ||
return `<link ${ps.slice(0, -1)}></link>`; | ||
} | ||
else { | ||
node = node ? `src="${node}" ` : ''; | ||
return document.createComment(`<link ${node}execute by garfish(dynamic)></link>`); | ||
} | ||
}, | ||
createScriptCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const { attributes, children } = node; | ||
const ps = attributesString(attributes); | ||
const code = (children === null || children === void 0 ? void 0 : children[0]) ? children[0].content : ''; | ||
return document.createComment(`<script ${ps} execute by garfish>${code}</script>`); | ||
} | ||
else { | ||
const { src, code } = node; | ||
const url = src ? `src="${src}" ` : ''; | ||
return document.createComment(`<script ${url}execute by garfish(dynamic)>${code}</script>`); | ||
} | ||
}, | ||
applyAttributes(el, attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return; | ||
for (const { key, value } of attributes) { | ||
if (value === null) { | ||
el.setAttribute(key, ''); | ||
} | ||
else if (typeof value === 'string') { | ||
if (key.charCodeAt(0) !== xChar) { | ||
el.setAttribute(key, value); | ||
} | ||
else if (key.charCodeAt(3) === colonChar) { | ||
el.setAttributeNS(xmlNS, key, value); | ||
} | ||
else if (key.charCodeAt(5) === colonChar) { | ||
el.setAttributeNS(xlinkNS, key, value); | ||
} | ||
else { | ||
el.setAttribute(key, value); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
function parseContentType(input) { | ||
@@ -225,3 +359,3 @@ input = input === null || input === void 0 ? void 0 : input.trim(); | ||
let type = ''; | ||
let subtype = ''; | ||
let subType = ''; | ||
while (idx < input.length && input[idx] !== '/') { | ||
@@ -237,11 +371,11 @@ type += input[idx]; | ||
while (idx < input.length && input[idx] !== ';') { | ||
subtype += input[idx]; | ||
subType += input[idx]; | ||
idx++; | ||
} | ||
subtype = subtype.replace(/[ \t\n\r]+$/, ''); | ||
if (subtype.length === 0) | ||
subType = subType.replace(/[ \t\n\r]+$/, ''); | ||
if (subType.length === 0) | ||
return null; | ||
return { | ||
type: type.toLocaleLowerCase(), | ||
subtype: subtype.toLocaleLowerCase(), | ||
subtype: subType.toLocaleLowerCase(), | ||
}; | ||
@@ -1280,138 +1414,2 @@ } | ||
const xChar = 120; // "x" char | ||
const colonChar = 58; // ":" char | ||
const ns = 'http://www.w3.org/2000/svg'; | ||
const xlinkNS = 'http://www.w3.org/1999/xlink'; // xmlns:xlink | ||
const xmlNS = 'http://www.w3.org/XML/1998/namespace'; // xmlns | ||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element | ||
const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + | ||
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + | ||
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + | ||
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + | ||
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + | ||
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + | ||
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + | ||
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + | ||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + | ||
'text,textPath,title,tspan,unknown,use,view'; | ||
const isSVG = makeMap(SVG_TAGS.split(',')); | ||
function attributesString(attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return ''; | ||
return attributes.reduce((total, { key, value }) => { | ||
return total + (value ? `${key}="${value}" ` : key); | ||
}, ''); | ||
} | ||
const DOMApis = { | ||
isText(node) { | ||
return node && node.type === 'text'; | ||
}, | ||
isNode(node) { | ||
return node && node.type === 'element'; | ||
}, | ||
isCommentNode(node) { | ||
return node && node.type === 'comment'; | ||
}, | ||
isCssLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'stylesheet'); | ||
} | ||
return false; | ||
}, | ||
isIconLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'icon'); | ||
} | ||
return false; | ||
}, | ||
isPrefetchJsLinkNode(node) { | ||
if (!this.isNode(node) || node.tagName !== 'link') | ||
return false; | ||
let hasRelAttr, hasAsAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'rel') { | ||
hasRelAttr = true; | ||
if (value !== 'preload' && value !== 'prefetch') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'as') { | ||
hasAsAttr = true; | ||
if (value !== 'script') | ||
return false; | ||
} | ||
} | ||
return Boolean(hasRelAttr && hasAsAttr); | ||
}, | ||
removeElement(el) { | ||
const parentNode = el && el.parentNode; | ||
if (parentNode) { | ||
parentNode.removeChild(el); | ||
} | ||
}, | ||
createElement(node) { | ||
const { tagName, attributes } = node; | ||
const el = isSVG(tagName) | ||
? document.createElementNS(ns, tagName) | ||
: document.createElement(tagName); | ||
this.applyAttributes(el, attributes); | ||
return el; | ||
}, | ||
createTextNode(node) { | ||
return document.createTextNode(node.content); | ||
}, | ||
createStyleNode(content) { | ||
const el = document.createElement('style'); | ||
content && (el.textContent = content); | ||
this.applyAttributes(el, [{ key: 'type', value: 'text/css' }]); | ||
return el; | ||
}, | ||
createLinkCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const ps = attributesString(node.attributes); | ||
return `<link ${ps.slice(0, -1)}></link>`; | ||
} | ||
else { | ||
node = node ? `src="${node}" ` : ''; | ||
return document.createComment(`<link ${node}execute by garfish(dynamic)></link>`); | ||
} | ||
}, | ||
createScriptCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const { attributes, children } = node; | ||
const ps = attributesString(attributes); | ||
const code = (children === null || children === void 0 ? void 0 : children[0]) ? children[0].content : ''; | ||
return document.createComment(`<script ${ps} execute by garfish>${code}</script>`); | ||
} | ||
else { | ||
const { src, code } = node; | ||
const url = src ? `src="${src}" ` : ''; | ||
return document.createComment(`<script ${url}execute by garfish(dynamic)>${code}</script>`); | ||
} | ||
}, | ||
applyAttributes(el, attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return; | ||
for (const { key, value } of attributes) { | ||
if (value === null) { | ||
el.setAttribute(key, ''); | ||
} | ||
else if (typeof value === 'string') { | ||
if (key.charCodeAt(0) !== xChar) { | ||
el.setAttribute(key, value); | ||
} | ||
else if (key.charCodeAt(3) === colonChar) { | ||
el.setAttributeNS(xmlNS, key, value); | ||
} | ||
else if (key.charCodeAt(5) === colonChar) { | ||
el.setAttributeNS(xlinkNS, key, value); | ||
} | ||
else { | ||
el.setAttribute(key, value); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
// Convert irregular grammar to compliant grammar | ||
@@ -1688,3 +1686,2 @@ // 1M text takes about time: | ||
exports.ComponentManager = ComponentManager; | ||
exports.DOMApis = DOMApis; | ||
exports.JavaScriptManager = JavaScriptManager; | ||
@@ -1691,0 +1688,0 @@ exports.Loader = Loader; |
@@ -38,3 +38,3 @@ 'use strict'; | ||
} | ||
// 数组变为对象 `['a'] => { a: true }` | ||
// Array to Object `['a'] => { a: true }` | ||
function makeMap(list) { | ||
@@ -92,27 +92,26 @@ const map = Object.create(null); | ||
} | ||
// 深度合并两个对象,能处理循环引用,后面的覆盖前面的,可选数组去重 | ||
// Deeply merge two objects, can handle circular references, the latter overwrite the previous | ||
function deepMerge(o, n, dp) { | ||
const lRecord = new WeakMap(); | ||
const rRecord = new WeakMap(); | ||
const vRecord = new WeakMap(); | ||
const leftRecord = new WeakMap(); | ||
const rightRecord = new WeakMap(); | ||
const valueRecord = new WeakMap(); | ||
const isArray = Array.isArray; | ||
const isAllRefs = (a, b) => { | ||
// 判断 merge 左右两边,不需要用到 vRecord | ||
if (lRecord.has(a) || rRecord.has(a)) { | ||
return lRecord.has(b) || rRecord.has(b); | ||
if (leftRecord.has(a) || rightRecord.has(a)) { | ||
return leftRecord.has(b) || rightRecord.has(b); | ||
} | ||
}; | ||
const clone = (v) => { | ||
// 深拷贝 | ||
// Deep clone | ||
if (isPrimitive(v) || typeof v === 'function') { | ||
return v; | ||
} | ||
else if (vRecord.has(v)) { | ||
return vRecord.get(v); | ||
else if (valueRecord.has(v)) { | ||
return valueRecord.get(v); | ||
} | ||
else if (lRecord.has(v)) { | ||
return lRecord.get(v); | ||
else if (leftRecord.has(v)) { | ||
return leftRecord.get(v); | ||
} | ||
else if (rRecord.has(v)) { | ||
return rRecord.get(v); | ||
else if (rightRecord.has(v)) { | ||
return rightRecord.get(v); | ||
} | ||
@@ -122,15 +121,15 @@ else if (isArray(v)) { | ||
v = unique(v); | ||
const res = []; | ||
vRecord.set(v, res); | ||
const arr = []; | ||
valueRecord.set(v, arr); | ||
for (let i = 0, len = v.length; i < len; i++) { | ||
res[i] = clone(v[i]); | ||
arr[i] = clone(v[i]); | ||
} | ||
return res; | ||
return arr; | ||
} | ||
else if (typeof v === 'object') { | ||
const res = {}; | ||
vRecord.set(v, res); | ||
const obj = {}; | ||
valueRecord.set(v, obj); | ||
const keys = Reflect.ownKeys(v); | ||
keys.forEach((key) => (res[key] = clone(v[key]))); | ||
return res; | ||
keys.forEach((key) => (obj[key] = clone(v[key]))); | ||
return obj; | ||
} | ||
@@ -152,7 +151,7 @@ }; | ||
const res = {}; | ||
const lkeys = Reflect.ownKeys(l); | ||
const rkeys = Reflect.ownKeys(r); | ||
lRecord.set(l, res); | ||
rRecord.set(r, res); | ||
lkeys.forEach((key) => { | ||
const leftKeys = Reflect.ownKeys(l); | ||
const rightKeys = Reflect.ownKeys(r); | ||
leftRecord.set(l, res); | ||
rightRecord.set(r, res); | ||
leftKeys.forEach((key) => { | ||
const lv = l[key]; | ||
@@ -167,17 +166,17 @@ const rv = r[key]; | ||
res[key] = isAllRefs(lv, rv) | ||
? lRecord.get(lv) // 左边右边同一个值,取哪个都行 | ||
? leftRecord.get(lv) // The same value on the left and right, whichever is OK | ||
: mergeObject(lv, rv); | ||
} | ||
else { | ||
res[key] = setValue(rRecord, rv); | ||
res[key] = setValue(rightRecord, rv); | ||
} | ||
} | ||
else { | ||
res[key] = setValue(lRecord, lv); | ||
res[key] = setValue(leftRecord, lv); | ||
} | ||
}); | ||
rkeys.forEach((key) => { | ||
rightKeys.forEach((key) => { | ||
if (hasOwn(res, key)) | ||
return; | ||
res[key] = setValue(rRecord, r[key]); | ||
res[key] = setValue(rightRecord, r[key]); | ||
}); | ||
@@ -188,14 +187,2 @@ return res; | ||
} | ||
// copy from https://github.com/getsentry/sentry-javascript/blob/6.4.0/packages/browser/src/tracekit.ts | ||
const sourceListTags = [ | ||
'link', | ||
'img', | ||
'script', | ||
'video', | ||
'audio', | ||
'style', | ||
]; | ||
makeMap(sourceListTags); | ||
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1 | ||
@@ -218,2 +205,149 @@ // Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3 | ||
// copy from https://github.com/getsentry/sentry-javascript/blob/6.4.0/packages/browser/src/tracekit.ts | ||
const sourceListTags = [ | ||
'link', | ||
'img', | ||
'script', | ||
'video', | ||
'audio', | ||
'style', | ||
]; | ||
makeMap(sourceListTags); | ||
const xChar = 120; // "x" char | ||
const colonChar = 58; // ":" char | ||
const ns = 'http://www.w3.org/2000/svg'; | ||
const xlinkNS = 'http://www.w3.org/1999/xlink'; // xmlns:xlink | ||
const xmlNS = 'http://www.w3.org/XML/1998/namespace'; // xmlns | ||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element | ||
const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + | ||
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + | ||
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + | ||
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + | ||
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + | ||
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + | ||
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + | ||
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + | ||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + | ||
'text,textPath,title,tspan,unknown,use,view'; | ||
const isSVG = makeMap(SVG_TAGS.split(',')); | ||
function attributesString(attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return ''; | ||
return attributes.reduce((total, { key, value }) => { | ||
return total + (value ? `${key}="${value}" ` : key); | ||
}, ''); | ||
} | ||
const DOMApis = { | ||
isText(node) { | ||
return node && node.type === 'text'; | ||
}, | ||
isNode(node) { | ||
return node && node.type === 'element'; | ||
}, | ||
isCommentNode(node) { | ||
return node && node.type === 'comment'; | ||
}, | ||
isCssLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'stylesheet'); | ||
} | ||
return false; | ||
}, | ||
isIconLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'icon'); | ||
} | ||
return false; | ||
}, | ||
isPrefetchJsLinkNode(node) { | ||
if (!this.isNode(node) || node.tagName !== 'link') | ||
return false; | ||
let hasRelAttr, hasAsAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'rel') { | ||
hasRelAttr = true; | ||
if (value !== 'preload' && value !== 'prefetch') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'as') { | ||
hasAsAttr = true; | ||
if (value !== 'script') | ||
return false; | ||
} | ||
} | ||
return Boolean(hasRelAttr && hasAsAttr); | ||
}, | ||
removeElement(el) { | ||
const parentNode = el && el.parentNode; | ||
if (parentNode) { | ||
parentNode.removeChild(el); | ||
} | ||
}, | ||
createElement(node) { | ||
const { tagName, attributes } = node; | ||
const el = isSVG(tagName) | ||
? document.createElementNS(ns, tagName) | ||
: document.createElement(tagName); | ||
this.applyAttributes(el, attributes); | ||
return el; | ||
}, | ||
createTextNode(node) { | ||
return document.createTextNode(node.content); | ||
}, | ||
createStyleNode(content) { | ||
const el = document.createElement('style'); | ||
content && (el.textContent = content); | ||
this.applyAttributes(el, [{ key: 'type', value: 'text/css' }]); | ||
return el; | ||
}, | ||
createLinkCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const ps = attributesString(node.attributes); | ||
return `<link ${ps.slice(0, -1)}></link>`; | ||
} | ||
else { | ||
node = node ? `src="${node}" ` : ''; | ||
return document.createComment(`<link ${node}execute by garfish(dynamic)></link>`); | ||
} | ||
}, | ||
createScriptCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const { attributes, children } = node; | ||
const ps = attributesString(attributes); | ||
const code = (children === null || children === void 0 ? void 0 : children[0]) ? children[0].content : ''; | ||
return document.createComment(`<script ${ps} execute by garfish>${code}</script>`); | ||
} | ||
else { | ||
const { src, code } = node; | ||
const url = src ? `src="${src}" ` : ''; | ||
return document.createComment(`<script ${url}execute by garfish(dynamic)>${code}</script>`); | ||
} | ||
}, | ||
applyAttributes(el, attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return; | ||
for (const { key, value } of attributes) { | ||
if (value === null) { | ||
el.setAttribute(key, ''); | ||
} | ||
else if (typeof value === 'string') { | ||
if (key.charCodeAt(0) !== xChar) { | ||
el.setAttribute(key, value); | ||
} | ||
else if (key.charCodeAt(3) === colonChar) { | ||
el.setAttributeNS(xmlNS, key, value); | ||
} | ||
else if (key.charCodeAt(5) === colonChar) { | ||
el.setAttributeNS(xlinkNS, key, value); | ||
} | ||
else { | ||
el.setAttribute(key, value); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
function parseContentType(input) { | ||
@@ -225,3 +359,3 @@ input = input === null || input === void 0 ? void 0 : input.trim(); | ||
let type = ''; | ||
let subtype = ''; | ||
let subType = ''; | ||
while (idx < input.length && input[idx] !== '/') { | ||
@@ -237,11 +371,11 @@ type += input[idx]; | ||
while (idx < input.length && input[idx] !== ';') { | ||
subtype += input[idx]; | ||
subType += input[idx]; | ||
idx++; | ||
} | ||
subtype = subtype.replace(/[ \t\n\r]+$/, ''); | ||
if (subtype.length === 0) | ||
subType = subType.replace(/[ \t\n\r]+$/, ''); | ||
if (subType.length === 0) | ||
return null; | ||
return { | ||
type: type.toLocaleLowerCase(), | ||
subtype: subtype.toLocaleLowerCase(), | ||
subtype: subType.toLocaleLowerCase(), | ||
}; | ||
@@ -1278,138 +1412,2 @@ } | ||
const xChar = 120; // "x" char | ||
const colonChar = 58; // ":" char | ||
const ns = 'http://www.w3.org/2000/svg'; | ||
const xlinkNS = 'http://www.w3.org/1999/xlink'; // xmlns:xlink | ||
const xmlNS = 'http://www.w3.org/XML/1998/namespace'; // xmlns | ||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element | ||
const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + | ||
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + | ||
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + | ||
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + | ||
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + | ||
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + | ||
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + | ||
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + | ||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + | ||
'text,textPath,title,tspan,unknown,use,view'; | ||
const isSVG = makeMap(SVG_TAGS.split(',')); | ||
function attributesString(attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return ''; | ||
return attributes.reduce((total, { key, value }) => { | ||
return total + (value ? `${key}="${value}" ` : key); | ||
}, ''); | ||
} | ||
const DOMApis = { | ||
isText(node) { | ||
return node && node.type === 'text'; | ||
}, | ||
isNode(node) { | ||
return node && node.type === 'element'; | ||
}, | ||
isCommentNode(node) { | ||
return node && node.type === 'comment'; | ||
}, | ||
isCssLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'stylesheet'); | ||
} | ||
return false; | ||
}, | ||
isIconLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'icon'); | ||
} | ||
return false; | ||
}, | ||
isPrefetchJsLinkNode(node) { | ||
if (!this.isNode(node) || node.tagName !== 'link') | ||
return false; | ||
let hasRelAttr, hasAsAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'rel') { | ||
hasRelAttr = true; | ||
if (value !== 'preload' && value !== 'prefetch') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'as') { | ||
hasAsAttr = true; | ||
if (value !== 'script') | ||
return false; | ||
} | ||
} | ||
return Boolean(hasRelAttr && hasAsAttr); | ||
}, | ||
removeElement(el) { | ||
const parentNode = el && el.parentNode; | ||
if (parentNode) { | ||
parentNode.removeChild(el); | ||
} | ||
}, | ||
createElement(node) { | ||
const { tagName, attributes } = node; | ||
const el = isSVG(tagName) | ||
? document.createElementNS(ns, tagName) | ||
: document.createElement(tagName); | ||
this.applyAttributes(el, attributes); | ||
return el; | ||
}, | ||
createTextNode(node) { | ||
return document.createTextNode(node.content); | ||
}, | ||
createStyleNode(content) { | ||
const el = document.createElement('style'); | ||
content && (el.textContent = content); | ||
this.applyAttributes(el, [{ key: 'type', value: 'text/css' }]); | ||
return el; | ||
}, | ||
createLinkCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const ps = attributesString(node.attributes); | ||
return `<link ${ps.slice(0, -1)}></link>`; | ||
} | ||
else { | ||
node = node ? `src="${node}" ` : ''; | ||
return document.createComment(`<link ${node}execute by garfish(dynamic)></link>`); | ||
} | ||
}, | ||
createScriptCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const { attributes, children } = node; | ||
const ps = attributesString(attributes); | ||
const code = (children === null || children === void 0 ? void 0 : children[0]) ? children[0].content : ''; | ||
return document.createComment(`<script ${ps} execute by garfish>${code}</script>`); | ||
} | ||
else { | ||
const { src, code } = node; | ||
const url = src ? `src="${src}" ` : ''; | ||
return document.createComment(`<script ${url}execute by garfish(dynamic)>${code}</script>`); | ||
} | ||
}, | ||
applyAttributes(el, attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return; | ||
for (const { key, value } of attributes) { | ||
if (value === null) { | ||
el.setAttribute(key, ''); | ||
} | ||
else if (typeof value === 'string') { | ||
if (key.charCodeAt(0) !== xChar) { | ||
el.setAttribute(key, value); | ||
} | ||
else if (key.charCodeAt(3) === colonChar) { | ||
el.setAttributeNS(xmlNS, key, value); | ||
} | ||
else if (key.charCodeAt(5) === colonChar) { | ||
el.setAttributeNS(xlinkNS, key, value); | ||
} | ||
else { | ||
el.setAttribute(key, value); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
// Convert irregular grammar to compliant grammar | ||
@@ -1686,3 +1684,2 @@ // 1M text takes about time: | ||
exports.ComponentManager = ComponentManager; | ||
exports.DOMApis = DOMApis; | ||
exports.JavaScriptManager = JavaScriptManager; | ||
@@ -1689,0 +1686,0 @@ exports.Loader = Loader; |
@@ -34,3 +34,3 @@ /*! ***************************************************************************** | ||
} | ||
// 数组变为对象 `['a'] => { a: true }` | ||
// Array to Object `['a'] => { a: true }` | ||
function makeMap(list) { | ||
@@ -88,27 +88,26 @@ const map = Object.create(null); | ||
} | ||
// 深度合并两个对象,能处理循环引用,后面的覆盖前面的,可选数组去重 | ||
// Deeply merge two objects, can handle circular references, the latter overwrite the previous | ||
function deepMerge(o, n, dp) { | ||
const lRecord = new WeakMap(); | ||
const rRecord = new WeakMap(); | ||
const vRecord = new WeakMap(); | ||
const leftRecord = new WeakMap(); | ||
const rightRecord = new WeakMap(); | ||
const valueRecord = new WeakMap(); | ||
const isArray = Array.isArray; | ||
const isAllRefs = (a, b) => { | ||
// 判断 merge 左右两边,不需要用到 vRecord | ||
if (lRecord.has(a) || rRecord.has(a)) { | ||
return lRecord.has(b) || rRecord.has(b); | ||
if (leftRecord.has(a) || rightRecord.has(a)) { | ||
return leftRecord.has(b) || rightRecord.has(b); | ||
} | ||
}; | ||
const clone = (v) => { | ||
// 深拷贝 | ||
// Deep clone | ||
if (isPrimitive(v) || typeof v === 'function') { | ||
return v; | ||
} | ||
else if (vRecord.has(v)) { | ||
return vRecord.get(v); | ||
else if (valueRecord.has(v)) { | ||
return valueRecord.get(v); | ||
} | ||
else if (lRecord.has(v)) { | ||
return lRecord.get(v); | ||
else if (leftRecord.has(v)) { | ||
return leftRecord.get(v); | ||
} | ||
else if (rRecord.has(v)) { | ||
return rRecord.get(v); | ||
else if (rightRecord.has(v)) { | ||
return rightRecord.get(v); | ||
} | ||
@@ -118,15 +117,15 @@ else if (isArray(v)) { | ||
v = unique(v); | ||
const res = []; | ||
vRecord.set(v, res); | ||
const arr = []; | ||
valueRecord.set(v, arr); | ||
for (let i = 0, len = v.length; i < len; i++) { | ||
res[i] = clone(v[i]); | ||
arr[i] = clone(v[i]); | ||
} | ||
return res; | ||
return arr; | ||
} | ||
else if (typeof v === 'object') { | ||
const res = {}; | ||
vRecord.set(v, res); | ||
const obj = {}; | ||
valueRecord.set(v, obj); | ||
const keys = Reflect.ownKeys(v); | ||
keys.forEach((key) => (res[key] = clone(v[key]))); | ||
return res; | ||
keys.forEach((key) => (obj[key] = clone(v[key]))); | ||
return obj; | ||
} | ||
@@ -148,7 +147,7 @@ }; | ||
const res = {}; | ||
const lkeys = Reflect.ownKeys(l); | ||
const rkeys = Reflect.ownKeys(r); | ||
lRecord.set(l, res); | ||
rRecord.set(r, res); | ||
lkeys.forEach((key) => { | ||
const leftKeys = Reflect.ownKeys(l); | ||
const rightKeys = Reflect.ownKeys(r); | ||
leftRecord.set(l, res); | ||
rightRecord.set(r, res); | ||
leftKeys.forEach((key) => { | ||
const lv = l[key]; | ||
@@ -163,17 +162,17 @@ const rv = r[key]; | ||
res[key] = isAllRefs(lv, rv) | ||
? lRecord.get(lv) // 左边右边同一个值,取哪个都行 | ||
? leftRecord.get(lv) // The same value on the left and right, whichever is OK | ||
: mergeObject(lv, rv); | ||
} | ||
else { | ||
res[key] = setValue(rRecord, rv); | ||
res[key] = setValue(rightRecord, rv); | ||
} | ||
} | ||
else { | ||
res[key] = setValue(lRecord, lv); | ||
res[key] = setValue(leftRecord, lv); | ||
} | ||
}); | ||
rkeys.forEach((key) => { | ||
rightKeys.forEach((key) => { | ||
if (hasOwn(res, key)) | ||
return; | ||
res[key] = setValue(rRecord, r[key]); | ||
res[key] = setValue(rightRecord, r[key]); | ||
}); | ||
@@ -184,14 +183,2 @@ return res; | ||
} | ||
// copy from https://github.com/getsentry/sentry-javascript/blob/6.4.0/packages/browser/src/tracekit.ts | ||
const sourceListTags = [ | ||
'link', | ||
'img', | ||
'script', | ||
'video', | ||
'audio', | ||
'style', | ||
]; | ||
makeMap(sourceListTags); | ||
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1 | ||
@@ -214,2 +201,149 @@ // Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3 | ||
// copy from https://github.com/getsentry/sentry-javascript/blob/6.4.0/packages/browser/src/tracekit.ts | ||
const sourceListTags = [ | ||
'link', | ||
'img', | ||
'script', | ||
'video', | ||
'audio', | ||
'style', | ||
]; | ||
makeMap(sourceListTags); | ||
const xChar = 120; // "x" char | ||
const colonChar = 58; // ":" char | ||
const ns = 'http://www.w3.org/2000/svg'; | ||
const xlinkNS = 'http://www.w3.org/1999/xlink'; // xmlns:xlink | ||
const xmlNS = 'http://www.w3.org/XML/1998/namespace'; // xmlns | ||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element | ||
const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + | ||
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + | ||
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + | ||
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + | ||
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + | ||
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + | ||
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + | ||
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + | ||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + | ||
'text,textPath,title,tspan,unknown,use,view'; | ||
const isSVG = makeMap(SVG_TAGS.split(',')); | ||
function attributesString(attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return ''; | ||
return attributes.reduce((total, { key, value }) => { | ||
return total + (value ? `${key}="${value}" ` : key); | ||
}, ''); | ||
} | ||
const DOMApis = { | ||
isText(node) { | ||
return node && node.type === 'text'; | ||
}, | ||
isNode(node) { | ||
return node && node.type === 'element'; | ||
}, | ||
isCommentNode(node) { | ||
return node && node.type === 'comment'; | ||
}, | ||
isCssLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'stylesheet'); | ||
} | ||
return false; | ||
}, | ||
isIconLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'icon'); | ||
} | ||
return false; | ||
}, | ||
isPrefetchJsLinkNode(node) { | ||
if (!this.isNode(node) || node.tagName !== 'link') | ||
return false; | ||
let hasRelAttr, hasAsAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'rel') { | ||
hasRelAttr = true; | ||
if (value !== 'preload' && value !== 'prefetch') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'as') { | ||
hasAsAttr = true; | ||
if (value !== 'script') | ||
return false; | ||
} | ||
} | ||
return Boolean(hasRelAttr && hasAsAttr); | ||
}, | ||
removeElement(el) { | ||
const parentNode = el && el.parentNode; | ||
if (parentNode) { | ||
parentNode.removeChild(el); | ||
} | ||
}, | ||
createElement(node) { | ||
const { tagName, attributes } = node; | ||
const el = isSVG(tagName) | ||
? document.createElementNS(ns, tagName) | ||
: document.createElement(tagName); | ||
this.applyAttributes(el, attributes); | ||
return el; | ||
}, | ||
createTextNode(node) { | ||
return document.createTextNode(node.content); | ||
}, | ||
createStyleNode(content) { | ||
const el = document.createElement('style'); | ||
content && (el.textContent = content); | ||
this.applyAttributes(el, [{ key: 'type', value: 'text/css' }]); | ||
return el; | ||
}, | ||
createLinkCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const ps = attributesString(node.attributes); | ||
return `<link ${ps.slice(0, -1)}></link>`; | ||
} | ||
else { | ||
node = node ? `src="${node}" ` : ''; | ||
return document.createComment(`<link ${node}execute by garfish(dynamic)></link>`); | ||
} | ||
}, | ||
createScriptCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const { attributes, children } = node; | ||
const ps = attributesString(attributes); | ||
const code = (children === null || children === void 0 ? void 0 : children[0]) ? children[0].content : ''; | ||
return document.createComment(`<script ${ps} execute by garfish>${code}</script>`); | ||
} | ||
else { | ||
const { src, code } = node; | ||
const url = src ? `src="${src}" ` : ''; | ||
return document.createComment(`<script ${url}execute by garfish(dynamic)>${code}</script>`); | ||
} | ||
}, | ||
applyAttributes(el, attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return; | ||
for (const { key, value } of attributes) { | ||
if (value === null) { | ||
el.setAttribute(key, ''); | ||
} | ||
else if (typeof value === 'string') { | ||
if (key.charCodeAt(0) !== xChar) { | ||
el.setAttribute(key, value); | ||
} | ||
else if (key.charCodeAt(3) === colonChar) { | ||
el.setAttributeNS(xmlNS, key, value); | ||
} | ||
else if (key.charCodeAt(5) === colonChar) { | ||
el.setAttributeNS(xlinkNS, key, value); | ||
} | ||
else { | ||
el.setAttribute(key, value); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
function parseContentType(input) { | ||
@@ -221,3 +355,3 @@ input = input === null || input === void 0 ? void 0 : input.trim(); | ||
let type = ''; | ||
let subtype = ''; | ||
let subType = ''; | ||
while (idx < input.length && input[idx] !== '/') { | ||
@@ -233,11 +367,11 @@ type += input[idx]; | ||
while (idx < input.length && input[idx] !== ';') { | ||
subtype += input[idx]; | ||
subType += input[idx]; | ||
idx++; | ||
} | ||
subtype = subtype.replace(/[ \t\n\r]+$/, ''); | ||
if (subtype.length === 0) | ||
subType = subType.replace(/[ \t\n\r]+$/, ''); | ||
if (subType.length === 0) | ||
return null; | ||
return { | ||
type: type.toLocaleLowerCase(), | ||
subtype: subtype.toLocaleLowerCase(), | ||
subtype: subType.toLocaleLowerCase(), | ||
}; | ||
@@ -1276,138 +1410,2 @@ } | ||
const xChar = 120; // "x" char | ||
const colonChar = 58; // ":" char | ||
const ns = 'http://www.w3.org/2000/svg'; | ||
const xlinkNS = 'http://www.w3.org/1999/xlink'; // xmlns:xlink | ||
const xmlNS = 'http://www.w3.org/XML/1998/namespace'; // xmlns | ||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element | ||
const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + | ||
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + | ||
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + | ||
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + | ||
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + | ||
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + | ||
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + | ||
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + | ||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + | ||
'text,textPath,title,tspan,unknown,use,view'; | ||
const isSVG = makeMap(SVG_TAGS.split(',')); | ||
function attributesString(attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return ''; | ||
return attributes.reduce((total, { key, value }) => { | ||
return total + (value ? `${key}="${value}" ` : key); | ||
}, ''); | ||
} | ||
const DOMApis = { | ||
isText(node) { | ||
return node && node.type === 'text'; | ||
}, | ||
isNode(node) { | ||
return node && node.type === 'element'; | ||
}, | ||
isCommentNode(node) { | ||
return node && node.type === 'comment'; | ||
}, | ||
isCssLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'stylesheet'); | ||
} | ||
return false; | ||
}, | ||
isIconLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'icon'); | ||
} | ||
return false; | ||
}, | ||
isPrefetchJsLinkNode(node) { | ||
if (!this.isNode(node) || node.tagName !== 'link') | ||
return false; | ||
let hasRelAttr, hasAsAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'rel') { | ||
hasRelAttr = true; | ||
if (value !== 'preload' && value !== 'prefetch') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'as') { | ||
hasAsAttr = true; | ||
if (value !== 'script') | ||
return false; | ||
} | ||
} | ||
return Boolean(hasRelAttr && hasAsAttr); | ||
}, | ||
removeElement(el) { | ||
const parentNode = el && el.parentNode; | ||
if (parentNode) { | ||
parentNode.removeChild(el); | ||
} | ||
}, | ||
createElement(node) { | ||
const { tagName, attributes } = node; | ||
const el = isSVG(tagName) | ||
? document.createElementNS(ns, tagName) | ||
: document.createElement(tagName); | ||
this.applyAttributes(el, attributes); | ||
return el; | ||
}, | ||
createTextNode(node) { | ||
return document.createTextNode(node.content); | ||
}, | ||
createStyleNode(content) { | ||
const el = document.createElement('style'); | ||
content && (el.textContent = content); | ||
this.applyAttributes(el, [{ key: 'type', value: 'text/css' }]); | ||
return el; | ||
}, | ||
createLinkCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const ps = attributesString(node.attributes); | ||
return `<link ${ps.slice(0, -1)}></link>`; | ||
} | ||
else { | ||
node = node ? `src="${node}" ` : ''; | ||
return document.createComment(`<link ${node}execute by garfish(dynamic)></link>`); | ||
} | ||
}, | ||
createScriptCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const { attributes, children } = node; | ||
const ps = attributesString(attributes); | ||
const code = (children === null || children === void 0 ? void 0 : children[0]) ? children[0].content : ''; | ||
return document.createComment(`<script ${ps} execute by garfish>${code}</script>`); | ||
} | ||
else { | ||
const { src, code } = node; | ||
const url = src ? `src="${src}" ` : ''; | ||
return document.createComment(`<script ${url}execute by garfish(dynamic)>${code}</script>`); | ||
} | ||
}, | ||
applyAttributes(el, attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return; | ||
for (const { key, value } of attributes) { | ||
if (value === null) { | ||
el.setAttribute(key, ''); | ||
} | ||
else if (typeof value === 'string') { | ||
if (key.charCodeAt(0) !== xChar) { | ||
el.setAttribute(key, value); | ||
} | ||
else if (key.charCodeAt(3) === colonChar) { | ||
el.setAttributeNS(xmlNS, key, value); | ||
} | ||
else if (key.charCodeAt(5) === colonChar) { | ||
el.setAttributeNS(xlinkNS, key, value); | ||
} | ||
else { | ||
el.setAttribute(key, value); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
// Convert irregular grammar to compliant grammar | ||
@@ -1683,2 +1681,2 @@ // 1M text takes about time: | ||
export { ComponentManager, DOMApis, JavaScriptManager, Loader, StyleManager, TemplateManager }; | ||
export { ComponentManager, JavaScriptManager, Loader, StyleManager, TemplateManager }; |
@@ -40,3 +40,3 @@ (function (global, factory) { | ||
} | ||
// 数组变为对象 `['a'] => { a: true }` | ||
// Array to Object `['a'] => { a: true }` | ||
function makeMap(list) { | ||
@@ -94,27 +94,26 @@ const map = Object.create(null); | ||
} | ||
// 深度合并两个对象,能处理循环引用,后面的覆盖前面的,可选数组去重 | ||
// Deeply merge two objects, can handle circular references, the latter overwrite the previous | ||
function deepMerge(o, n, dp) { | ||
const lRecord = new WeakMap(); | ||
const rRecord = new WeakMap(); | ||
const vRecord = new WeakMap(); | ||
const leftRecord = new WeakMap(); | ||
const rightRecord = new WeakMap(); | ||
const valueRecord = new WeakMap(); | ||
const isArray = Array.isArray; | ||
const isAllRefs = (a, b) => { | ||
// 判断 merge 左右两边,不需要用到 vRecord | ||
if (lRecord.has(a) || rRecord.has(a)) { | ||
return lRecord.has(b) || rRecord.has(b); | ||
if (leftRecord.has(a) || rightRecord.has(a)) { | ||
return leftRecord.has(b) || rightRecord.has(b); | ||
} | ||
}; | ||
const clone = (v) => { | ||
// 深拷贝 | ||
// Deep clone | ||
if (isPrimitive(v) || typeof v === 'function') { | ||
return v; | ||
} | ||
else if (vRecord.has(v)) { | ||
return vRecord.get(v); | ||
else if (valueRecord.has(v)) { | ||
return valueRecord.get(v); | ||
} | ||
else if (lRecord.has(v)) { | ||
return lRecord.get(v); | ||
else if (leftRecord.has(v)) { | ||
return leftRecord.get(v); | ||
} | ||
else if (rRecord.has(v)) { | ||
return rRecord.get(v); | ||
else if (rightRecord.has(v)) { | ||
return rightRecord.get(v); | ||
} | ||
@@ -124,15 +123,15 @@ else if (isArray(v)) { | ||
v = unique(v); | ||
const res = []; | ||
vRecord.set(v, res); | ||
const arr = []; | ||
valueRecord.set(v, arr); | ||
for (let i = 0, len = v.length; i < len; i++) { | ||
res[i] = clone(v[i]); | ||
arr[i] = clone(v[i]); | ||
} | ||
return res; | ||
return arr; | ||
} | ||
else if (typeof v === 'object') { | ||
const res = {}; | ||
vRecord.set(v, res); | ||
const obj = {}; | ||
valueRecord.set(v, obj); | ||
const keys = Reflect.ownKeys(v); | ||
keys.forEach((key) => (res[key] = clone(v[key]))); | ||
return res; | ||
keys.forEach((key) => (obj[key] = clone(v[key]))); | ||
return obj; | ||
} | ||
@@ -154,7 +153,7 @@ }; | ||
const res = {}; | ||
const lkeys = Reflect.ownKeys(l); | ||
const rkeys = Reflect.ownKeys(r); | ||
lRecord.set(l, res); | ||
rRecord.set(r, res); | ||
lkeys.forEach((key) => { | ||
const leftKeys = Reflect.ownKeys(l); | ||
const rightKeys = Reflect.ownKeys(r); | ||
leftRecord.set(l, res); | ||
rightRecord.set(r, res); | ||
leftKeys.forEach((key) => { | ||
const lv = l[key]; | ||
@@ -169,17 +168,17 @@ const rv = r[key]; | ||
res[key] = isAllRefs(lv, rv) | ||
? lRecord.get(lv) // 左边右边同一个值,取哪个都行 | ||
? leftRecord.get(lv) // The same value on the left and right, whichever is OK | ||
: mergeObject(lv, rv); | ||
} | ||
else { | ||
res[key] = setValue(rRecord, rv); | ||
res[key] = setValue(rightRecord, rv); | ||
} | ||
} | ||
else { | ||
res[key] = setValue(lRecord, lv); | ||
res[key] = setValue(leftRecord, lv); | ||
} | ||
}); | ||
rkeys.forEach((key) => { | ||
rightKeys.forEach((key) => { | ||
if (hasOwn(res, key)) | ||
return; | ||
res[key] = setValue(rRecord, r[key]); | ||
res[key] = setValue(rightRecord, r[key]); | ||
}); | ||
@@ -190,14 +189,2 @@ return res; | ||
} | ||
// copy from https://github.com/getsentry/sentry-javascript/blob/6.4.0/packages/browser/src/tracekit.ts | ||
const sourceListTags = [ | ||
'link', | ||
'img', | ||
'script', | ||
'video', | ||
'audio', | ||
'style', | ||
]; | ||
makeMap(sourceListTags); | ||
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1 | ||
@@ -220,2 +207,149 @@ // Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3 | ||
// copy from https://github.com/getsentry/sentry-javascript/blob/6.4.0/packages/browser/src/tracekit.ts | ||
const sourceListTags = [ | ||
'link', | ||
'img', | ||
'script', | ||
'video', | ||
'audio', | ||
'style', | ||
]; | ||
makeMap(sourceListTags); | ||
const xChar = 120; // "x" char | ||
const colonChar = 58; // ":" char | ||
const ns = 'http://www.w3.org/2000/svg'; | ||
const xlinkNS = 'http://www.w3.org/1999/xlink'; // xmlns:xlink | ||
const xmlNS = 'http://www.w3.org/XML/1998/namespace'; // xmlns | ||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element | ||
const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + | ||
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + | ||
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + | ||
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + | ||
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + | ||
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + | ||
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + | ||
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + | ||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + | ||
'text,textPath,title,tspan,unknown,use,view'; | ||
const isSVG = makeMap(SVG_TAGS.split(',')); | ||
function attributesString(attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return ''; | ||
return attributes.reduce((total, { key, value }) => { | ||
return total + (value ? `${key}="${value}" ` : key); | ||
}, ''); | ||
} | ||
const DOMApis = { | ||
isText(node) { | ||
return node && node.type === 'text'; | ||
}, | ||
isNode(node) { | ||
return node && node.type === 'element'; | ||
}, | ||
isCommentNode(node) { | ||
return node && node.type === 'comment'; | ||
}, | ||
isCssLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'stylesheet'); | ||
} | ||
return false; | ||
}, | ||
isIconLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'icon'); | ||
} | ||
return false; | ||
}, | ||
isPrefetchJsLinkNode(node) { | ||
if (!this.isNode(node) || node.tagName !== 'link') | ||
return false; | ||
let hasRelAttr, hasAsAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'rel') { | ||
hasRelAttr = true; | ||
if (value !== 'preload' && value !== 'prefetch') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'as') { | ||
hasAsAttr = true; | ||
if (value !== 'script') | ||
return false; | ||
} | ||
} | ||
return Boolean(hasRelAttr && hasAsAttr); | ||
}, | ||
removeElement(el) { | ||
const parentNode = el && el.parentNode; | ||
if (parentNode) { | ||
parentNode.removeChild(el); | ||
} | ||
}, | ||
createElement(node) { | ||
const { tagName, attributes } = node; | ||
const el = isSVG(tagName) | ||
? document.createElementNS(ns, tagName) | ||
: document.createElement(tagName); | ||
this.applyAttributes(el, attributes); | ||
return el; | ||
}, | ||
createTextNode(node) { | ||
return document.createTextNode(node.content); | ||
}, | ||
createStyleNode(content) { | ||
const el = document.createElement('style'); | ||
content && (el.textContent = content); | ||
this.applyAttributes(el, [{ key: 'type', value: 'text/css' }]); | ||
return el; | ||
}, | ||
createLinkCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const ps = attributesString(node.attributes); | ||
return `<link ${ps.slice(0, -1)}></link>`; | ||
} | ||
else { | ||
node = node ? `src="${node}" ` : ''; | ||
return document.createComment(`<link ${node}execute by garfish(dynamic)></link>`); | ||
} | ||
}, | ||
createScriptCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const { attributes, children } = node; | ||
const ps = attributesString(attributes); | ||
const code = (children === null || children === void 0 ? void 0 : children[0]) ? children[0].content : ''; | ||
return document.createComment(`<script ${ps} execute by garfish>${code}</script>`); | ||
} | ||
else { | ||
const { src, code } = node; | ||
const url = src ? `src="${src}" ` : ''; | ||
return document.createComment(`<script ${url}execute by garfish(dynamic)>${code}</script>`); | ||
} | ||
}, | ||
applyAttributes(el, attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return; | ||
for (const { key, value } of attributes) { | ||
if (value === null) { | ||
el.setAttribute(key, ''); | ||
} | ||
else if (typeof value === 'string') { | ||
if (key.charCodeAt(0) !== xChar) { | ||
el.setAttribute(key, value); | ||
} | ||
else if (key.charCodeAt(3) === colonChar) { | ||
el.setAttributeNS(xmlNS, key, value); | ||
} | ||
else if (key.charCodeAt(5) === colonChar) { | ||
el.setAttributeNS(xlinkNS, key, value); | ||
} | ||
else { | ||
el.setAttribute(key, value); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
function parseContentType(input) { | ||
@@ -227,3 +361,3 @@ input = input === null || input === void 0 ? void 0 : input.trim(); | ||
let type = ''; | ||
let subtype = ''; | ||
let subType = ''; | ||
while (idx < input.length && input[idx] !== '/') { | ||
@@ -239,11 +373,11 @@ type += input[idx]; | ||
while (idx < input.length && input[idx] !== ';') { | ||
subtype += input[idx]; | ||
subType += input[idx]; | ||
idx++; | ||
} | ||
subtype = subtype.replace(/[ \t\n\r]+$/, ''); | ||
if (subtype.length === 0) | ||
subType = subType.replace(/[ \t\n\r]+$/, ''); | ||
if (subType.length === 0) | ||
return null; | ||
return { | ||
type: type.toLocaleLowerCase(), | ||
subtype: subtype.toLocaleLowerCase(), | ||
subtype: subType.toLocaleLowerCase(), | ||
}; | ||
@@ -1282,138 +1416,2 @@ } | ||
const xChar = 120; // "x" char | ||
const colonChar = 58; // ":" char | ||
const ns = 'http://www.w3.org/2000/svg'; | ||
const xlinkNS = 'http://www.w3.org/1999/xlink'; // xmlns:xlink | ||
const xmlNS = 'http://www.w3.org/XML/1998/namespace'; // xmlns | ||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element | ||
const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + | ||
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + | ||
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + | ||
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + | ||
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + | ||
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + | ||
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + | ||
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + | ||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + | ||
'text,textPath,title,tspan,unknown,use,view'; | ||
const isSVG = makeMap(SVG_TAGS.split(',')); | ||
function attributesString(attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return ''; | ||
return attributes.reduce((total, { key, value }) => { | ||
return total + (value ? `${key}="${value}" ` : key); | ||
}, ''); | ||
} | ||
const DOMApis = { | ||
isText(node) { | ||
return node && node.type === 'text'; | ||
}, | ||
isNode(node) { | ||
return node && node.type === 'element'; | ||
}, | ||
isCommentNode(node) { | ||
return node && node.type === 'comment'; | ||
}, | ||
isCssLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'stylesheet'); | ||
} | ||
return false; | ||
}, | ||
isIconLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'icon'); | ||
} | ||
return false; | ||
}, | ||
isPrefetchJsLinkNode(node) { | ||
if (!this.isNode(node) || node.tagName !== 'link') | ||
return false; | ||
let hasRelAttr, hasAsAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'rel') { | ||
hasRelAttr = true; | ||
if (value !== 'preload' && value !== 'prefetch') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'as') { | ||
hasAsAttr = true; | ||
if (value !== 'script') | ||
return false; | ||
} | ||
} | ||
return Boolean(hasRelAttr && hasAsAttr); | ||
}, | ||
removeElement(el) { | ||
const parentNode = el && el.parentNode; | ||
if (parentNode) { | ||
parentNode.removeChild(el); | ||
} | ||
}, | ||
createElement(node) { | ||
const { tagName, attributes } = node; | ||
const el = isSVG(tagName) | ||
? document.createElementNS(ns, tagName) | ||
: document.createElement(tagName); | ||
this.applyAttributes(el, attributes); | ||
return el; | ||
}, | ||
createTextNode(node) { | ||
return document.createTextNode(node.content); | ||
}, | ||
createStyleNode(content) { | ||
const el = document.createElement('style'); | ||
content && (el.textContent = content); | ||
this.applyAttributes(el, [{ key: 'type', value: 'text/css' }]); | ||
return el; | ||
}, | ||
createLinkCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const ps = attributesString(node.attributes); | ||
return `<link ${ps.slice(0, -1)}></link>`; | ||
} | ||
else { | ||
node = node ? `src="${node}" ` : ''; | ||
return document.createComment(`<link ${node}execute by garfish(dynamic)></link>`); | ||
} | ||
}, | ||
createScriptCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const { attributes, children } = node; | ||
const ps = attributesString(attributes); | ||
const code = (children === null || children === void 0 ? void 0 : children[0]) ? children[0].content : ''; | ||
return document.createComment(`<script ${ps} execute by garfish>${code}</script>`); | ||
} | ||
else { | ||
const { src, code } = node; | ||
const url = src ? `src="${src}" ` : ''; | ||
return document.createComment(`<script ${url}execute by garfish(dynamic)>${code}</script>`); | ||
} | ||
}, | ||
applyAttributes(el, attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return; | ||
for (const { key, value } of attributes) { | ||
if (value === null) { | ||
el.setAttribute(key, ''); | ||
} | ||
else if (typeof value === 'string') { | ||
if (key.charCodeAt(0) !== xChar) { | ||
el.setAttribute(key, value); | ||
} | ||
else if (key.charCodeAt(3) === colonChar) { | ||
el.setAttributeNS(xmlNS, key, value); | ||
} | ||
else if (key.charCodeAt(5) === colonChar) { | ||
el.setAttributeNS(xlinkNS, key, value); | ||
} | ||
else { | ||
el.setAttribute(key, value); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
// Convert irregular grammar to compliant grammar | ||
@@ -1690,3 +1688,2 @@ // 1M text takes about time: | ||
exports.ComponentManager = ComponentManager; | ||
exports.DOMApis = DOMApis; | ||
exports.JavaScriptManager = JavaScriptManager; | ||
@@ -1693,0 +1690,0 @@ exports.Loader = Loader; |
{ | ||
"name": "@garfish/loader", | ||
"version": "0.0.9", | ||
"version": "0.0.12", | ||
"description": "loader module.", | ||
@@ -34,3 +34,3 @@ "keywords": [ | ||
"peerDependencies": { | ||
"@garfish/core": "^0.0.1" | ||
"@garfish/core": "^0.0.9" | ||
}, | ||
@@ -41,3 +41,3 @@ "publishConfig": { | ||
}, | ||
"gitHead": "e54d52d3757bb6d2fa95eb9d688e737b4e9fb25f" | ||
"gitHead": "306ff92ee38c5ca5b01c60b91453619f885620fe" | ||
} |
281959
-15.69%16
-84.31%7882
-14.64%