Skip to content

Commit 69e6c71

Browse files
committed
feat(@angular/cli): use separate tsconfigs
This PR adds tsconfigs for each separate application: - `src/tsconfig.app.json`: configuration for the Angular app. - `src/tsconfig.spec.json`: configuration for the unit tests. Defaults to the Angular app config. - `e2e/tsconfig.e2e.json`: configuration for the e2e tests. There is an additional root-level `tsconfig.json` that is used for editor integration. For Angular version 4 projects, these tsconfigs will use inheritance since it's available with TypeScript 2.1. This is not a breaking change. Existing projects should not be affected.
1 parent 1e30159 commit 69e6c71

File tree

14 files changed

+171
-90
lines changed

14 files changed

+171
-90
lines changed

docs/documentation/stories/third-party-lib.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ npm install d3 --save
99
npm install @types/d3 --save-dev
1010
```
1111

12+
Then open `src/tsconfig.app.json` and add it to the `types` array:
13+
14+
```
15+
"types":[
16+
"d3"
17+
]
18+
```
19+
20+
If the library you added typings for is only to be used on your e2e tests,
21+
instead use `e2e/tsconfig.e2e.json`.
22+
The same goes for unit tests and `src/tsconfig.spec.json`.
23+
1224
If the library doesn't have typings available at `@types/`, you can still use it by
1325
manually adding typings for it:
1426

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{<% if (ng4) { %>
2+
"extends": "<%= relativeRootPath %>/tsconfig.json",
3+
"compilerOptions": {
4+
"lib": [
5+
"es2016",
6+
"dom"
7+
],<% } else { %>
8+
"compilerOptions": {
9+
"sourceMap": true,
10+
"declaration": false,
11+
"moduleResolution": "node",
12+
"emitDecoratorMetadata": true,
13+
"experimentalDecorators": true,
14+
"lib": [
15+
"es2016",
16+
"dom"
17+
],<% } %>
18+
"outDir": "<%= relativeRootPath %>/out-tsc/app",
19+
"target": "es5",
20+
"module": "es2015",
21+
"baseUrl": "",
22+
"types": []
23+
},
24+
"exclude": [
25+
"test.ts",
26+
"**/*.spec.ts"
27+
]
28+
}

packages/@angular/cli/blueprints/ng2/files/__path__/tsconfig.json

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{<% if (ng4) { %>
2+
"extends": "<%= relativeRootPath %>/tsconfig.json",
3+
"compilerOptions": {<% } else { %>
4+
"compilerOptions": {
5+
"sourceMap": true,
6+
"declaration": false,
7+
"moduleResolution": "node",
8+
"emitDecoratorMetadata": true,
9+
"experimentalDecorators": true,
10+
"lib": [
11+
"es2016"
12+
],<% } %>
13+
"outDir": "<%= relativeRootPath %>/out-tsc/spec",
14+
"module": "commonjs",
15+
"target": "es6",
16+
"baseUrl": "",
17+
"types": [
18+
"jasmine",
19+
"node"
20+
]
21+
},
22+
"files": [
23+
"test.ts"
24+
],
25+
"include": [
26+
"**/*.spec.ts"
27+
]
28+
}

packages/@angular/cli/blueprints/ng2/files/angular-cli.json

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"main": "main.ts",
1616
"polyfills": "polyfills.ts",
1717
"test": "test.ts",
18-
"tsconfig": "tsconfig.json",
18+
"tsconfig": "tsconfig.app.json",
19+
"testTsconfig": "tsconfig.spec.json",
1920
"prefix": "<%= prefix %>",
2021
"styles": [
2122
"styles.<%= styleExt %>"
@@ -35,12 +36,13 @@
3536
},
3637
"lint": [
3738
{
38-
"files": "<%= sourceDir %>/**/*.ts",
39-
"project": "<%= sourceDir %>/tsconfig.json"
39+
"project": "<%= sourceDir %>/tsconfig.app.json"
4040
},
4141
{
42-
"files": "e2e/**/*.ts",
43-
"project": "e2e/tsconfig.json"
42+
"project": "<%= sourceDir %>/tsconfig.spec.json"
43+
},
44+
{
45+
"project": "e2e/tsconfig.e2e.json"
4446
}
4547
],
4648
"test": {
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
{
2-
"compileOnSave": false,
1+
{<% if (ng4) { %>
2+
"extends": "../tsconfig.json",
3+
"compilerOptions": {<% } else { %>
34
"compilerOptions": {
5+
"sourceMap": true,
46
"declaration": false,
7+
"moduleResolution": "node",
58
"emitDecoratorMetadata": true,
69
"experimentalDecorators": true,
710
"lib": [
811
"es2016"
9-
],
10-
"module": "commonjs",
11-
"moduleResolution": "node",
12+
],<% } %>
1213
"outDir": "../dist/out-tsc-e2e",
13-
"sourceMap": true,
14+
"module": "commonjs",
1415
"target": "es6",
15-
"typeRoots": [
16-
"../node_modules/@types"
16+
"types":[
17+
"jasmine",
18+
"node"
1719
]
1820
}
1921
}

packages/@angular/cli/blueprints/ng2/files/protractor.conf.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ exports.config = {
2222
},
2323
beforeLaunch: function() {
2424
require('ts-node').register({
25-
project: 'e2e'
25+
project: 'e2e/tsconfig.e2e.json'
2626
});
2727
},
2828
onPrepare() {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"compileOnSave": false,
3+
"compilerOptions": {
4+
"outDir": "./dist/out-tsc",
5+
"sourceMap": true,
6+
"declaration": false,
7+
"moduleResolution": "node",
8+
"emitDecoratorMetadata": true,
9+
"experimentalDecorators": true,
10+
"lib": [
11+
"es2016"
12+
]
13+
}
14+
}

packages/@angular/cli/lib/config/schema.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,13 @@
9494
},
9595
"tsconfig": {
9696
"type": "string",
97-
"default": "tsconfig.json",
97+
"default": "tsconfig.app.json",
9898
"description": "The name of the TypeScript configuration file."
9999
},
100+
"testTsconfig": {
101+
"type": "string",
102+
"description": "The name of the TypeScript configuration file for unit tests."
103+
},
100104
"prefix": {
101105
"type": "string",
102106
"description": "The prefix to apply to generated selectors."

packages/@angular/cli/models/webpack-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export class NgCliWebpackConfig {
106106

107107
public addAppConfigDefaults(appConfig: any) {
108108
const appConfigDefaults: any = {
109+
testTsconfig: appConfig.tsconfig,
109110
scripts: [],
110111
styles: []
111112
};

packages/@angular/cli/models/webpack-configs/typescript.ts

Lines changed: 34 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -64,79 +64,61 @@ function _createAotPlugin(wco: WebpackConfigOptions, options: any) {
6464
}
6565

6666
return new AotPlugin(Object.assign({}, {
67-
tsConfigPath: path.resolve(projectRoot, appConfig.root, appConfig.tsconfig),
6867
mainPath: path.join(projectRoot, appConfig.root, appConfig.main),
6968
i18nFile: buildOptions.i18nFile,
7069
i18nFormat: buildOptions.i18nFormat,
7170
locale: buildOptions.locale,
72-
hostReplacementPaths
71+
hostReplacementPaths,
72+
// If we don't explicitely list excludes, it will default to `['**/*.spec.ts']`.
73+
exclude: []
7374
}, options));
7475
}
7576

7677

7778
export const getNonAotConfig = function(wco: WebpackConfigOptions) {
78-
const { projectRoot, appConfig } = wco;
79-
let exclude = [ '**/*.spec.ts' ];
80-
if (appConfig.test) {
81-
exclude.push(path.join(projectRoot, appConfig.root, appConfig.test));
82-
}
79+
const { appConfig, projectRoot } = wco;
80+
const tsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);
8381

8482
return {
85-
module: {
86-
rules: [
87-
{
88-
test: /\.ts$/,
89-
loader: webpackLoader,
90-
exclude: [/\.(spec|e2e)\.ts$/]
91-
}
92-
]
93-
},
94-
plugins: [
95-
_createAotPlugin(wco, { exclude, skipCodeGeneration: true }),
96-
]
83+
module: { rules: [{ test: /\.ts$/, loader: webpackLoader }] },
84+
plugins: [ _createAotPlugin(wco, { tsConfigPath, skipCodeGeneration: true }) ]
9785
};
9886
};
9987

10088
export const getAotConfig = function(wco: WebpackConfigOptions) {
10189
const { projectRoot, appConfig } = wco;
102-
let exclude = [ '**/*.spec.ts' ];
103-
if (appConfig.test) { exclude.push(path.join(projectRoot, appConfig.root, appConfig.test)); };
90+
const tsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);
91+
const testTsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.testTsconfig);
92+
93+
let pluginOptions: any = { tsConfigPath };
94+
95+
// Fallback to exclude spec files from AoT compilation on projects using a shared tsconfig.
96+
if (testTsConfigPath === tsConfigPath) {
97+
let exclude = [ '**/*.spec.ts' ];
98+
if (appConfig.test) { exclude.push(path.join(projectRoot, appConfig.root, appConfig.test)); };
99+
pluginOptions.exclude = exclude;
100+
}
101+
104102
return {
105-
module: {
106-
rules: [
107-
{
108-
test: /\.ts$/,
109-
loader: webpackLoader,
110-
exclude: [/\.(spec|e2e)\.ts$/]
111-
}
112-
]
113-
},
114-
plugins: [
115-
_createAotPlugin(wco, { exclude })
116-
]
103+
module: { rules: [{ test: /\.ts$/, loader: webpackLoader }] },
104+
plugins: [ _createAotPlugin(wco, pluginOptions) ]
117105
};
118106
};
119107

120108
export const getNonAotTestConfig = function(wco: WebpackConfigOptions) {
109+
const { projectRoot, appConfig } = wco;
110+
const tsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.testTsconfig);
111+
const appTsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);
112+
113+
let pluginOptions: any = { tsConfigPath, skipCodeGeneration: true };
114+
115+
// Fallback to correct module format on projects using a shared tsconfig.
116+
if (tsConfigPath === appTsConfigPath) {
117+
pluginOptions.compilerOptions = { module: 'commonjs' };
118+
}
119+
121120
return {
122-
module: {
123-
rules: [
124-
{
125-
test: /\.ts$/,
126-
loader: webpackLoader,
127-
query: { module: 'commonjs' },
128-
exclude: [/\.(e2e)\.ts$/]
129-
}
130-
]
131-
},
132-
plugins: [
133-
_createAotPlugin(wco, {
134-
exclude: [],
135-
skipCodeGeneration: true,
136-
compilerOptions: {
137-
module: 'commonjs'
138-
}
139-
}),
140-
]
121+
module: { rules: [{ test: /\.ts$/, loader: webpackLoader }] },
122+
plugins: [ _createAotPlugin(wco, pluginOptions) ]
141123
};
142124
};

tests/e2e/tests/build/aot/exclude.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ng } from '../../../utils/process';
2+
import { writeFile, moveFile } from '../../../utils/fs';
3+
import { updateJsonFile } from '../../../utils/project';
4+
import { getGlobalVariable } from '../../../utils/env';
5+
6+
export default function () {
7+
// Disable parts of it in webpack tests.
8+
const ejected = getGlobalVariable('argv').eject;
9+
10+
// Check if **/*.spec.ts files are excluded by default.
11+
return Promise.resolve()
12+
// This import would cause aot to fail.
13+
.then(() => writeFile('src/another.component.spec.ts', `
14+
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
15+
`))
16+
.then(() => ng('build', '--aot'))
17+
// Verify backwards compatibility with old project using the shared tsconfig.
18+
.then(() => moveFile('src/tsconfig.app.json', 'src/tsconfig.json'))
19+
.then(() => updateJsonFile('.angular-cli.json', configJson => {
20+
const app = configJson['apps'][0];
21+
app.tsconfig = 'tsconfig.json';
22+
delete app['testTsconfig'];
23+
}))
24+
.then(() => updateJsonFile('src/tsconfig.json', tsconfigJson => {
25+
delete tsconfigJson['exclude'];
26+
}))
27+
.then(() => ng('build', '--aot'))
28+
.then(() => !ejected && ng('test', '--single-run'));
29+
}

tests/e2e/tests/misc/different-file-format.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const options = {
99

1010
export default function() {
1111
return Promise.resolve()
12-
.then(() => fs.prependToFile('./src/tsconfig.json', '\ufeff', options))
12+
.then(() => fs.prependToFile('./src/tsconfig.app.json', '\ufeff', options))
1313
.then(() => fs.prependToFile('./.angular-cli.json', '\ufeff', options))
1414
.then(() => ng('build', '--env=dev'));
1515
}

tests/e2e/utils/project.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {getGlobalVariable} from './env';
55
const packages = require('../../../lib/packages');
66

77

8-
const tsConfigPath = 'src/tsconfig.json';
8+
const tsConfigPath = 'src/tsconfig.app.json';
99

1010

1111
export function updateJsonFile(filePath: string, fn: (json: any) => any | void) {

0 commit comments

Comments
 (0)