84 lines
2.2 KiB
JavaScript
84 lines
2.2 KiB
JavaScript
|
const { memoize, isString, isRegExp } = require('lodash');
|
||
|
const { parse } = require('postcss');
|
||
|
const { CSS_TO_REMOVE } = require('./constants');
|
||
|
|
||
|
const getSelectorRemoveTesters = memoize(() =>
|
||
|
CSS_TO_REMOVE.map((x) => {
|
||
|
if (isString(x)) {
|
||
|
return (selector) => x === selector;
|
||
|
}
|
||
|
if (isRegExp(x)) {
|
||
|
return (selector) => x.test(selector);
|
||
|
}
|
||
|
|
||
|
throw new Error(`Unexpected type in CSS_TO_REMOVE content "${x}". Expected String or RegExp.`);
|
||
|
}),
|
||
|
);
|
||
|
|
||
|
const getRemoveTesters = memoize(() => {
|
||
|
const selectorTesters = getSelectorRemoveTesters();
|
||
|
|
||
|
// These are mostly carried over from the previous project
|
||
|
// https://gitlab.com/gitlab-org/frontend/gitlab-css-statistics/-/blob/2aa00af25dba08fc71081c77206f45efe817ea4b/lib/gl_startup_extract.js
|
||
|
return [
|
||
|
(node) => node.type === 'comment',
|
||
|
(node) =>
|
||
|
node.type === 'atrule' &&
|
||
|
(node.params === 'print' ||
|
||
|
node.params === 'prefers-reduced-motion: reduce' ||
|
||
|
node.name === 'keyframe' ||
|
||
|
node.name === 'charset'),
|
||
|
(node) => node.selector && node.selectors && !node.selectors.length,
|
||
|
(node) => node.selector && selectorTesters.some((fn) => fn(node.selector)),
|
||
|
(node) =>
|
||
|
node.type === 'decl' &&
|
||
|
(node.prop === 'transition' ||
|
||
|
node.prop.indexOf('-webkit-') > -1 ||
|
||
|
node.prop.indexOf('-ms-') > -1),
|
||
|
];
|
||
|
});
|
||
|
|
||
|
const getNodesToRemove = (nodes) => {
|
||
|
const removeTesters = getRemoveTesters();
|
||
|
const remNodes = [];
|
||
|
|
||
|
nodes.forEach((node) => {
|
||
|
if (removeTesters.some((fn) => fn(node))) {
|
||
|
remNodes.push(node);
|
||
|
} else if (node.nodes?.length) {
|
||
|
remNodes.push(...getNodesToRemove(node.nodes));
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return remNodes;
|
||
|
};
|
||
|
|
||
|
const getEmptyNodesToRemove = (nodes) =>
|
||
|
nodes
|
||
|
.filter((node) => node.nodes)
|
||
|
.reduce((acc, node) => {
|
||
|
if (node.nodes.length) {
|
||
|
acc.push(...getEmptyNodesToRemove(node.nodes));
|
||
|
} else {
|
||
|
acc.push(node);
|
||
|
}
|
||
|
|
||
|
return acc;
|
||
|
}, []);
|
||
|
|
||
|
const cleanCSS = (css) => {
|
||
|
const cssRoot = parse(css);
|
||
|
|
||
|
getNodesToRemove(cssRoot.nodes).forEach((node) => {
|
||
|
node.remove();
|
||
|
});
|
||
|
|
||
|
getEmptyNodesToRemove(cssRoot.nodes).forEach((node) => {
|
||
|
node.remove();
|
||
|
});
|
||
|
|
||
|
return cssRoot.toResult().css;
|
||
|
};
|
||
|
|
||
|
module.exports = { cleanCSS };
|