blob: 98f2fdf1a12233cc34253f652259edcab5af8de0 [file] [log] [blame]
const fs = require('fs');
const path = require('path');
const css = require('css');
const cssWhat = require('css-what');
const acorn = require('acorn');
const utils = require('../utils');
const promisify = require('util').promisify;
const readFile = promisify(fs.readFile);
const FRONTEND_PATH = path.join(__dirname, '..', '..', 'front_end');
const classes = new Set();
const strings = new Set();
const trickyStrings = new Set([
'crc-node__tree-hostname',
'tooltip-boundary',
'terminal',
'terminal-cursor',
'composition-view'
]);
(async function() {
await Promise.all(fs.readdirSync(FRONTEND_PATH).map(dir => processFolder(dir)));
const unused = [];
for (const className of classes) {
if (strings.has(className) || trickyStrings.has(className))
continue;
if (className.startsWith('CodeMirror'))
continue;
if (className.startsWith('xterm-'))
continue;
if (className.startsWith('lh-'))
continue;
if (className.startsWith('cm-'))
continue;
if (className.startsWith('navigator-'))
continue;
if (className.startsWith('object-value-'))
continue;
if (className.startsWith('security-summary-'))
continue;
if (className.startsWith('security-explanation-title-'))
continue;
if (className.startsWith('security-explanation-'))
continue;
if (className.startsWith('lock-icon-'))
continue;
if (className.startsWith('security-property-'))
continue;
if (className.startsWith('url-scheme-'))
continue;
if (className.startsWith('infobar-'))
continue;
if (className.startsWith('shadow-root-depth-'))
continue;
if (className.startsWith('timeline-overview-'))
continue;
if (className.startsWith('spritesheet-'))
continue;
if (className.startsWith('report-icon--'))
continue;
if (checkSuffix('-start'))
continue;
if (checkSuffix('-end'))
continue;
if (checkSuffix('-column'))
continue;
if (checkSuffix('-overview-grid'))
continue;
if (checkSuffix('-overview-container'))
continue;
if (checkSuffix('-icon'))
continue;
unused.push(className);
function checkSuffix(suffix) {
return className.endsWith(suffix) && strings.has(className.substring(0, className.length - suffix.length));
}
}
console.log(unused);
console.log(unused.length);
})();
async function processFolder(dir) {
if (!utils.isDir(path.join(FRONTEND_PATH, dir)))
return;
const modulePath = path.join(FRONTEND_PATH, dir, 'module.json');
if (!utils.isFile(modulePath))
return;
const content = JSON.parse(await readFile(modulePath, 'utf8'));
const promises = [];
for (const resource of content.resources || []) {
if (!resource.endsWith('.css'))
continue;
promises.push(processCSSFile(path.join(FRONTEND_PATH, dir, resource)));
}
const skips = new Set(content.skip_compilation || []);
for (const script of content.scripts || []) {
if (skips.has(script))
continue;
promises.push(processScriptFile(path.join(FRONTEND_PATH, dir, script)));
}
await Promise.all(promises);
}
async function processCSSFile(cssFile) {
const content = await readFile(cssFile, 'utf8');
try {
const ast = css.parse(content);
for (const rule of ast.stylesheet.rules) {
for (const selector of rule.selectors || []) {
for (const token of parseSimpleSelector(selector)) {
if (token.name === 'class' || token.name === 'id')
classes.add(token.value);
}
}
}
} catch(e) {
console.log(cssFile, e)
}
}
function parseSimpleSelector(selector) {
// css-what isn't the best. Try catch.
try {
const parsed = cssWhat(selector)
return parsed[0] || [];
} catch(e) {
return [];
}
}
async function processScriptFile(scriptFile) {
const content = await readFile(scriptFile, 'utf8');
const tokens = acorn.tokenizer(content);
for (const token of tokens) {
if(token.type.label === 'string' || token.type.label === 'template') {
for (const word of token.value.split(' '))
strings.add(word);
const regex = /class\s*=\s*['"]?([\w\-_ ]*)/ig;
let result;
while ((result = regex.exec(token.value))) {
for (const word of result[1].split(' '))
strings.add(word);
}
}
}
}