blob: 3a8f0dd0c81c168c6dd4172008d2fe38a5d138b9 [file] [log] [blame]
// Copyright 2019 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.
// eslint-disable-next-line rulesdir/es_modules_import
import {MemberExpressionKind} from 'ast-types/gen/kinds';
import child_process from 'child_process';
import fs from 'fs';
import path from 'path';
import {parse, print, types} from 'recast';
import {promisify} from 'util';
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
if (!process.env.PWD) {
throw new Error('Could not find required PWD environment variable.');
}
const FRONT_END_FOLDER = path.join(process.env.PWD, '..', '..', 'front_end');
const TEST_FOLDER =
path.join(process.env.PWD, '..', '..', '..', '..', 'blink', 'web_tests', 'http', 'tests', 'devtools');
function capitalizeFirstLetter(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
const FOLDER_MAPPING: {[name: string]: string} = require(path.join('..', 'build', 'special_case_namespaces.json'));
function computeNamespaceName(folderName: string): string {
if (folderName in FOLDER_MAPPING) {
return FOLDER_MAPPING[folderName];
}
return capitalizeFirstLetter(folderName);
}
// Transform an expression of UI.ARIAUtils.Foo to its string representation
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function getFullTypeName(expression: MemberExpressionKind): any {
let name = '';
while (expression.object.type === 'MemberExpression') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
name = `${(expression.property as any).name}${name && `.${name}` || ''}`;
expression = expression.object;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return `${(expression.property as any).name}${name && `.${name}` || ''}`;
}
const b = types.builders;
function rewriteSource(refactoringNamespace: string, source: string) {
const ast = parse(source);
const removedExports: string[] = [];
// Remove global exports
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ast.program.body = ast.program.body.map((statement: any) => {
// UI.ARIAUtils.Foo = 5;
if (statement.type === 'ExpressionStatement') {
// UI.ARIAUtils.Foo = 5
if (statement.expression.type === 'AssignmentExpression') {
const assignment = statement.expression;
if (assignment.left.type === 'MemberExpression') {
// UI.ARIAUtils
const topLevelAssignment = assignment.left;
// UI.ARIAUtils.Foo
const fullName = `${computeNamespaceName(refactoringNamespace)}.${getFullTypeName(topLevelAssignment)}`;
try {
const usedInModuleJson = Boolean(child_process.execSync(
`grep --include=\*module.json -r ${fullName} ${FRONT_END_FOLDER} || true`, {encoding: 'utf8'}));
const usedInLayoutTests =
Boolean(child_process.execSync(`grep -r ${fullName} ${TEST_FOLDER} || true`, {encoding: 'utf8'}));
const usedInLayoutTestRunners = Boolean(child_process.execSync(
`grep --include=\*test_runner\*.js -r ${fullName} ${FRONT_END_FOLDER} || true`, {encoding: 'utf8'}));
if (!usedInModuleJson && !usedInLayoutTests && !usedInLayoutTestRunners) {
removedExports.push(assignment.right.name);
return b.emptyStatement();
}
} catch (e) {
console.log(e);
return statement;
}
}
}
}
return statement;
});
// Remove ES exports
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ast.program.body = ast.program.body.map((statement: any) => {
if (statement.type === 'ExportNamedDeclaration') {
if (statement.declaration) {
switch (statement.declaration.type) {
case 'ClassDeclaration':
if (removedExports.includes(statement.declaration.id.name)) {
return b.classDeclaration.from({
...statement.declaration,
comments: statement.comments || [],
});
}
break;
case 'VariableDeclaration':
if (removedExports.includes(statement.declaration.declarations[0].id.name)) {
return b.variableDeclaration.from({
...statement.declaration,
comments: statement.comments || [],
});
}
break;
case 'FunctionDeclaration':
if (removedExports.includes(statement.declaration.id.name)) {
return b.functionDeclaration.from({
...statement.declaration,
comments: statement.comments || [],
});
}
break;
default:
throw new Error(`Unknown type: ${statement.declaration.type}`);
}
}
}
return statement;
});
return print(ast).code;
}
async function main(refactoringNamespace: string) {
const folderName = path.join(FRONT_END_FOLDER, refactoringNamespace);
for (const file of fs.readdirSync(folderName, {withFileTypes: true})) {
if (!file.name.endsWith('-legacy.js')) {
continue;
}
const pathName = path.join(folderName, file.name);
const source = await readFile(pathName, {encoding: 'utf-8'});
const rewrittenSource = rewriteSource(refactoringNamespace, source);
await writeFile(pathName, rewrittenSource);
}
}
if (!process.argv[2]) {
console.error('No arguments specified. Run this script with "<folder-name>". For example: "common"');
process.exit(1);
}
if (process.argv[2].endsWith('test_runner')) {
process.exit(0);
}
main(process.argv[2]);