Files
react-native-devtools-frontend/scripts/migration/replace-internal-references.ts
T
Paul Lewis 839037fe83 [TypeScript] disallow explicit any
This CL explicitly disallows use of the `any` keyword via eslint. This
is to make sure we don't accidentally miss typing info. This CL also
adds eslint-ignore statements for existing uses of any (mostly in tests)
as well as fixing a few cases where the types were simple enough to add
quickly.

R=jacktfranklin@chromium.org

Change-Id: Ia913a30d63c1ae3d969785af332a8dde512cc275
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2308544
Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
Commit-Queue: Paul Lewis <aerotwist@chromium.org>
2020-07-21 14:31:32 +00:00

166 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 { parse, print, types, visit } from 'recast';
import fs from 'fs';
import path from 'path';
import { promisify } from 'util';
import { getMappings } from './get-mappings.js';
const readDir = promisify(fs.readdir);
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
const b = types.builders;
const FRONT_END_FOLDER = path.join(__dirname, '..', '..', 'front_end');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async function rewriteSource(pathName: string, srcFile: string, mappings:Map<string, any>, useExternalRefs = false) {
const filePath = path.join(pathName, srcFile);
const srcFileContents = await readFile(filePath, { encoding: 'utf-8' });
const ast = parse(srcFileContents);
const importsRequired = new Set<{file: string, replacement: string, sameFolderReplacement: string}>();
visit(ast, {
visitComment(path) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const comments = (path.node as any).comments;
for (const comment of comments) {
if (comment.loc) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(comment.loc as any).indent = 0;
}
for (const [str, value] of mappings.entries()) {
const containsString = new RegExp(`${str}([^\\.\\w])`, 'g');
const stringMatches = containsString.exec(comment.value);
if (!stringMatches) {
continue;
}
const replacement = useExternalRefs ? value.replacement : value.sameFolderReplacement;
importsRequired.add(value);
comment.value = comment.value.replace(stringMatches[0], replacement + stringMatches[0].slice(-1));
}
}
this.traverse(path);
},
visitMemberExpression(path) {
const node = path.node;
const nodeCopy = b.memberExpression.from({...node, comments: []});
const nodeAsCode = print(nodeCopy).code;
for (const [str, value] of mappings.entries()) {
if (nodeAsCode !== str) {
continue;
}
const name = useExternalRefs ? value.replacement : value.sameFolderReplacement;
importsRequired.add(value);
return b.identifier.from({ name, comments: node.comments || [] });
}
this.traverse(path);
},
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const importMap = new Map<string, any[]>();
for (const { file, sameFolderReplacement, replacement } of importsRequired) {
if (filePath === file) {
continue;
}
const src = path.dirname(filePath);
const dst = path.dirname(file);
let replacementIdentifier = '';
let relativePath = path.relative(src, dst);
const isSameFolder = relativePath === '';
if (isSameFolder) {
relativePath = './';
replacementIdentifier = sameFolderReplacement;
} else {
relativePath += '/';
replacementIdentifier = replacement;
}
const targetImportFile = relativePath + path.basename(file);
if (!importMap.has(targetImportFile)) {
importMap.set(targetImportFile, []);
}
const imports = importMap.get(targetImportFile)!;
if (useExternalRefs) {
if (imports.length === 0) {
// We are creating statements like import * as Foo from '../foo/foo.js' so
// here we take the first part of the identifier, e.g. Foo.Bar.Bar gives us
// Foo so we can make import * as Foo from that.
const namespaceIdentifier = replacementIdentifier.split('.')[0];
imports.push(b.importNamespaceSpecifier(b.identifier(namespaceIdentifier)));
}
// Make sure there is only one import * from Foo import added.
continue;
}
imports.push(b.importSpecifier(b.identifier(replacementIdentifier)));
}
// Add missing imports.
for (const [targetImportFile, specifiers] of importMap) {
const newImport = b.importDeclaration.from({
specifiers,
comments: ast.program.body[0].comments,
source: b.literal(targetImportFile),
});
// Remove any file comments.
ast.program.body[0].comments = [];
// Add the import statements.
ast.program.body.unshift(newImport);
}
return print(ast).code;
}
async function main(folder: string, namespaces?: string[]) {
const pathName = path.join(FRONT_END_FOLDER, folder);
const srcDir = await readDir(pathName);
const useExternalRefs = namespaces !== undefined && (namespaces[0] !== folder);
let mappings = new Map();
if (namespaces && namespaces.length) {
for (const namespace of namespaces) {
mappings = await getMappings(namespace, mappings, useExternalRefs);
}
} else {
mappings = await getMappings(folder, mappings, useExternalRefs);
}
for (const srcFile of srcDir) {
if (srcFile === `${folder}.js` || srcFile === `${folder}-legacy.js` || !srcFile.endsWith('.js')) {
continue;
}
const distFileContents = await rewriteSource(pathName, srcFile, mappings, useExternalRefs);
await writeFile(path.join(pathName, `${srcFile}`), distFileContents);
}
}
if (!process.argv[2]) {
console.error('No arguments specified. Run this script with "<folder-name>". For example: "ui"');
process.exit(1);
}
main(process.argv[2], process.argv[3] && process.argv[3].split(',') || undefined);