Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ivy): verify whether TypeScript version is supported #33377

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/compiler-cli/src/ngtsc/program.ts
Expand Up @@ -11,6 +11,7 @@ import * as ts from 'typescript';

import * as api from '../transformers/api';
import {nocollapseHack} from '../transformers/nocollapse_hack';
import {verifySupportedTypeScriptVersion} from '../typescript_support';

import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ReferencesRegistry} from './annotations';
import {BaseDefDecoratorHandler} from './annotations/src/base_def';
Expand Down Expand Up @@ -74,6 +75,10 @@ export class NgtscProgram implements api.Program {
constructor(
rootNames: ReadonlyArray<string>, private options: api.CompilerOptions,
private host: api.CompilerHost, oldProgram?: NgtscProgram) {
if (!options.disableTypeScriptVersionCheck) {
verifySupportedTypeScriptVersion();
}

if (shouldEnablePerfTracing(options)) {
this.perfTracker = PerfTracker.zeroedToNow();
this.perfRecorder = this.perfTracker;
Expand Down
46 changes: 4 additions & 42 deletions packages/compiler-cli/src/transformers/program.ts
Expand Up @@ -13,9 +13,9 @@ import * as path from 'path';
import * as ts from 'typescript';

import {TypeCheckHost, translateDiagnostics} from '../diagnostics/translate_diagnostics';
import {compareVersions} from '../diagnostics/typescript_version';
import {MetadataCollector, ModuleMetadata, createBundleIndexHost} from '../metadata';
import {NgtscProgram} from '../ngtsc/program';
import {verifySupportedTypeScriptVersion} from '../typescript_support';

import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, DiagnosticMessageChain, EmitFlags, LazyRoute, LibrarySummary, Program, SOURCE, TsEmitArguments, TsEmitCallback, TsMergeEmitResultsCallback} from './api';
import {CodeGenerator, TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalReferences} from './compiler_host';
Expand Down Expand Up @@ -67,19 +67,6 @@ const defaultEmitCallback: TsEmitCallback =
program.emit(
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);

/**
* Minimum supported TypeScript version
* ∀ supported typescript version v, v >= MIN_TS_VERSION
*/
const MIN_TS_VERSION = '3.6.4';

/**
* Supremum of supported TypeScript versions
* ∀ supported typescript version v, v < MAX_TS_VERSION
* MAX_TS_VERSION is not considered as a supported TypeScript version
*/
const MAX_TS_VERSION = '3.7.0';

class AngularCompilerProgram implements Program {
private rootNames: string[];
private metadataCache: MetadataCache;
Expand Down Expand Up @@ -116,7 +103,9 @@ class AngularCompilerProgram implements Program {
private host: CompilerHost, oldProgram?: Program) {
this.rootNames = [...rootNames];

checkVersion(ts.version, MIN_TS_VERSION, MAX_TS_VERSION, options.disableTypeScriptVersionCheck);
if (!options.disableTypeScriptVersionCheck) {
verifySupportedTypeScriptVersion();
}

this.oldTsProgram = oldProgram ? oldProgram.getTsProgram() : undefined;
if (oldProgram) {
Expand Down Expand Up @@ -864,33 +853,6 @@ class AngularCompilerProgram implements Program {
}
}

/**
* Checks whether a given version ∈ [minVersion, maxVersion[
* An error will be thrown if the following statements are simultaneously true:
* - the given version ∉ [minVersion, maxVersion[,
* - the result of the version check is not meant to be bypassed (the parameter disableVersionCheck
* is false)
*
* @param version The version on which the check will be performed
* @param minVersion The lower bound version. A valid version needs to be greater than minVersion
* @param maxVersion The upper bound version. A valid version needs to be strictly less than
* maxVersion
* @param disableVersionCheck Indicates whether version check should be bypassed
*
* @throws Will throw an error if the following statements are simultaneously true:
* - the given version ∉ [minVersion, maxVersion[,
* - the result of the version check is not meant to be bypassed (the parameter disableVersionCheck
* is false)
*/
export function checkVersion(
version: string, minVersion: string, maxVersion: string,
disableVersionCheck: boolean | undefined) {
if ((compareVersions(version, minVersion) < 0 || compareVersions(version, maxVersion) >= 0) &&
!disableVersionCheck) {
throw new Error(
`The Angular Compiler requires TypeScript >=${minVersion} and <${maxVersion} but ${version} was found instead.`);
}
}

export function createProgram({rootNames, options, host, oldProgram}: {
rootNames: ReadonlyArray<string>,
Expand Down
59 changes: 59 additions & 0 deletions packages/compiler-cli/src/typescript_support.ts
@@ -0,0 +1,59 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {compareVersions} from './diagnostics/typescript_version';

/**
* Minimum supported TypeScript version
* ∀ supported typescript version v, v >= MIN_TS_VERSION
*/
const MIN_TS_VERSION = '3.6.4';

/**
* Supremum of supported TypeScript versions
* ∀ supported typescript version v, v < MAX_TS_VERSION
* MAX_TS_VERSION is not considered as a supported TypeScript version
*/
const MAX_TS_VERSION = '3.7.0';

/**
* The currently used version of TypeScript, which can be adjusted for testing purposes using
* `setTypeScriptVersionForTesting` and `restoreTypeScriptVersionForTesting` below.
*/
let tsVersion = ts.version;

export function setTypeScriptVersionForTesting(version: string): void {
tsVersion = version;
}

export function restoreTypeScriptVersionForTesting(): void {
tsVersion = ts.version;
}

/**
* Checks whether a given version ∈ [minVersion, maxVersion[
* An error will be thrown if the following statements are simultaneously true:
* - the given version ∉ [minVersion, maxVersion[,
*
* @param version The version on which the check will be performed
* @param minVersion The lower bound version. A valid version needs to be greater than minVersion
* @param maxVersion The upper bound version. A valid version needs to be strictly less than
* maxVersion
*
* @throws Will throw an error if the given version ∉ [minVersion, maxVersion[
*/
export function checkVersion(version: string, minVersion: string, maxVersion: string) {
if ((compareVersions(version, minVersion) < 0 || compareVersions(version, maxVersion) >= 0)) {
throw new Error(
`The Angular Compiler requires TypeScript >=${minVersion} and <${maxVersion} but ${version} was found instead.`);
}
}

export function verifySupportedTypeScriptVersion(): void {
checkVersion(tsVersion, MIN_TS_VERSION, MAX_TS_VERSION);
}
43 changes: 43 additions & 0 deletions packages/compiler-cli/test/ngtsc/ngtsc_spec.ts
Expand Up @@ -13,6 +13,7 @@ import {ErrorCode, ngErrorCode} from '../../src/ngtsc/diagnostics';
import {absoluteFrom} from '../../src/ngtsc/file_system';
import {runInEachFileSystem} from '../../src/ngtsc/file_system/testing';
import {LazyRoute} from '../../src/ngtsc/routing';
import {restoreTypeScriptVersionForTesting, setTypeScriptVersionForTesting} from '../../src/typescript_support';
import {loadStandardTestFiles} from '../helpers/src/mock_file_loading';

import {NgtscTestEnvironment} from './env';
Expand Down Expand Up @@ -4722,6 +4723,48 @@ export const Foo = Foo__PRE_R3__;
});
});

describe('disableTypeScriptVersionCheck', () => {
afterEach(() => restoreTypeScriptVersionForTesting());

it('produces an error when not supported and version check is enabled', () => {
setTypeScriptVersionForTesting('3.4.0');
env.tsconfig({disableTypeScriptVersionCheck: false});
env.write('empty.ts', '');

const diags = env.driveDiagnostics();
expect(diags.length).toBe(1);
expect(diags[0].messageText).toContain('but 3.4.0 was found instead');
});

it('does not produce an error when supported and version check is enabled', () => {
env.tsconfig({disableTypeScriptVersionCheck: false});
env.write('empty.ts', '');

// The TypeScript version is not overwritten, so the version
// that is actually used should be supported
const diags = env.driveDiagnostics();
expect(diags.length).toBe(0);
});

it('does not produce an error when not supported but version check is disabled', () => {
setTypeScriptVersionForTesting('3.4.0');
env.tsconfig({disableTypeScriptVersionCheck: true});
env.write('empty.ts', '');

const diags = env.driveDiagnostics();
expect(diags.length).toBe(0);
});

it('produces an error when not supported using default configuration', () => {
setTypeScriptVersionForTesting('3.4.0');
env.write('empty.ts', '');

const diags = env.driveDiagnostics();
expect(diags.length).toBe(1);
expect(diags[0].messageText).toContain('but 3.4.0 was found instead');
});
});

describe('inline resources', () => {
it('should process inline <style> tags', () => {
env.write('test.ts', `
Expand Down
32 changes: 1 addition & 31 deletions packages/compiler-cli/test/transformers/program_spec.ts
Expand Up @@ -12,7 +12,7 @@ import * as path from 'path';
import * as ts from 'typescript';
import {formatDiagnostics} from '../../src/perform_compile';
import {CompilerHost, EmitFlags, LazyRoute} from '../../src/transformers/api';
import {checkVersion, createSrcToOutPathMapper} from '../../src/transformers/program';
import {createSrcToOutPathMapper} from '../../src/transformers/program';
import {StructureIsReused, tsStructureIsReused} from '../../src/transformers/util';
import {TestSupport, expectNoDiagnosticsInProgram, setup} from '../test_support';

Expand Down Expand Up @@ -1116,34 +1116,4 @@ describe('ng program', () => {
});
});

describe('checkVersion', () => {
const MIN_TS_VERSION = '2.7.2';
const MAX_TS_VERSION = '2.8.0';

const versionError = (version: string) =>
`The Angular Compiler requires TypeScript >=${MIN_TS_VERSION} and <${MAX_TS_VERSION} but ${version} was found instead.`;

it('should not throw when a supported TypeScript version is used', () => {
expect(() => checkVersion('2.7.2', MIN_TS_VERSION, MAX_TS_VERSION, undefined)).not.toThrow();
expect(() => checkVersion('2.7.2', MIN_TS_VERSION, MAX_TS_VERSION, false)).not.toThrow();
expect(() => checkVersion('2.7.2', MIN_TS_VERSION, MAX_TS_VERSION, true)).not.toThrow();
});

it('should handle a TypeScript version < the minimum supported one', () => {
expect(() => checkVersion('2.4.1', MIN_TS_VERSION, MAX_TS_VERSION, undefined))
.toThrowError(versionError('2.4.1'));
expect(() => checkVersion('2.4.1', MIN_TS_VERSION, MAX_TS_VERSION, false))
.toThrowError(versionError('2.4.1'));
expect(() => checkVersion('2.4.1', MIN_TS_VERSION, MAX_TS_VERSION, true)).not.toThrow();
});

it('should handle a TypeScript version > the maximum supported one', () => {
expect(() => checkVersion('2.9.0', MIN_TS_VERSION, MAX_TS_VERSION, undefined))
.toThrowError(versionError('2.9.0'));
expect(() => checkVersion('2.9.0', MIN_TS_VERSION, MAX_TS_VERSION, false))
.toThrowError(versionError('2.9.0'));
expect(() => checkVersion('2.9.0', MIN_TS_VERSION, MAX_TS_VERSION, true)).not.toThrow();
});
});

});
35 changes: 35 additions & 0 deletions packages/compiler-cli/test/typescript_support_spec.ts
@@ -0,0 +1,35 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {checkVersion} from '../src/typescript_support';

describe('checkVersion', () => {
const MIN_TS_VERSION = '2.7.2';
const MAX_TS_VERSION = '2.8.0';

const versionError = (version: string) =>
`The Angular Compiler requires TypeScript >=${MIN_TS_VERSION} and <${MAX_TS_VERSION} but ${version} was found instead.`;

it('should not throw when a supported TypeScript version is used', () => {
expect(() => checkVersion('2.7.2', MIN_TS_VERSION, MAX_TS_VERSION)).not.toThrow();
expect(() => checkVersion('2.7.9', MIN_TS_VERSION, MAX_TS_VERSION)).not.toThrow();
});

it('should handle a TypeScript version < the minimum supported one', () => {
expect(() => checkVersion('2.4.1', MIN_TS_VERSION, MAX_TS_VERSION))
.toThrowError(versionError('2.4.1'));
expect(() => checkVersion('2.7.1', MIN_TS_VERSION, MAX_TS_VERSION))
.toThrowError(versionError('2.7.1'));
});

it('should handle a TypeScript version > the maximum supported one', () => {
expect(() => checkVersion('2.9.0', MIN_TS_VERSION, MAX_TS_VERSION))
.toThrowError(versionError('2.9.0'));
expect(() => checkVersion('2.8.0', MIN_TS_VERSION, MAX_TS_VERSION))
.toThrowError(versionError('2.8.0'));
});
});