This commit is contained in:
Yanzhen Yu
2020-11-21 11:03:32 +08:00
parent c489e06acf
commit da182f4927
2 changed files with 108 additions and 89 deletions

View File

@@ -440,11 +440,11 @@ export function parse(css: string, options: ParserOptions = {}) {
* http://ostermiller.org/findcomment.html */ * http://ostermiller.org/findcomment.html */
return trim(m[0]) return trim(m[0])
.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '') .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '')
.replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, m => { .replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, (m) => {
return m.replace(/,/g, '\u200C'); return m.replace(/,/g, '\u200C');
}) })
.split(/\s*(?![^(]*\)),\s*/) .split(/\s*(?![^(]*\)),\s*/)
.map(s => { .map((s) => {
return s.replace(/\u200C/g, ','); return s.replace(/\u200C/g, ',');
}); });
} }
@@ -888,7 +888,7 @@ function addParent(obj: Stylesheet, parent?: Stylesheet) {
for (const k of Object.keys(obj)) { for (const k of Object.keys(obj)) {
const value = obj[k as keyof Stylesheet]; const value = obj[k as keyof Stylesheet];
if (Array.isArray(value)) { if (Array.isArray(value)) {
value.forEach(v => { value.forEach((v) => {
addParent(v, childParent); addParent(v, childParent);
}); });
} else if (value && typeof value === 'object') { } else if (value && typeof value === 'object') {

View File

@@ -34,9 +34,7 @@ function getValidTagName(tagName: string): string {
function getCssRulesString(s: CSSStyleSheet): string | null { function getCssRulesString(s: CSSStyleSheet): string | null {
try { try {
const rules = s.rules || s.cssRules; const rules = s.rules || s.cssRules;
return rules return rules ? Array.from(rules).map(getCssRuleString).join('') : null;
? Array.from(rules).map(getCssRuleString).join('')
: null;
} catch (error) { } catch (error) {
return null; return null;
} }
@@ -74,18 +72,20 @@ export function absoluteToStylesheet(
URL_IN_CSS_REF, URL_IN_CSS_REF,
(origin, quote1, path1, quote2, path2, path3) => { (origin, quote1, path1, quote2, path2, path3) => {
const filePath = path1 || path2 || path3; const filePath = path1 || path2 || path3;
const maybe_quote = quote1 || quote2 || ''; const maybeQuote = quote1 || quote2 || '';
if (!filePath) { if (!filePath) {
return origin; return origin;
} }
if (!RELATIVE_PATH.test(filePath)) { if (!RELATIVE_PATH.test(filePath)) {
return `url(${maybe_quote}${filePath}${maybe_quote})`; return `url(${maybeQuote}${filePath}${maybeQuote})`;
} }
if (DATA_URI.test(filePath)) { if (DATA_URI.test(filePath)) {
return `url(${maybe_quote}${filePath}${maybe_quote})`; return `url(${maybeQuote}${filePath}${maybeQuote})`;
} }
if (filePath[0] === '/') { if (filePath[0] === '/') {
return `url(${maybe_quote}${extractOrigin(href) + filePath}${maybe_quote})`; return `url(${maybeQuote}${
extractOrigin(href) + filePath
}${maybeQuote})`;
} }
const stack = href.split('/'); const stack = href.split('/');
const parts = filePath.split('/'); const parts = filePath.split('/');
@@ -99,7 +99,7 @@ export function absoluteToStylesheet(
stack.push(part); stack.push(part);
} }
} }
return `url(${maybe_quote}${stack.join('/')}${maybe_quote})`; return `url(${maybeQuote}${stack.join('/')}${maybeQuote})`;
}, },
); );
} }
@@ -179,7 +179,7 @@ export function _isBlockedElement(
}); });
} }
if (blockSelector) { if (blockSelector) {
return element.matches(blockSelector) return element.matches(blockSelector);
} }
return false; return false;
@@ -208,7 +208,11 @@ function serializeNode(
systemId: (n as DocumentType).systemId, systemId: (n as DocumentType).systemId,
}; };
case n.ELEMENT_NODE: case n.ELEMENT_NODE:
const needBlock = _isBlockedElement(n as HTMLElement, blockClass, blockSelector); const needBlock = _isBlockedElement(
n as HTMLElement,
blockClass,
blockSelector,
);
const tagName = getValidTagName((n as HTMLElement).tagName); const tagName = getValidTagName((n as HTMLElement).tagName);
let attributes: attributes = {}; let attributes: attributes = {};
for (const { name, value } of Array.from((n as HTMLElement).attributes)) { for (const { name, value } of Array.from((n as HTMLElement).attributes)) {
@@ -339,7 +343,7 @@ function serializeNode(
} }
} }
function lowerIfExists(maybeAttr : string | number | boolean) : string { function lowerIfExists(maybeAttr: string | number | boolean): string {
if (maybeAttr === undefined) { if (maybeAttr === undefined) {
return ''; return '';
} else { } else {
@@ -347,68 +351,83 @@ function lowerIfExists(maybeAttr : string | number | boolean) : string {
} }
} }
function slimDOMExcluded(sn: serializedNode, slimDOMOptions: SlimDOMOptions): boolean { function slimDOMExcluded(
sn: serializedNode,
slimDOMOptions: SlimDOMOptions,
): boolean {
if (slimDOMOptions.comment && sn.type === NodeType.Comment) { if (slimDOMOptions.comment && sn.type === NodeType.Comment) {
// TODO: convert IE conditional comments to real nodes // TODO: convert IE conditional comments to real nodes
return true; return true;
} else if (sn.type === NodeType.Element) { } else if (sn.type === NodeType.Element) {
if (slimDOMOptions.script && if (
(sn.tagName === 'script' || slimDOMOptions.script &&
(sn.tagName === 'link' && sn.attributes.rel === 'preload' && sn.attributes['as'] === 'script') (sn.tagName === 'script' ||
)) { (sn.tagName === 'link' &&
sn.attributes.rel === 'preload' &&
sn.attributes.as === 'script'))
) {
return true; return true;
} else if (slimDOMOptions.headFavicon && ( } else if (
(sn.tagName === 'link' && sn.attributes.rel === 'shortcut icon') slimDOMOptions.headFavicon &&
|| (sn.tagName === 'meta' && ( ((sn.tagName === 'link' && sn.attributes.rel === 'shortcut icon') ||
lowerIfExists(sn.attributes['name']).match(/^msapplication-tile(image|color)$/) (sn.tagName === 'meta' &&
|| lowerIfExists(sn.attributes['name']) === 'application-name' (lowerIfExists(sn.attributes.name).match(
|| lowerIfExists(sn.attributes['rel']) === 'icon' /^msapplication-tile(image|color)$/,
|| lowerIfExists(sn.attributes['rel']) === 'apple-touch-icon' ) ||
|| lowerIfExists(sn.attributes['rel']) === 'shortcut icon' lowerIfExists(sn.attributes.name) === 'application-name' ||
)))) { lowerIfExists(sn.attributes.rel) === 'icon' ||
lowerIfExists(sn.attributes.rel) === 'apple-touch-icon' ||
lowerIfExists(sn.attributes.rel) === 'shortcut icon')))
) {
return true; return true;
} else if (sn.tagName === 'meta') { } else if (sn.tagName === 'meta') {
if (slimDOMOptions.headMetaDescKeywords && ( if (
lowerIfExists(sn.attributes['name']).match(/^description|keywords$/) slimDOMOptions.headMetaDescKeywords &&
)) { lowerIfExists(sn.attributes.name).match(/^description|keywords$/)
) {
return true; return true;
} else if (slimDOMOptions.headMetaSocial && ( } else if (
lowerIfExists(sn.attributes['property']).match(/^(og|twitter|fb):/) // og = opengraph (facebook) slimDOMOptions.headMetaSocial &&
|| lowerIfExists(sn.attributes['name']).match(/^(og|twitter):/) (lowerIfExists(sn.attributes.property).match(/^(og|twitter|fb):/) || // og = opengraph (facebook)
|| lowerIfExists(sn.attributes['name']) === 'pinterest' lowerIfExists(sn.attributes.name).match(/^(og|twitter):/) ||
)) { lowerIfExists(sn.attributes.name) === 'pinterest')
) {
return true; return true;
} else if (slimDOMOptions.headMetaRobots && ( } else if (
lowerIfExists(sn.attributes['name']) === 'robots' slimDOMOptions.headMetaRobots &&
|| lowerIfExists(sn.attributes['name']) === 'googlebot' (lowerIfExists(sn.attributes.name) === 'robots' ||
|| lowerIfExists(sn.attributes['name']) === 'bingbot' lowerIfExists(sn.attributes.name) === 'googlebot' ||
)) { lowerIfExists(sn.attributes.name) === 'bingbot')
) {
return true; return true;
} else if (slimDOMOptions.headMetaHttpEquiv && ( } else if (
slimDOMOptions.headMetaHttpEquiv &&
sn.attributes['http-equiv'] !== undefined sn.attributes['http-equiv'] !== undefined
)) { ) {
// e.g. X-UA-Compatible, Content-Type, Content-Language, // e.g. X-UA-Compatible, Content-Type, Content-Language,
// cache-control, X-Translated-By // cache-control, X-Translated-By
return true; return true;
} else if (slimDOMOptions.headMetaAuthorship && ( } else if (
lowerIfExists(sn.attributes['name']) === 'author' slimDOMOptions.headMetaAuthorship &&
|| lowerIfExists(sn.attributes['name']) === 'generator' (lowerIfExists(sn.attributes.name) === 'author' ||
|| lowerIfExists(sn.attributes['name']) === 'framework' lowerIfExists(sn.attributes.name) === 'generator' ||
|| lowerIfExists(sn.attributes['name']) === 'publisher' lowerIfExists(sn.attributes.name) === 'framework' ||
|| lowerIfExists(sn.attributes['name']) === 'progid' lowerIfExists(sn.attributes.name) === 'publisher' ||
|| lowerIfExists(sn.attributes['property']).match(/^article:/) lowerIfExists(sn.attributes.name) === 'progid' ||
|| lowerIfExists(sn.attributes['property']).match(/^product:/) lowerIfExists(sn.attributes.property).match(/^article:/) ||
)) { lowerIfExists(sn.attributes.property).match(/^product:/))
) {
return true; return true;
} else if (slimDOMOptions.headMetaVerification && ( } else if (
lowerIfExists(sn.attributes['name']) === 'google-site-verification' slimDOMOptions.headMetaVerification &&
|| lowerIfExists(sn.attributes['name']) === 'yandex-verification' (lowerIfExists(sn.attributes.name) === 'google-site-verification' ||
|| lowerIfExists(sn.attributes['name']) === 'csrf-token' lowerIfExists(sn.attributes.name) === 'yandex-verification' ||
|| lowerIfExists(sn.attributes['name']) === 'p:domain_verify' lowerIfExists(sn.attributes.name) === 'csrf-token' ||
|| lowerIfExists(sn.attributes['name']) === 'verify-v1' lowerIfExists(sn.attributes.name) === 'p:domain_verify' ||
|| lowerIfExists(sn.attributes['name']) === 'verification' lowerIfExists(sn.attributes.name) === 'verify-v1' ||
|| lowerIfExists(sn.attributes['name']) === 'shopify-checkout-api-token' lowerIfExists(sn.attributes.name) === 'verification' ||
)) { lowerIfExists(sn.attributes.name) === 'shopify-checkout-api-token')
) {
return true; return true;
} }
} }
@@ -448,12 +467,13 @@ export function serializeNodeWithId(
// Try to reuse the previous id // Try to reuse the previous id
if ('__sn' in n) { if ('__sn' in n) {
id = n.__sn.id; id = n.__sn.id;
} else if (slimDOMExcluded(_serializedNode, slimDOMOptions) || } else if (
(!preserveWhiteSpace && slimDOMExcluded(_serializedNode, slimDOMOptions) ||
_serializedNode.type === NodeType.Text && (!preserveWhiteSpace &&
!_serializedNode.isStyle && _serializedNode.type === NodeType.Text &&
!_serializedNode.textContent.replace(/^\s+|\s+$/gm,'').length !_serializedNode.isStyle &&
)) { !_serializedNode.textContent.replace(/^\s+|\s+$/gm, '').length)
) {
id = IGNORED_NODE; id = IGNORED_NODE;
} else { } else {
id = genId(); id = genId();
@@ -461,7 +481,7 @@ export function serializeNodeWithId(
const serializedNode = Object.assign(_serializedNode, { id }); const serializedNode = Object.assign(_serializedNode, { id });
(n as INode).__sn = serializedNode; (n as INode).__sn = serializedNode;
if (id === IGNORED_NODE) { if (id === IGNORED_NODE) {
return null; // slimDOM return null; // slimDOM
} }
map[id] = n as INode; map[id] = n as INode;
let recordChild = !skipChild; let recordChild = !skipChild;
@@ -476,9 +496,9 @@ export function serializeNodeWithId(
recordChild recordChild
) { ) {
if ( if (
(slimDOMOptions.headWhitespace && slimDOMOptions.headWhitespace &&
_serializedNode.type === NodeType.Element && _serializedNode.type === NodeType.Element &&
_serializedNode.tagName == 'head') _serializedNode.tagName === 'head'
// would impede performance: || getComputedStyle(n)['white-space'] === 'normal' // would impede performance: || getComputedStyle(n)['white-space'] === 'normal'
) { ) {
preserveWhiteSpace = false; preserveWhiteSpace = false;
@@ -538,24 +558,23 @@ function snapshot(
? {} ? {}
: maskAllInputsOrOptions; : maskAllInputsOrOptions;
const slimDOMOptions: SlimDOMOptions = const slimDOMOptions: SlimDOMOptions =
(slimDOMSensibleOrOptions === true || slimDOMSensibleOrOptions === true || slimDOMSensibleOrOptions === 'all'
slimDOMSensibleOrOptions === 'all') ? // if true: set of sensible options that should not throw away any information
// if true: set of sensible options that should not throw away any information {
? { script: true,
script: true, comment: true,
comment: true, headFavicon: true,
headFavicon: true, headWhitespace: true,
headWhitespace: true, headMetaDescKeywords: slimDOMSensibleOrOptions === 'all', // destructive
headMetaDescKeywords: slimDOMSensibleOrOptions === 'all', // destructive headMetaSocial: true,
headMetaSocial: true, headMetaRobots: true,
headMetaRobots: true, headMetaHttpEquiv: true,
headMetaHttpEquiv: true, headMetaAuthorship: true,
headMetaAuthorship: true, headMetaVerification: true,
headMetaVerification: true, }
} : slimDOMSensibleOrOptions === false
: slimDOMSensibleOrOptions === false ? {}
? {} : slimDOMSensibleOrOptions;
: slimDOMSensibleOrOptions;
return [ return [
serializeNodeWithId( serializeNodeWithId(
n, n,