mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 13:44:39 +08:00
R=aerotwist@chromium.org Bug: 1011811 Change-Id: I4c4fd280adb4f5f7b327239d08d80b2cb98b203b Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2560248 Reviewed-by: Paul Lewis <aerotwist@chromium.org> Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org>
181 lines
4.6 KiB
JavaScript
181 lines
4.6 KiB
JavaScript
// Copyright 2020 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
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)));
|
|
}
|
|
for (const script of content.scripts || []) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|