Skip to content

Commit d7e5bbf

Browse files
alan-agius4kara
authored andcommitted
feat(compiler-cli): add support to extend angularCompilerOptions (angular#22717)
`TypeScript` only supports merging and extending of `compilerOptions`. This is an implementation to support extending and inheriting of `angularCompilerOptions` from multiple files. Closes: angular#22684 PR Close angular#22717
1 parent a9a81f9 commit d7e5bbf

File tree

4 files changed

+138
-1
lines changed

4 files changed

+138
-1
lines changed

aio/content/guide/aot-compiler.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,28 @@ Chuck: After reviewing your PR comment I'm still at a loss. See [comment there](
13081308
}
13091309
```
13101310
1311+
{@a tsconfig-extends}
1312+
## Configuration inheritance with extends
1313+
Similar to TypeScript Compiler, Angular Compiler also supports `extends` in the `tsconfig.json` on `angularCompilerOptions`. A tsconfig file can inherit configurations from another file using the `extends` property.
1314+
The `extends` is a top level property parallel to `compilerOptions` and `angularCompilerOptions`.
1315+
The configuration from the base file are loaded first, then overridden by those in the inheriting config file.
1316+
Example:
1317+
```json
1318+
{
1319+
"extends": "../tsconfig.base.json",
1320+
"compilerOptions": {
1321+
"experimentalDecorators": true,
1322+
...
1323+
},
1324+
"angularCompilerOptions": {
1325+
"fullTemplateTypeCheck": true,
1326+
"preserveWhitespaces": true,
1327+
...
1328+
}
1329+
}
1330+
```
1331+
More information about tsconfig extends can be found in the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
1332+
13111333
{@a compiler-options}
13121334
## Angular template compiler options
13131335

packages/compiler-cli/src/perform_compile.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,37 @@ export function readConfiguration(
128128
try {
129129
const {projectFile, basePath} = calcProjectFileAndBasePath(project);
130130

131-
let {config, error} = ts.readConfigFile(projectFile, ts.sys.readFile);
131+
const readExtendedConfigFile =
132+
(configFile: string, existingConfig?: any): {config?: any, error?: ts.Diagnostic} => {
133+
const {config, error} = ts.readConfigFile(configFile, ts.sys.readFile);
134+
135+
if (error) {
136+
return {error};
137+
}
138+
139+
// we are only interested into merging 'angularCompilerOptions' as
140+
// other options like 'compilerOptions' are merged by TS
141+
const baseConfig = existingConfig || config;
142+
if (existingConfig) {
143+
baseConfig.angularCompilerOptions = {...config.angularCompilerOptions,
144+
...baseConfig.angularCompilerOptions};
145+
}
146+
147+
if (config.extends) {
148+
let extendedConfigPath = path.resolve(path.dirname(configFile), config.extends);
149+
extendedConfigPath = path.extname(extendedConfigPath) ? extendedConfigPath :
150+
`${extendedConfigPath}.json`;
151+
152+
if (fs.existsSync(extendedConfigPath)) {
153+
// Call read config recursively as TypeScript only merges CompilerOptions
154+
return readExtendedConfigFile(extendedConfigPath, baseConfig);
155+
}
156+
}
157+
158+
return {config: baseConfig};
159+
};
160+
161+
const {config, error} = readExtendedConfigFile(projectFile);
132162

133163
if (error) {
134164
return {

packages/compiler-cli/test/BUILD.bazel

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,30 @@ jasmine_node_test(
133133
"//tools/testing:node",
134134
],
135135
)
136+
137+
# perform_compile_spec
138+
ts_library(
139+
name = "perform_compile_lib",
140+
testonly = 1,
141+
srcs = [
142+
"perform_compile_spec.ts",
143+
],
144+
deps = [
145+
":test_utils",
146+
"//packages/compiler",
147+
"//packages/compiler-cli",
148+
],
149+
)
150+
151+
jasmine_node_test(
152+
name = "perform_compile",
153+
bootstrap = ["angular/tools/testing/init_node_spec.js"],
154+
data = [
155+
"//packages/core:npm_package",
156+
],
157+
deps = [
158+
":perform_compile_lib",
159+
"//packages/core",
160+
"//tools/testing:node",
161+
],
162+
)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import * as path from 'path';
10+
11+
import {readConfiguration} from '../src/perform_compile';
12+
13+
import {TestSupport, setup} from './test_support';
14+
15+
describe('perform_compile', () => {
16+
let support: TestSupport;
17+
let basePath: string;
18+
19+
beforeEach(() => {
20+
support = setup();
21+
basePath = support.basePath;
22+
});
23+
24+
function writeSomeConfigs() {
25+
support.writeFiles({
26+
'tsconfig-level-1.json': `{
27+
"extends": "./tsconfig-level-2.json",
28+
"angularCompilerOptions": {
29+
"annotateForClosureCompiler": true
30+
}
31+
}
32+
`,
33+
'tsconfig-level-2.json': `{
34+
"extends": "./tsconfig-level-3.json",
35+
"angularCompilerOptions": {
36+
"skipMetadataEmit": true
37+
}
38+
}
39+
`,
40+
'tsconfig-level-3.json': `{
41+
"angularCompilerOptions": {
42+
"annotateForClosureCompiler": false,
43+
"annotationsAs": "decorators"
44+
}
45+
}
46+
`,
47+
});
48+
}
49+
50+
it('should merge tsconfig "angularCompilerOptions"', () => {
51+
writeSomeConfigs();
52+
const {options} = readConfiguration(path.resolve(basePath, 'tsconfig-level-1.json'));
53+
expect(options.annotateForClosureCompiler).toBe(true);
54+
expect(options.annotationsAs).toBe('decorators');
55+
expect(options.skipMetadataEmit).toBe(true);
56+
});
57+
58+
});

0 commit comments

Comments
 (0)