Skip to content

Commit ca344b1

Browse files
committed
fix(@angular/cli): verify Angular/TS version combos
1 parent d1adc8c commit ca344b1

File tree

6 files changed

+98
-5
lines changed

6 files changed

+98
-5
lines changed

packages/@angular/cli/commands/build.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,9 @@ const BuildCommand = Command.extend({
199199
]),
200200

201201
run: function (commandOptions: BuildTaskOptions) {
202-
// Check angular version.
202+
// Check Angular and TypeScript versions.
203203
Version.assertAngularVersionIs2_3_1OrHigher(this.project.root);
204+
Version.assertTypescriptVersion(this.project.root);
204205

205206
// Default vendor chunk to false when build optimizer is on.
206207
if (commandOptions.vendorChunk === undefined) {

packages/@angular/cli/commands/serve.ts

+2
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ const ServeCommand = Command.extend({
126126
run: function (commandOptions: ServeTaskOptions) {
127127
const ServeTask = require('../tasks/serve').default;
128128

129+
// Check Angular and TypeScript versions.
129130
Version.assertAngularVersionIs2_3_1OrHigher(this.project.root);
131+
Version.assertTypescriptVersion(this.project.root);
130132

131133
// Default vendor chunk to false when build optimizer is on.
132134
if (commandOptions.vendorChunk === undefined) {

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

+5
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,11 @@
586586
"description": "Show a warning when the global version is newer than the local one.",
587587
"type": "boolean",
588588
"default": true
589+
},
590+
"typescriptMismatch": {
591+
"description": "Show a warning when the TypeScript version is incompatible",
592+
"type": "boolean",
593+
"default": true
589594
}
590595
}
591596
}

packages/@angular/cli/upgrade/version.ts

+46-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import {SemVer} from 'semver';
1+
import {SemVer, satisfies} from 'semver';
22
import {bold, red, yellow} from 'chalk';
3-
import {stripIndents} from 'common-tags';
3+
import {stripIndents, stripIndent} from 'common-tags';
44
import {readFileSync, existsSync} from 'fs';
55
import * as path from 'path';
66

@@ -143,6 +143,50 @@ export class Version {
143143
}
144144
}
145145

146+
static assertTypescriptVersion(projectRoot: string) {
147+
if (!CliConfig.fromGlobal().get('warnings.typescriptMismatch')) {
148+
return;
149+
}
150+
let compilerVersion: string, tsVersion: string;
151+
try {
152+
compilerVersion = requireProjectModule(projectRoot, '@angular/compiler-cli').VERSION.full;
153+
tsVersion = requireProjectModule(projectRoot, 'typescript').version;
154+
} catch (_) {
155+
console.error(bold(red(stripIndents`
156+
Versions of @angular/compiler-cli and typescript could not be determined.
157+
The most common reason for this is a broken npm install.
158+
159+
Please make sure your package.json contains both @angular/compiler-cli and typescript in
160+
devDependencies, then delete node_modules and package-lock.json (if you have one) and
161+
run npm install again.
162+
`)));
163+
process.exit(2);
164+
}
165+
166+
const versionCombos = [
167+
{ compiler: '>=2.3.1 <3.0.0', typescript: '>=2.0.2 <2.3.0' },
168+
{ compiler: '>=4.0.0 <5.0.0', typescript: '>=2.1.0 <2.4.0' },
169+
{ compiler: '>=5.0.0 <6.0.0', typescript: '>=2.4.0 <2.6.0' }
170+
];
171+
172+
const currentCombo = versionCombos.find((combo) => satisfies(compilerVersion, combo.compiler));
173+
174+
if (currentCombo && !satisfies(tsVersion, currentCombo.typescript)) {
175+
// First line of warning looks weird being split in two, disable tslint for it.
176+
console.log((yellow('\n' + stripIndent`
177+
@angular/compiler-cli@${compilerVersion} requires typescript@'${
178+
currentCombo.typescript}' but ${tsVersion} was found instead.
179+
Using this version can result in undefined behaviour and difficult to debug problems.
180+
181+
Please run the following command to install a compatible version of TypeScript.
182+
183+
npm install typescript@'${currentCombo.typescript}'
184+
185+
To disable this warning run "ng set --global warnings.typescriptMismatch=false".
186+
` + '\n')));
187+
}
188+
}
189+
146190
static isPreWebpack(): boolean {
147191
// CliConfig is a bit stricter with the schema, so we need to be a little looser with it.
148192
const version = Version.fromProject();
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { ng, npm } from '../../utils/process';
2+
import { getGlobalVariable } from '../../utils/env';
3+
4+
5+
export default function () {
6+
// [email protected] is not part of the officially supported range in latest stable.
7+
// Update as needed.
8+
let unsupportedTsVersion = '2.5';
9+
10+
// Skip this in Appveyor tests.
11+
if (getGlobalVariable('argv').nightly) {
12+
unsupportedTsVersion = '2.3';
13+
}
14+
15+
return Promise.resolve()
16+
.then(() => npm('uninstall', 'typescript', '--no-save'))
17+
.then(() => ng('build'))
18+
.catch((err) => {
19+
if (!err.message.match('Versions of @angular/compiler-cli and typescript could not')) {
20+
throw new Error('Expected to have missing dependency error in output.');
21+
}
22+
})
23+
.then(() => npm('install', `typescript@${unsupportedTsVersion}`, '--no-save'))
24+
.then(() => ng('build'))
25+
.then((output) => {
26+
if (!output.stdout.match('Using this version can result in undefined behaviour')) {
27+
throw new Error('Expected to have typescript version mismatch warning in output.');
28+
}
29+
})
30+
.then(() => ng('set', '--global', 'warnings.typescriptMismatch=false'))
31+
.then(() => ng('build'))
32+
.then((output) => {
33+
if (output.stdout.match('Using this version can result in undefined behaviour')) {
34+
throw new Error('Expected to not have typescript version mismatch warning in output.');
35+
}
36+
})
37+
// Cleanup
38+
.then(() => npm('install'))
39+
.then(() => ng('set', '--global', 'warnings.typescriptMismatch=true'));
40+
}
41+

tests/e2e/utils/process.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce
7777
_processes = _processes.filter(p => p !== childProcess);
7878

7979
if (!error) {
80-
resolve({ stdout });
80+
resolve({ stdout, stderr });
8181
} else {
82-
err.message += `${error}...\n\nSTDOUT:\n${stdout}\n`;
82+
err.message += `${error}...\n\nSTDOUT:\n${stdout}\n\nSTDERR:\n${stderr}\n`;
8383
reject(err);
8484
}
8585
});

0 commit comments

Comments
 (0)