blob: 7b01499f2db43ec660f7f4446050990d3d95f65e [file] [edit]
// 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');
const imports = generatedCode.moduleImports.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, imports, types, classDeclaration, creatorFunction]
.filter(x => {
// Filter out any of the previous parts that were empty, to avoid
// tonnes of blank lines in the bridge output.
return !!x;
})
.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));
}