101 lines
5.3 KiB
JavaScript
101 lines
5.3 KiB
JavaScript
|
"use strict";
|
||
|
// -------------------------------------------------------------
|
||
|
// WARNING: this file is used by both the client and the server.
|
||
|
// Do not use any browser or node-specific API!
|
||
|
// -------------------------------------------------------------
|
||
|
/* eslint hammerhead/proto-methods: 2 */
|
||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||
|
};
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const regexp_escape_1 = __importDefault(require("../utils/regexp-escape"));
|
||
|
const internal_attributes_1 = __importDefault(require("../processing/dom/internal-attributes"));
|
||
|
const url_1 = require("../utils/url");
|
||
|
const attributes_1 = require("./dom/attributes");
|
||
|
const dom_1 = __importDefault(require("./dom"));
|
||
|
const SOURCE_MAP_RE = /\/\*\s*[#@]\s*sourceMappingURL\s*=[\s\S]*?\*\/|\/\/[\t ]*[#@][\t ]*sourceMappingURL[\t ]*=.*/ig;
|
||
|
const CSS_URL_PROPERTY_VALUE_RE = /(url\s*\(\s*(['"]?))([^\s]*?)(\2\s*\))|(@import\s+(['"]))([^\s]*?)(\6)/g;
|
||
|
const ATTRIBUTE_SELECTOR_RE = /((?:(\W?)(\w+))?\[\s*)(\w+)(\s*\^?=)/g;
|
||
|
const STYLESHEET_PROCESSING_START_COMMENT = '/*hammerhead|stylesheet|start*/';
|
||
|
const STYLESHEET_PROCESSING_END_COMMENT = '/*hammerhead|stylesheet|end*/';
|
||
|
const HOVER_PSEUDO_CLASS_RE = /:\s*hover(\W)/gi;
|
||
|
const PSEUDO_CLASS_RE = new RegExp(`\\[${internal_attributes_1.default.hoverPseudoClass}\\](\\W)`, 'ig');
|
||
|
const IS_STYLE_SHEET_PROCESSED_RE = new RegExp(`\\s*${(0, regexp_escape_1.default)(STYLESHEET_PROCESSING_START_COMMENT)}`, 'gi');
|
||
|
const STYLESHEET_PROCESSING_COMMENTS_RE = new RegExp(`${(0, regexp_escape_1.default)(STYLESHEET_PROCESSING_START_COMMENT)}\n?|` +
|
||
|
`\n?${(0, regexp_escape_1.default)(STYLESHEET_PROCESSING_END_COMMENT)}\\s*`, 'gi');
|
||
|
class StyleProcessor {
|
||
|
constructor() {
|
||
|
this.STYLESHEET_PROCESSING_START_COMMENT = STYLESHEET_PROCESSING_START_COMMENT;
|
||
|
this.STYLESHEET_PROCESSING_END_COMMENT = STYLESHEET_PROCESSING_END_COMMENT;
|
||
|
this.proxyless = false;
|
||
|
}
|
||
|
process(css, urlReplacer, shouldIncludeProcessingComments) {
|
||
|
if (!css || typeof css !== 'string' || shouldIncludeProcessingComments && IS_STYLE_SHEET_PROCESSED_RE.test(css))
|
||
|
return css;
|
||
|
// NOTE: Replace the :hover pseudo-class.
|
||
|
css = css.replace(HOVER_PSEUDO_CLASS_RE, '[' + internal_attributes_1.default.hoverPseudoClass + ']$1');
|
||
|
// NOTE: Remove all 'source map' directives.
|
||
|
css = css.replace(SOURCE_MAP_RE, '');
|
||
|
// NOTE: Replace URLs in CSS rules with proxy URLs.
|
||
|
if (!this.proxyless)
|
||
|
css = this._replaceStylesheetUrls(css, urlReplacer);
|
||
|
// NOTE: Replace url attributes to stored attributes
|
||
|
css = this._replaceUrlAttributes(css);
|
||
|
if (shouldIncludeProcessingComments)
|
||
|
css = `${STYLESHEET_PROCESSING_START_COMMENT}\n${css}\n${STYLESHEET_PROCESSING_END_COMMENT}`;
|
||
|
return css;
|
||
|
}
|
||
|
cleanUp(css, parseProxyUrl) {
|
||
|
if (typeof css !== 'string')
|
||
|
return css;
|
||
|
css = css
|
||
|
.replace(PSEUDO_CLASS_RE, ':hover$1')
|
||
|
.replace(internal_attributes_1.default.storedAttrPostfix, '');
|
||
|
css = this._removeStylesheetProcessingComments(css);
|
||
|
if (!this.proxyless) {
|
||
|
css = this._replaceStylesheetUrls(css, (url) => {
|
||
|
const parsedProxyUrl = parseProxyUrl(url);
|
||
|
return parsedProxyUrl ? parsedProxyUrl.destUrl : url;
|
||
|
});
|
||
|
}
|
||
|
return css;
|
||
|
}
|
||
|
_removeStylesheetProcessingComments(css) {
|
||
|
const parts = css.split(STYLESHEET_PROCESSING_COMMENTS_RE);
|
||
|
const stylesheetPartsFound = parts.length >= 3;
|
||
|
if (!stylesheetPartsFound)
|
||
|
return css;
|
||
|
for (let i = 0; i < parts.length; i += 2) {
|
||
|
let whiteSpaceCount = 0;
|
||
|
// NOTE: search for whitespaces from the end of the string
|
||
|
// we do not use /\s*$/ regex intentionally to improve performance
|
||
|
for (let j = parts[i].length - 1; j >= 0; j--) {
|
||
|
if (!(/\s/.test(parts[i][j]))) // eslint-disable-line @typescript-eslint/no-extra-parens
|
||
|
break;
|
||
|
whiteSpaceCount++;
|
||
|
}
|
||
|
parts[i] = parts[i].substring(0, parts[i].length - whiteSpaceCount);
|
||
|
}
|
||
|
return parts.join('');
|
||
|
}
|
||
|
_replaceStylesheetUrls(css, processor) {
|
||
|
return css.replace(CSS_URL_PROPERTY_VALUE_RE, (match, prefix1, _q1, url1, postfix1, prefix2, _q2, url2, postfix2) => {
|
||
|
const prefix = prefix1 || prefix2;
|
||
|
const url = url1 || url2;
|
||
|
const processedUrl = (0, url_1.isSpecialPage)(url) ? url : processor(url);
|
||
|
const postfix = postfix1 || postfix2;
|
||
|
return url ? prefix + processedUrl + postfix : match;
|
||
|
});
|
||
|
}
|
||
|
_replaceUrlAttributes(css) {
|
||
|
return css.replace(ATTRIBUTE_SELECTOR_RE, (match, prefix, prev, possibleTag, attribute, postfix) => {
|
||
|
const tagName = prev === '.' || prev === '#' ? '' : possibleTag;
|
||
|
if (!tagName || !attributes_1.URL_ATTR_TAGS[attribute] || attributes_1.URL_ATTR_TAGS[attribute].indexOf(tagName) === -1)
|
||
|
return match;
|
||
|
return prefix + dom_1.default.getStoredAttrName(attribute) + postfix;
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
exports.default = new StyleProcessor();module.exports = exports.default;
|
||
|
|