123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- 'use strict';
- /**
- * @typedef {import('../lib/types').XastElement} XastElement
- */
- const csso = require('csso');
- exports.type = 'visitor';
- exports.name = 'minifyStyles';
- exports.active = true;
- exports.description =
- 'minifies styles and removes unused styles based on usage data';
- /**
- * Minifies styles (<style> element + style attribute) using CSSO
- *
- * @author strarsis <strarsis@gmail.com>
- *
- * @type {import('../lib/types').Plugin<csso.MinifyOptions & Omit<csso.CompressOptions, 'usage'> & {
- * usage?: boolean | {
- * force?: boolean,
- * ids?: boolean,
- * classes?: boolean,
- * tags?: boolean
- * }
- * }>}
- */
- exports.fn = (_root, { usage, ...params }) => {
- let enableTagsUsage = true;
- let enableIdsUsage = true;
- let enableClassesUsage = true;
- // force to use usage data even if it unsafe (document contains <script> or on* attributes)
- let forceUsageDeoptimized = false;
- if (typeof usage === 'boolean') {
- enableTagsUsage = usage;
- enableIdsUsage = usage;
- enableClassesUsage = usage;
- } else if (usage) {
- enableTagsUsage = usage.tags == null ? true : usage.tags;
- enableIdsUsage = usage.ids == null ? true : usage.ids;
- enableClassesUsage = usage.classes == null ? true : usage.classes;
- forceUsageDeoptimized = usage.force == null ? false : usage.force;
- }
- /**
- * @type {Array<XastElement>}
- */
- const styleElements = [];
- /**
- * @type {Array<XastElement>}
- */
- const elementsWithStyleAttributes = [];
- let deoptimized = false;
- /**
- * @type {Set<string>}
- */
- const tagsUsage = new Set();
- /**
- * @type {Set<string>}
- */
- const idsUsage = new Set();
- /**
- * @type {Set<string>}
- */
- const classesUsage = new Set();
- return {
- element: {
- enter: (node) => {
- // detect deoptimisations
- if (node.name === 'script') {
- deoptimized = true;
- }
- for (const name of Object.keys(node.attributes)) {
- if (name.startsWith('on')) {
- deoptimized = true;
- }
- }
- // collect tags, ids and classes usage
- tagsUsage.add(node.name);
- if (node.attributes.id != null) {
- idsUsage.add(node.attributes.id);
- }
- if (node.attributes.class != null) {
- for (const className of node.attributes.class.split(/\s+/)) {
- classesUsage.add(className);
- }
- }
- // collect style elements or elements with style attribute
- if (node.name === 'style' && node.children.length !== 0) {
- styleElements.push(node);
- } else if (node.attributes.style != null) {
- elementsWithStyleAttributes.push(node);
- }
- },
- },
- root: {
- exit: () => {
- /**
- * @type {csso.Usage}
- */
- const cssoUsage = {};
- if (deoptimized === false || forceUsageDeoptimized === true) {
- if (enableTagsUsage && tagsUsage.size !== 0) {
- cssoUsage.tags = Array.from(tagsUsage);
- }
- if (enableIdsUsage && idsUsage.size !== 0) {
- cssoUsage.ids = Array.from(idsUsage);
- }
- if (enableClassesUsage && classesUsage.size !== 0) {
- cssoUsage.classes = Array.from(classesUsage);
- }
- }
- // minify style elements
- for (const node of styleElements) {
- if (
- node.children[0].type === 'text' ||
- node.children[0].type === 'cdata'
- ) {
- const cssText = node.children[0].value;
- const minified = csso.minify(cssText, {
- ...params,
- usage: cssoUsage,
- }).css;
- // preserve cdata if necessary
- // TODO split cdata -> text optimisation into separate plugin
- if (cssText.indexOf('>') >= 0 || cssText.indexOf('<') >= 0) {
- node.children[0].type = 'cdata';
- node.children[0].value = minified;
- } else {
- node.children[0].type = 'text';
- node.children[0].value = minified;
- }
- }
- }
- // minify style attributes
- for (const node of elementsWithStyleAttributes) {
- // style attribute
- const elemStyle = node.attributes.style;
- node.attributes.style = csso.minifyBlock(elemStyle, {
- ...params,
- }).css;
- }
- },
- },
- };
- };
|