refactor(bazel): Fix warning about overridden tsconfig options (#28674)

Under Bazel, some compilerOptions in tsconfig.json are controlled by
downstream rules. The default tsconfig.json causes Bazel to print out
warnings about overriden settings.

This commit makes a backup of the original tsconfig.json and removes
tsconfig settings that are controlled by Bazel.

As part of this fix, JsonAst utils are refactored into separate package
and unit tests are added.

PR closes https://github.com/angular/angular/issues/28034

PR Close #28674
This commit is contained in:
Keen Yee Liau
2019-02-13 15:30:12 +08:00
committed by Misko Hevery
parent 841a1d32e1
commit 65c2deacbb
7 changed files with 297 additions and 18 deletions

View File

@ -8,11 +8,12 @@
* @fileoverview Schematics for ng-new project that builds with Bazel.
*/
import {SchematicContext, apply, applyTemplates, chain, mergeWith, move, Rule, schematic, Tree, url, SchematicsException, UpdateRecorder,} from '@angular-devkit/schematics';
import {parseJsonAst, JsonAstObject, strings, JsonValue} from '@angular-devkit/core';
import {JsonAstObject, parseJsonAst, strings} from '@angular-devkit/core';
import {Rule, SchematicContext, SchematicsException, Tree, apply, applyTemplates, chain, mergeWith, move, schematic, url} from '@angular-devkit/schematics';
import {getWorkspacePath} from '@schematics/angular/utility/config';
import {findPropertyInAstObject, insertPropertyInAstObjectInOrder} from '@schematics/angular/utility/json-utils';
import {validateProjectName} from '@schematics/angular/utility/validation';
import {getWorkspacePath} from '@schematics/angular/utility/config';
import {isJsonAstObject, removeKeyValueInAstObject as removeKeyValueInAstObject, replacePropertyInAstObject} from '../utility/json-utils';
import {Schema} from './schema';
/**
@ -102,21 +103,6 @@ function updateGitignore() {
};
}
function replacePropertyInAstObject(
recorder: UpdateRecorder, node: JsonAstObject, propertyName: string, value: JsonValue,
indent: number) {
const property = findPropertyInAstObject(node, propertyName);
if (property === null) {
throw new Error(`Property ${propertyName} does not exist in JSON object`);
}
const {start, text} = property;
recorder.remove(start.offset, text.length);
const indentStr = '\n' +
' '.repeat(indent);
const content = JSON.stringify(value, null, ' ').replace(/\n/g, indentStr);
recorder.insertLeft(start.offset, content);
}
function updateAngularJsonToUseBazelBuilder(options: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const {name} = options;
@ -216,6 +202,61 @@ function backupAngularJson(): Rule {
};
}
/**
* Create a backup for the original tsconfig.json file in case user wants to
* eject Bazel and revert to the original workflow.
*/
function backupTsconfigJson(): Rule {
return (host: Tree, context: SchematicContext) => {
const tsconfigPath = 'tsconfig.json';
if (!host.exists(tsconfigPath)) {
return;
}
host.create(
`${tsconfigPath}.bak`, '// This is a backup file of the original tsconfig.json. ' +
'This file is needed in case you want to revert to the workflow without Bazel.\n\n' +
host.read(tsconfigPath));
};
}
/**
* Bazel controls the compilation options of tsc, so many options in
* tsconfig.json generated by the default CLI schematics are not applicable.
* This function updates the tsconfig.json to remove Bazel-controlled
* parameters. This prevents Bazel from printing out warnings about overriden
* settings.
*/
function updateTsconfigJson(): Rule {
return (host: Tree, context: SchematicContext) => {
const tsconfigPath = 'tsconfig.json';
if (!host.exists(tsconfigPath)) {
return host;
}
const content = host.read(tsconfigPath).toString();
const ast = parseJsonAst(content);
if (!isJsonAstObject(ast)) {
return host;
}
const compilerOptions = findPropertyInAstObject(ast, 'compilerOptions');
if (!isJsonAstObject(compilerOptions)) {
return host;
}
const recorder = host.beginUpdate(tsconfigPath);
// target and module are controlled by downstream dependencies, such as
// ts_devserver
removeKeyValueInAstObject(recorder, content, compilerOptions, 'target');
removeKeyValueInAstObject(recorder, content, compilerOptions, 'module');
// typeRoots is always set to the @types subdirectory of the node_modules
// attribute
removeKeyValueInAstObject(recorder, content, compilerOptions, 'typeRoots');
// rootDir and baseUrl are always the workspace root directory
removeKeyValueInAstObject(recorder, content, compilerOptions, 'rootDir');
removeKeyValueInAstObject(recorder, content, compilerOptions, 'baseUrl');
host.commitUpdate(recorder);
return host;
};
}
export default function(options: Schema): Rule {
return (host: Tree) => {
validateProjectName(options.name);
@ -225,8 +266,10 @@ export default function(options: Schema): Rule {
addDevAndProdMainForAot(options),
addDevDependenciesToPackageJson(options),
backupAngularJson(),
backupTsconfigJson(),
updateAngularJsonToUseBazelBuilder(options),
updateGitignore(),
updateTsconfigJson(),
]);
};
}