Files
react-native-devtools-frontend/scripts/migration/replace-internal-references.ts
T
Tim van der Lippe ba26b2b503 Run ESLint on the scripts directory
We weren't running ESLint on this directory and, as it turns out, there
are quite a few linter errors in this folder.

R=jacktfranklin@chromium.org

Change-Id: Ic6cb9f6ad4858f69114e0c433a954041215b49e2
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2096705
Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org>
Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
2020-03-11 14:47:51 +00:00

162 lines
5.1 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');
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) {
const comments = (path.node as any).comments;
for (const comment of comments) {
if (comment.loc) {
(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);
},
});
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);