Skip to content

Commit

Permalink
feat(@angular/cli): use separate tsconfigs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
filipesilva committed Feb 22, 2017
1 parent 1e30159 commit 69e6c71
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 90 deletions.
12 changes: 12 additions & 0 deletions docs/documentation/stories/third-party-lib.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ npm install d3 --save
npm install @types/d3 --save-dev
```

Then open `src/tsconfig.app.json` and add it to the `types` array:

```
"types":[
"d3"
]
```

If the library you added typings for is only to be used on your e2e tests,
instead use `e2e/tsconfig.e2e.json`.
The same goes for unit tests and `src/tsconfig.spec.json`.

If the library doesn't have typings available at `@types/`, you can still use it by
manually adding typings for it:

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{<% if (ng4) { %>
"extends": "<%= relativeRootPath %>/tsconfig.json",
"compilerOptions": {
"lib": [
"es2016",
"dom"
],<% } else { %>
"compilerOptions": {
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [
"es2016",
"dom"
],<% } %>
"outDir": "<%= relativeRootPath %>/out-tsc/app",
"target": "es5",
"module": "es2015",
"baseUrl": "",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
21 changes: 0 additions & 21 deletions packages/@angular/cli/blueprints/ng2/files/__path__/tsconfig.json

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{<% if (ng4) { %>
"extends": "<%= relativeRootPath %>/tsconfig.json",
"compilerOptions": {<% } else { %>
"compilerOptions": {
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [
"es2016"
],<% } %>
"outDir": "<%= relativeRootPath %>/out-tsc/spec",
"module": "commonjs",
"target": "es6",

This comment has been minimized.

Copy link
@eddy-geek

eddy-geek Mar 1, 2017

Why es6 here? it breaks e.g. phantomJS

This comment has been minimized.

Copy link
@filipesilva

filipesilva Mar 1, 2017

Author Contributor

It's what we had before with the single config. You can take it out if you want.

"baseUrl": "",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts"
],
"include": [
"**/*.spec.ts"
]
}
12 changes: 7 additions & 5 deletions packages/@angular/cli/blueprints/ng2/files/angular-cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.json",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "<%= prefix %>",
"styles": [
"styles.<%= styleExt %>"
Expand All @@ -35,12 +36,13 @@
},
"lint": [
{
"files": "<%= sourceDir %>/**/*.ts",
"project": "<%= sourceDir %>/tsconfig.json"
"project": "<%= sourceDir %>/tsconfig.app.json"
},
{
"files": "e2e/**/*.ts",
"project": "e2e/tsconfig.json"
"project": "<%= sourceDir %>/tsconfig.spec.json"
},
{
"project": "e2e/tsconfig.e2e.json"
}
],
"test": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
{
"compileOnSave": false,
{<% if (ng4) { %>
"extends": "../tsconfig.json",
"compilerOptions": {<% } else { %>
"compilerOptions": {
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [
"es2016"
],
"module": "commonjs",
"moduleResolution": "node",
],<% } %>
"outDir": "../dist/out-tsc-e2e",
"sourceMap": true,
"module": "commonjs",
"target": "es6",
"typeRoots": [
"../node_modules/@types"
"types":[
"jasmine",
"node"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ exports.config = {
},
beforeLaunch: function() {
require('ts-node').register({
project: 'e2e'
project: 'e2e/tsconfig.e2e.json'
});
},
onPrepare() {
Expand Down
14 changes: 14 additions & 0 deletions packages/@angular/cli/blueprints/ng2/files/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [
"es2016"
]
}
}
6 changes: 5 additions & 1 deletion packages/@angular/cli/lib/config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,13 @@
},
"tsconfig": {
"type": "string",
"default": "tsconfig.json",
"default": "tsconfig.app.json",
"description": "The name of the TypeScript configuration file."
},
"testTsconfig": {
"type": "string",
"description": "The name of the TypeScript configuration file for unit tests."
},
"prefix": {
"type": "string",
"description": "The prefix to apply to generated selectors."
Expand Down
1 change: 1 addition & 0 deletions packages/@angular/cli/models/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export class NgCliWebpackConfig {

public addAppConfigDefaults(appConfig: any) {
const appConfigDefaults: any = {
testTsconfig: appConfig.tsconfig,
scripts: [],
styles: []
};
Expand Down
86 changes: 34 additions & 52 deletions packages/@angular/cli/models/webpack-configs/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,79 +64,61 @@ function _createAotPlugin(wco: WebpackConfigOptions, options: any) {
}

return new AotPlugin(Object.assign({}, {
tsConfigPath: path.resolve(projectRoot, appConfig.root, appConfig.tsconfig),
mainPath: path.join(projectRoot, appConfig.root, appConfig.main),
i18nFile: buildOptions.i18nFile,
i18nFormat: buildOptions.i18nFormat,
locale: buildOptions.locale,
hostReplacementPaths
hostReplacementPaths,
// If we don't explicitely list excludes, it will default to `['**/*.spec.ts']`.
exclude: []
}, options));
}


export const getNonAotConfig = function(wco: WebpackConfigOptions) {
const { projectRoot, appConfig } = wco;
let exclude = [ '**/*.spec.ts' ];
if (appConfig.test) {
exclude.push(path.join(projectRoot, appConfig.root, appConfig.test));
}
const { appConfig, projectRoot } = wco;
const tsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);

return {
module: {
rules: [
{
test: /\.ts$/,
loader: webpackLoader,
exclude: [/\.(spec|e2e)\.ts$/]
}
]
},
plugins: [
_createAotPlugin(wco, { exclude, skipCodeGeneration: true }),
]
module: { rules: [{ test: /\.ts$/, loader: webpackLoader }] },
plugins: [ _createAotPlugin(wco, { tsConfigPath, skipCodeGeneration: true }) ]
};
};

export const getAotConfig = function(wco: WebpackConfigOptions) {
const { projectRoot, appConfig } = wco;
let exclude = [ '**/*.spec.ts' ];
if (appConfig.test) { exclude.push(path.join(projectRoot, appConfig.root, appConfig.test)); };
const tsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);
const testTsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.testTsconfig);

let pluginOptions: any = { tsConfigPath };

// Fallback to exclude spec files from AoT compilation on projects using a shared tsconfig.
if (testTsConfigPath === tsConfigPath) {
let exclude = [ '**/*.spec.ts' ];
if (appConfig.test) { exclude.push(path.join(projectRoot, appConfig.root, appConfig.test)); };
pluginOptions.exclude = exclude;
}

return {
module: {
rules: [
{
test: /\.ts$/,
loader: webpackLoader,
exclude: [/\.(spec|e2e)\.ts$/]
}
]
},
plugins: [
_createAotPlugin(wco, { exclude })
]
module: { rules: [{ test: /\.ts$/, loader: webpackLoader }] },
plugins: [ _createAotPlugin(wco, pluginOptions) ]
};
};

export const getNonAotTestConfig = function(wco: WebpackConfigOptions) {
const { projectRoot, appConfig } = wco;
const tsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.testTsconfig);
const appTsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);

let pluginOptions: any = { tsConfigPath, skipCodeGeneration: true };

// Fallback to correct module format on projects using a shared tsconfig.
if (tsConfigPath === appTsConfigPath) {
pluginOptions.compilerOptions = { module: 'commonjs' };
}

return {
module: {
rules: [
{
test: /\.ts$/,
loader: webpackLoader,
query: { module: 'commonjs' },
exclude: [/\.(e2e)\.ts$/]
}
]
},
plugins: [
_createAotPlugin(wco, {
exclude: [],
skipCodeGeneration: true,
compilerOptions: {
module: 'commonjs'
}
}),
]
module: { rules: [{ test: /\.ts$/, loader: webpackLoader }] },
plugins: [ _createAotPlugin(wco, pluginOptions) ]
};
};
29 changes: 29 additions & 0 deletions tests/e2e/tests/build/aot/exclude.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ng } from '../../../utils/process';
import { writeFile, moveFile } from '../../../utils/fs';
import { updateJsonFile } from '../../../utils/project';
import { getGlobalVariable } from '../../../utils/env';

export default function () {
// Disable parts of it in webpack tests.
const ejected = getGlobalVariable('argv').eject;

// Check if **/*.spec.ts files are excluded by default.
return Promise.resolve()
// This import would cause aot to fail.
.then(() => writeFile('src/another.component.spec.ts', `
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
`))
.then(() => ng('build', '--aot'))
// Verify backwards compatibility with old project using the shared tsconfig.
.then(() => moveFile('src/tsconfig.app.json', 'src/tsconfig.json'))
.then(() => updateJsonFile('.angular-cli.json', configJson => {
const app = configJson['apps'][0];
app.tsconfig = 'tsconfig.json';
delete app['testTsconfig'];
}))
.then(() => updateJsonFile('src/tsconfig.json', tsconfigJson => {
delete tsconfigJson['exclude'];
}))
.then(() => ng('build', '--aot'))
.then(() => !ejected && ng('test', '--single-run'));
}
2 changes: 1 addition & 1 deletion tests/e2e/tests/misc/different-file-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const options = {

export default function() {
return Promise.resolve()
.then(() => fs.prependToFile('./src/tsconfig.json', '\ufeff', options))
.then(() => fs.prependToFile('./src/tsconfig.app.json', '\ufeff', options))
.then(() => fs.prependToFile('./.angular-cli.json', '\ufeff', options))
.then(() => ng('build', '--env=dev'));
}
2 changes: 1 addition & 1 deletion tests/e2e/utils/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {getGlobalVariable} from './env';
const packages = require('../../../lib/packages');


const tsConfigPath = 'src/tsconfig.json';
const tsConfigPath = 'src/tsconfig.app.json';


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

0 comments on commit 69e6c71

Please sign in to comment.