blob: a482e38f316cccc7ed314c753ded5572fb3614ea [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.
import {CommentKind, ExpressionKind, IdentifierKind, 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);
const FRONT_END_FOLDER = path.join(__dirname, '..', '..', 'front_end');
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
function getFullTypeName(expression: MemberExpressionKind): any {
let name = '';
while (expression.object.type === 'MemberExpression') {
name = `${(expression.property as any).name}${name && `.${name}` || ''}`;
expression = expression.object;
}
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
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 usages =
child_process.execSync(`grep -r ${fullName} ${FRONT_END_FOLDER} || true`, {encoding: 'utf8'});
// It is only used once, in its assignment
if (usages.split('\n').length == 2) {
removedExports.push(assignment.right.name);
return b.emptyStatement();
}
} catch (e) {
console.log(e);
return statement;
}
}
}
}
return statement;
});
// Remove ES exports
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('.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]);