| // 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]); |