Files
react-native-devtools-frontend/scripts/component_bridges/cli.ts
T
Jack Franklin 324f304f58 Component bridges presubmit check
This CL introduces a PRESUBMIT that will:

1. run `npm run regenerate-all-component-bridges`
2. Check that the git diff is clean.

If the git diff is not clean, it will fail. If the bridge has to be
manually edited (e.g. to work around a bug in the generator), it must
have a comment that matches the pattern `MANUALLY_EDITED_BRIDGE=...`.
The regeneration script will ignore bridges that contain these.

Change-Id: I372a3cd17a8e94bcdcea69ef64db189d2be6bde1
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2364597
Commit-Queue: Jack Franklin <jacktfranklin@chromium.org>
Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org>
2020-09-03 10:57:31 +00:00

123 lines
4.5 KiB
TypeScript

// Copyright 2020 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 * as fs from 'fs';
import * as path from 'path';
import {generateClosureBridge, GeneratedCode} from './generate_closure.js';
import {filePathToTypeScriptSourceFile, walkTree} from './walk_tree.js';
const chromeLicense = `// Copyright ${new Date().getFullYear()} 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.
`;
const getFullBridgeFilePath = (inputFilePath: string) => {
const dir = path.dirname(inputFilePath);
const baseName = path.basename(inputFilePath, '.ts');
return path.join(dir, `${baseName}_bridge.js`);
};
export const writeToDisk = (inputFilePath: string, generatedCode: GeneratedCode) => {
const baseName = path.basename(inputFilePath, '.ts');
const outputFileName = getFullBridgeFilePath(inputFilePath);
const importStatement = `import './${baseName}.js';`;
const types = generatedCode.types
.map(typePart => {
return typePart.join('\n');
})
.join('\n');
const classDeclaration = generatedCode.closureClass.join('\n');
const creatorFunction = generatedCode.creatorFunction.join('\n');
/*
* We replace any `\` with `/` here because the PRESUBMIT check rebuilds all
* bridge files And if we let it use OS-specific separators when outputting
* this comment it'll be different on Linux vs Windows systems. So to keep the
* output consistent we ensure we use Linux style separators here. This is
* only a comment to help the developer find the source file, so it's not a
* problem that the path isn't accurate on Windows.
*/
const relativeFilePath = path.relative(process.cwd(), inputFilePath).replace(/\\/g, '/');
const byHandWarning = `/**
* WARNING: do not modify this file by hand!
* it was automatically generated by the bridge generator
* if you made changes to the source code and need to update this file, run:
* npm run generate-bridge-file ${relativeFilePath}
*/
`;
// extra \n to ensure ending with a linebreak at end of file
const finalCode =
[chromeLicense, byHandWarning, importStatement, types, classDeclaration, creatorFunction].join('\n') + '\n';
fs.writeFileSync(outputFileName, finalCode, {encoding: 'utf8'});
return {
output: outputFileName,
code: finalCode,
};
};
interface Options {
forceRewriting: boolean;
silenceOutput: boolean;
}
const checkForManuallyEditedBridgeFile = (componentSourceFilePath: string): boolean => {
const bridgeFilePath = getFullBridgeFilePath(componentSourceFilePath);
if (!fs.existsSync(bridgeFilePath)) {
return false;
}
const contentsOfBridge = fs.readFileSync(bridgeFilePath, {encoding: 'utf-8'});
return contentsOfBridge.includes('MANUALLY_EDITED_BRIDGE=');
};
export const parseTypeScriptComponent = (componentSourceFilePath: string, options: Options = {
forceRewriting: false,
silenceOutput: false,
}) => {
if (!options.silenceOutput) {
console.log(`\n${path.basename(componentSourceFilePath)}`);
}
const hasManuallyEditedBridge = checkForManuallyEditedBridgeFile(componentSourceFilePath);
if (hasManuallyEditedBridge && !options.forceRewriting) {
if (!options.silenceOutput) {
console.log('Skipping bridge generation; existing bridge file contains a `MANUALLY_EDITED_BRIDGE=` comment.');
console.log('To regenerate, pass the `--force` flag or remove that comment from the existing bridge and re-run.');
}
return {output: undefined, code: undefined};
}
const file = filePathToTypeScriptSourceFile(componentSourceFilePath);
const state = walkTree(file, componentSourceFilePath);
const generatedCode = generateClosureBridge(state);
return writeToDisk(componentSourceFilePath, generatedCode);
};
export const main = (args: string[]) => {
const bridgeComponentPath = path.resolve(process.cwd(), args[0]);
const forceRewriting = args.includes('--force');
const silenceOutput = args.includes('--silent');
if (!bridgeComponentPath || !fs.existsSync(bridgeComponentPath)) {
throw new Error(`Could not find bridgeComponent path ${bridgeComponentPath}`);
}
const {output} = parseTypeScriptComponent(bridgeComponentPath, {forceRewriting, silenceOutput});
if (output && !silenceOutput) {
console.log('Wrote bridge file to', path.relative(process.cwd(), output));
}
return output;
};
if (require.main === module) {
main(process.argv.slice(2));
}