mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-08 20:58:24 +08:00
This CL bans the TS `!` operator which is used to assert not null. Typically when we use this we are making errors harder to track down should the value actually end up being null. Checking it takes more work but will give nicer errors. Fixed: 1154182 Change-Id: I11599541c199204e4ca21c9fd81a256614d70389 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2566804 Commit-Queue: Jack Franklin <jacktfranklin@chromium.org> Reviewed-by: Alex Rudenko <alexrudenko@chromium.org> Reviewed-by: Paul Lewis <aerotwist@chromium.org>
169 lines
5.5 KiB
TypeScript
169 lines
5.5 KiB
TypeScript
// 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 (!imports) {
|
||
throw new Error(`Expected to find imports for ${targetImportFile} but found none.`);
|
||
}
|
||
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);
|