Keen Yee Liau 7a7781e925 refactor(bazel): Remove NodeJsSyncHost (#29796)
`NodeJsSyncHost` is no longer provided by BuilderContext in
architect v2, and its usage caused subtle path resolution issues
in Windows.

This PR cleans up `@angular/bazel` builder to use all native path
and fs methods.

PR Close #29796
2019-04-15 10:52:03 -07:00

148 lines
4.1 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/// <reference types='node'/>
import {fork} from 'child_process';
import {copyFileSync, existsSync, readdirSync, statSync, unlinkSync} from 'fs';
import {dirname, join, normalize} from 'path';
export type Executable = 'bazel' | 'ibazel';
export type Command = 'build' | 'test' | 'run' | 'coverage' | 'query';
/**
* Spawn the Bazel process. Trap SINGINT to make sure Bazel process is killed.
*/
export function runBazel(
projectDir: string, binary: string, command: Command, workspaceTarget: string,
flags: string[]) {
projectDir = normalize(projectDir);
binary = normalize(binary);
return new Promise((resolve, reject) => {
const buildProcess = fork(binary, [command, workspaceTarget, ...flags], {
cwd: projectDir,
stdio: 'inherit',
});
process.on('SIGINT', (signal) => {
if (!buildProcess.killed) {
buildProcess.kill();
reject(new Error(`Bazel process received ${signal}.`));
}
});
buildProcess.once('close', (code: number) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`${binary} failed with code ${code}.`));
}
});
});
}
/**
* Resolves the path to `@bazel/bazel` or `@bazel/ibazel`.
*/
export function checkInstallation(name: Executable, projectDir: string): string {
projectDir = normalize(projectDir);
const packageName = `@bazel/${name}/package.json`;
try {
const bazelPath = require.resolve(packageName, {
paths: [projectDir],
});
return dirname(bazelPath);
} catch (error) {
if (error.code === 'MODULE_NOT_FOUND') {
throw new Error(
`Could not run ${name}. Please make sure that the ` +
`"${name}" command is installed by running ` +
`"npm install ${packageName}" or "yarn install ${packageName}".`);
}
throw error;
}
}
/**
* Returns the absolute path to the template directory in `@angular/bazel`.
*/
export function getTemplateDir(root: string): string {
root = normalize(root);
const packageJson = require.resolve('@angular/bazel/package.json', {
paths: [root],
});
const packageDir = dirname(packageJson);
const templateDir = join(packageDir, 'src', 'builders', 'files');
if (!statSync(templateDir).isDirectory()) {
throw new Error('Could not find Bazel template directory in "@angular/bazel".');
}
return templateDir;
}
/**
* Recursively list the specified 'dir' using depth-first approach. Paths
* returned are relative to 'dir'.
*/
function listR(dir: string): string[] {
function list(dir: string, root: string, results: string[]) {
const paths = readdirSync(dir);
for (const path of paths) {
const absPath = join(dir, path);
const relPath = join(root, path);
if (statSync(absPath).isFile()) {
results.push(relPath);
} else {
list(absPath, relPath, results);
}
}
return results;
}
return list(dir, '', []);
}
/**
* Copy Bazel files (WORKSPACE, BUILD.bazel, etc) from the template directory to
* the project `root` directory, and return the absolute paths of the files
* copied, so that they can be deleted later.
* Existing files in `root` will not be replaced.
*/
export function copyBazelFiles(root: string, templateDir: string) {
root = normalize(root);
templateDir = normalize(templateDir);
const bazelFiles: string[] = [];
const templates = listR(templateDir);
for (const template of templates) {
const name = template.replace('__dot__', '.').replace('.template', '');
const source = join(templateDir, template);
const dest = join(root, name);
try {
if (!existsSync(dest)) {
copyFileSync(source, dest);
bazelFiles.push(dest);
}
} catch {
}
}
return bazelFiles;
}
/**
* Delete the specified 'files'. This function never throws.
*/
export function deleteBazelFiles(files: string[]) {
for (const file of files) {
try {
unlinkSync(file);
} catch {
}
}
}