blob: f4da3cd26d6cda31c19d3098335a53003d8f4857 [file]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.adjustMinifyCSSOptions = exports.defaultStrategy = exports.defaultMinifyOptions = exports.defaultMinifyCSSOptions = void 0;
/// <reference types="./types/clean-css" />
// Reference needed for d.ts distribution files in rollup-plugin-minify-html-literals
const CleanCSS = require("clean-css");
const optimization_level_1 = require("clean-css/lib/options/optimization-level");
const html_minifier_1 = require("html-minifier");
/**
* The default <code>clean-css</code> options, optimized for production
* minification.
*/
exports.defaultMinifyCSSOptions = {};
/**
* The default <code>html-minifier</code> options, optimized for production
* minification.
*/
exports.defaultMinifyOptions = {
caseSensitive: true,
collapseWhitespace: true,
decodeEntities: true,
minifyCSS: exports.defaultMinifyCSSOptions,
minifyJS: true,
processConditionalComments: true,
removeAttributeQuotes: false,
removeComments: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
};
/**
* The default strategy. This uses <code>html-minifier</code> to minify HTML and
* <code>clean-css</code> to minify CSS.
*/
exports.defaultStrategy = {
getPlaceholder(parts) {
// Using @ and (); will cause the expression not to be removed in CSS.
// However, sometimes the semicolon can be removed (ex: inline styles).
// In those cases, we want to make sure that the HTML splitting also
// accounts for the missing semicolon.
const suffix = '();';
let placeholder = '@TEMPLATE_EXPRESSION';
while (parts.some(part => part.text.includes(placeholder + suffix))) {
placeholder += '_';
}
return placeholder + suffix;
},
combineHTMLStrings(parts, placeholder) {
return parts.map(part => part.text).join(placeholder);
},
minifyHTML(html, options = {}) {
let minifyCSSOptions;
if (options.minifyCSS) {
if (options.minifyCSS !== true &&
typeof options.minifyCSS !== 'function') {
minifyCSSOptions = { ...options.minifyCSS };
}
else {
minifyCSSOptions = {};
}
}
else {
minifyCSSOptions = false;
}
let adjustedMinifyCSSOptions = false;
if (minifyCSSOptions) {
adjustedMinifyCSSOptions = adjustMinifyCSSOptions(minifyCSSOptions);
}
let result = html_minifier_1.minify(html, {
...options,
minifyCSS: adjustedMinifyCSSOptions
});
if (options.collapseWhitespace) {
// html-minifier does not support removing newlines inside <svg>
// attributes. Support this, but be careful not to remove newlines from
// supported areas (such as within <pre> and <textarea> tags).
const matches = Array.from(result.matchAll(/<svg/g)).reverse();
for (const match of matches) {
const startTagIndex = match.index;
const closeTagIndex = result.indexOf('</svg', startTagIndex);
if (closeTagIndex < 0) {
// Malformed SVG without a closing tag
continue;
}
const start = result.substring(0, startTagIndex);
let svg = result.substring(startTagIndex, closeTagIndex);
const end = result.substring(closeTagIndex);
svg = svg.replace(/\r?\n/g, '');
result = start + svg + end;
}
}
if (adjustedMinifyCSSOptions &&
adjustedMinifyCSSOptions.level[optimization_level_1.OptimizationLevel.One].tidySelectors) {
// Fix https://github.com/jakubpawlowicz/clean-css/issues/996
result = fixCleanCssTidySelectors(html, result);
}
return result;
},
minifyCSS(css, options = {}) {
const adjustedOptions = adjustMinifyCSSOptions(options);
const output = new CleanCSS(adjustedOptions).minify(css);
if (output.errors && output.errors.length) {
throw new Error(output.errors.join('\n\n'));
}
if (adjustedOptions.level[optimization_level_1.OptimizationLevel.One].tidySelectors) {
output.styles = fixCleanCssTidySelectors(css, output.styles);
}
return output.styles;
},
splitHTMLByPlaceholder(html, placeholder) {
const parts = html.split(placeholder);
// Make the last character (a semicolon) optional. See above.
if (placeholder.endsWith(';')) {
const withoutSemicolon = placeholder.substring(0, placeholder.length - 1);
for (let i = parts.length - 1; i >= 0; i--) {
parts.splice(i, 1, ...parts[i].split(withoutSemicolon));
}
}
return parts;
}
};
function adjustMinifyCSSOptions(options = {}) {
const level = optimization_level_1.optimizationLevelFrom(options.level);
const originalTransform = typeof options.level === 'object' &&
options.level[1] &&
options.level[1].transform;
level[optimization_level_1.OptimizationLevel.One].transform = (property, value) => {
if (value.startsWith('@TEMPLATE_EXPRESSION') && !value.endsWith(';')) {
// The CSS minifier has removed the semicolon from the placeholder
// and we need to add it back.
return (value = `${value};`);
}
return originalTransform ? originalTransform(property, value) : value;
};
return {
...options,
level
};
}
exports.adjustMinifyCSSOptions = adjustMinifyCSSOptions;
function fixCleanCssTidySelectors(original, result) {
const regex = /(::?.+\((.*)\))[\s\r\n]*{/gm;
let match;
while ((match = regex.exec(original)) != null) {
const pseudoClass = match[1];
const parameters = match[2];
if (!parameters.match(/\s/)) {
continue;
}
const parametersWithoutSpaces = parameters.replace(/\s/g, '');
const resultPseudoClass = pseudoClass.replace(parameters, parametersWithoutSpaces);
const resultStartIndex = result.indexOf(resultPseudoClass);
if (resultStartIndex < 0) {
continue;
}
const resultEndIndex = resultStartIndex + resultPseudoClass.length;
// Restore the original pseudo class with spaces
result =
result.substring(0, resultStartIndex) +
pseudoClass +
result.substring(resultEndIndex);
}
return result;
}
//# sourceMappingURL=strategy.js.map