feat(compiler-cli): make enableIvy ngtsc/true equivalent (#28616)
Currently setting `enableIvy` to true runs a hybrid mode of `ngc` and `ngtsc`. This is counterintuitive given the name of the flag itself. This PR makes the `true` value equivalent to the previous `ngtsc`, and `ngtsc` becomes an alias for `true`. Effectively this removes the hybrid mode as well since there's no other way to enable it. PR Close #28616
This commit is contained in:
parent
a17fd43fd5
commit
1923c2f99c
@ -76,7 +76,7 @@ def _enable_ivy_value(ctx):
|
|||||||
if strategy == "legacy":
|
if strategy == "legacy":
|
||||||
return False
|
return False
|
||||||
elif strategy == "aot":
|
elif strategy == "aot":
|
||||||
return "ngtsc"
|
return True
|
||||||
else:
|
else:
|
||||||
fail("unreachable")
|
fail("unreachable")
|
||||||
|
|
||||||
|
@ -54,8 +54,7 @@ export function mainDiagnosticsForTest(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined {
|
function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined {
|
||||||
const transformDecorators = options.enableIvy !== 'ngtsc' && options.enableIvy !== 'tsc' &&
|
const transformDecorators = !options.enableIvy && options.annotationsAs !== 'decorators';
|
||||||
options.annotationsAs !== 'decorators';
|
|
||||||
const transformTypesToClosure = options.annotateForClosureCompiler;
|
const transformTypesToClosure = options.annotateForClosureCompiler;
|
||||||
if (!transformDecorators && !transformTypesToClosure) {
|
if (!transformDecorators && !transformTypesToClosure) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -192,7 +191,7 @@ function reportErrorsAndExit(
|
|||||||
const errorsAndWarnings = filterErrorsAndWarnings(allDiagnostics);
|
const errorsAndWarnings = filterErrorsAndWarnings(allDiagnostics);
|
||||||
if (errorsAndWarnings.length) {
|
if (errorsAndWarnings.length) {
|
||||||
const formatHost = getFormatDiagnosticsHost(options);
|
const formatHost = getFormatDiagnosticsHost(options);
|
||||||
if (options && (options.enableIvy === true || options.enableIvy === 'ngtsc')) {
|
if (options && options.enableIvy === true) {
|
||||||
const ngDiagnostics = errorsAndWarnings.filter(api.isNgDiagnostic);
|
const ngDiagnostics = errorsAndWarnings.filter(api.isNgDiagnostic);
|
||||||
const tsDiagnostics = errorsAndWarnings.filter(api.isTsDiagnostic);
|
const tsDiagnostics = errorsAndWarnings.filter(api.isTsDiagnostic);
|
||||||
consoleError(replaceTsWithNgInErrors(
|
consoleError(replaceTsWithNgInErrors(
|
||||||
|
@ -120,6 +120,10 @@ export function calcProjectFileAndBasePath(project: string):
|
|||||||
|
|
||||||
export function createNgCompilerOptions(
|
export function createNgCompilerOptions(
|
||||||
basePath: string, config: any, tsOptions: ts.CompilerOptions): api.CompilerOptions {
|
basePath: string, config: any, tsOptions: ts.CompilerOptions): api.CompilerOptions {
|
||||||
|
// enableIvy `ngtsc` is an alias for `true`.
|
||||||
|
if (config.angularCompilerOptions && config.angularCompilerOptions.enableIvy === 'ngtsc') {
|
||||||
|
config.angularCompilerOptions.enableIvy = true;
|
||||||
|
}
|
||||||
return {...tsOptions, ...config.angularCompilerOptions, genDir: basePath, basePath};
|
return {...tsOptions, ...config.angularCompilerOptions, genDir: basePath, basePath};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,9 +194,8 @@ export interface CompilerOptions extends ts.CompilerOptions {
|
|||||||
* Acceptable values are as follows:
|
* Acceptable values are as follows:
|
||||||
*
|
*
|
||||||
* `false` - run ngc normally
|
* `false` - run ngc normally
|
||||||
* `true` - run ngc with its usual global analysis, but compile decorators to Ivy fields instead
|
* `true` - run the ngtsc compiler instead of the normal ngc compiler
|
||||||
* of running the View Engine compilers
|
* `ngtsc` - alias for `true`
|
||||||
* `ngtsc` - run the ngtsc compiler instead of the normal ngc compiler
|
|
||||||
* `tsc` - behave like plain tsc as much as possible (used for testing JIT code)
|
* `tsc` - behave like plain tsc as much as possible (used for testing JIT code)
|
||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
|
@ -263,11 +263,10 @@ class AngularCompilerProgram implements Program {
|
|||||||
emitCallback?: TsEmitCallback,
|
emitCallback?: TsEmitCallback,
|
||||||
mergeEmitResultsCallback?: TsMergeEmitResultsCallback,
|
mergeEmitResultsCallback?: TsMergeEmitResultsCallback,
|
||||||
} = {}): ts.EmitResult {
|
} = {}): ts.EmitResult {
|
||||||
if (this.options.enableIvy === 'ngtsc' || this.options.enableIvy === 'tsc') {
|
if (this.options.enableIvy) {
|
||||||
throw new Error('Cannot run legacy compiler in ngtsc mode');
|
throw new Error('Cannot run legacy compiler in ngtsc mode');
|
||||||
}
|
}
|
||||||
return this.options.enableIvy === true ? this._emitRender3(parameters) :
|
return this._emitRender2(parameters);
|
||||||
this._emitRender2(parameters);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _emitRender3(
|
private _emitRender3(
|
||||||
@ -899,7 +898,7 @@ export function createProgram({rootNames, options, host, oldProgram}: {
|
|||||||
options: CompilerOptions,
|
options: CompilerOptions,
|
||||||
host: CompilerHost, oldProgram?: Program
|
host: CompilerHost, oldProgram?: Program
|
||||||
}): Program {
|
}): Program {
|
||||||
if (options.enableIvy === 'ngtsc') {
|
if (options.enableIvy === true) {
|
||||||
return new NgtscProgram(rootNames, options, host, oldProgram);
|
return new NgtscProgram(rootNames, options, host, oldProgram);
|
||||||
} else if (options.enableIvy === 'tsc') {
|
} else if (options.enableIvy === 'tsc') {
|
||||||
return new TscPassThroughProgram(rootNames, options, host, oldProgram);
|
return new TscPassThroughProgram(rootNames, options, host, oldProgram);
|
||||||
|
@ -2001,108 +2001,6 @@ describe('ngc transformer command-line', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ivy', () => {
|
|
||||||
function emittedFile(name: string): string {
|
|
||||||
const outputName = path.resolve(outDir, name);
|
|
||||||
expect(fs.existsSync(outputName)).toBe(true);
|
|
||||||
return fs.readFileSync(outputName, {encoding: 'UTF-8'});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should emit the hello world example', () => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"extends": "./tsconfig-base.json",
|
|
||||||
"files": ["hello-world.ts"],
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"enableIvy": true
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
write('hello-world.ts', `
|
|
||||||
import {Component, NgModule} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'hello-world',
|
|
||||||
template: 'Hello, world!'
|
|
||||||
})
|
|
||||||
export class HelloWorldComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [HelloWorldComponent]
|
|
||||||
})
|
|
||||||
export class HelloWorldModule {}
|
|
||||||
`);
|
|
||||||
const exitCode = main(['-p', path.join(basePath, 'tsconfig.json')]);
|
|
||||||
expect(exitCode).toBe(0, 'Compile failed');
|
|
||||||
expect(emittedFile('hello-world.js')).toContain('ngComponentDef');
|
|
||||||
expect(emittedFile('hello-world.js')).toContain('HelloWorldComponent_Factory');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should emit an injection of a string token', () => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"extends": "./tsconfig-base.json",
|
|
||||||
"files": ["hello-world.ts"],
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"enableIvy": true
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
write('hello-world.ts', `
|
|
||||||
import {Component, Inject, NgModule} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'hello-world',
|
|
||||||
template: 'Hello, world!'
|
|
||||||
})
|
|
||||||
export class HelloWorldComponent {
|
|
||||||
constructor (@Inject('test') private test: string) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [HelloWorldComponent],
|
|
||||||
providers: [
|
|
||||||
{provide: 'test', useValue: 'test'}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class HelloWorldModule {}
|
|
||||||
`);
|
|
||||||
const errors: string[] = [];
|
|
||||||
const exitCode = main(['-p', path.join(basePath, 'tsconfig.json')], msg => errors.push(msg));
|
|
||||||
expect(exitCode).toBe(0, `Compile failed:\n${errors.join('\n ')}`);
|
|
||||||
expect(emittedFile('hello-world.js')).toContain('ngComponentDef');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should emit an example that uses the E() instruction', () => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"extends": "./tsconfig-base.json",
|
|
||||||
"files": ["hello-world.ts"],
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"enableIvy": true
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
write('hello-world.ts', `
|
|
||||||
import {Component, NgModule} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'hello-world',
|
|
||||||
template: '<h1><div style="text-align:center"> Hello, {{name}}! </div></h1> '
|
|
||||||
})
|
|
||||||
export class HelloWorldComponent {
|
|
||||||
name = 'World';
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({declarations: [HelloWorldComponent]})
|
|
||||||
export class HelloWorldModule {}
|
|
||||||
`);
|
|
||||||
const errors: string[] = [];
|
|
||||||
const exitCode = main(['-p', path.join(basePath, 'tsconfig.json')], msg => errors.push(msg));
|
|
||||||
expect(exitCode).toBe(0, `Compile failed:\n${errors.join('\n ')}`);
|
|
||||||
expect(emittedFile('hello-world.js')).toContain('ngComponentDef');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('tree shakeable services', () => {
|
describe('tree shakeable services', () => {
|
||||||
|
|
||||||
function compileService(source: string): string {
|
function compileService(source: string): string {
|
||||||
@ -2321,92 +2219,6 @@ describe('ngc transformer command-line', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngInjectorDef', () => {
|
|
||||||
it('is applied with lowered metadata', () => {
|
|
||||||
writeConfig(`{
|
|
||||||
"extends": "./tsconfig-base.json",
|
|
||||||
"files": ["module.ts"],
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"enableIvy": true,
|
|
||||||
"skipTemplateCodegen": true
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
write('module.ts', `
|
|
||||||
import {Injectable, NgModule} from '@angular/core';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ServiceA {}
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ServiceB {}
|
|
||||||
|
|
||||||
@NgModule()
|
|
||||||
export class Exported {}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
providers: [ServiceA]
|
|
||||||
})
|
|
||||||
export class Imported {
|
|
||||||
static forRoot() {
|
|
||||||
console.log('not statically analyzable');
|
|
||||||
return {
|
|
||||||
ngModule: Imported,
|
|
||||||
providers: [] as any,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
providers: [ServiceA, ServiceB],
|
|
||||||
imports: [Imported.forRoot()],
|
|
||||||
exports: [Exported],
|
|
||||||
})
|
|
||||||
export class Module {}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const exitCode = main(['-p', path.join(basePath, 'tsconfig.json')], errorSpy);
|
|
||||||
expect(exitCode).toEqual(0);
|
|
||||||
|
|
||||||
const modulePath = path.resolve(outDir, 'module.js');
|
|
||||||
const moduleSource = fs.readFileSync(modulePath, 'utf8');
|
|
||||||
expect(moduleSource)
|
|
||||||
.toContain('var ɵ1 = [ServiceA, ServiceB], ɵ2 = [Imported.forRoot()], ɵ3 = [Exported];');
|
|
||||||
expect(moduleSource)
|
|
||||||
.toContain(
|
|
||||||
'Imported.ngInjectorDef = i0.defineInjector({ factory: function Imported_Factory() { return new Imported(); }, providers: ɵ0, imports: [] });');
|
|
||||||
expect(moduleSource)
|
|
||||||
.toContain(
|
|
||||||
'Module.ngInjectorDef = i0.defineInjector({ factory: function Module_Factory() { return new Module(); }, providers: ɵ1, imports: [ɵ2, ɵ3] });');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('rewrites Injector to INJECTOR in Ivy factory functions ', () => {
|
|
||||||
writeConfig(`{
|
|
||||||
"extends": "./tsconfig-base.json",
|
|
||||||
"files": ["service.ts"],
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"enableIvy": true
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
write('service.ts', `
|
|
||||||
import {Injectable, Injector} from '@angular/core';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class Service {
|
|
||||||
constructor(private injector: Injector) {}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const exitCode = main(['-p', path.join(basePath, 'tsconfig.json')], errorSpy);
|
|
||||||
expect(exitCode).toEqual(0);
|
|
||||||
|
|
||||||
const modulePath = path.resolve(outDir, 'service.js');
|
|
||||||
const moduleSource = fs.readFileSync(modulePath, 'utf8');
|
|
||||||
expect(moduleSource).not.toMatch(/inject\(i0\.Injector/);
|
|
||||||
expect(moduleSource).toMatch(/inject\(i0\.INJECTOR/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('libraries should not break strictMetadataEmit', () => {
|
it('libraries should not break strictMetadataEmit', () => {
|
||||||
// first only generate .d.ts / .js / .metadata.json files
|
// first only generate .d.ts / .js / .metadata.json files
|
||||||
writeConfig(`{
|
writeConfig(`{
|
||||||
|
@ -19,7 +19,7 @@ describe('ngtools_api (deprecated)', () => {
|
|||||||
beforeEach(() => { testSupport = setup(); });
|
beforeEach(() => { testSupport = setup(); });
|
||||||
|
|
||||||
function createProgram(rootNames: string[]) {
|
function createProgram(rootNames: string[]) {
|
||||||
const options = testSupport.createCompilerOptions({enableIvy: ivyEnabled && 'ngtsc'});
|
const options = testSupport.createCompilerOptions({enableIvy: ivyEnabled});
|
||||||
const host = ts.createCompilerHost(options, true);
|
const host = ts.createCompilerHost(options, true);
|
||||||
const program =
|
const program =
|
||||||
ts.createProgram(rootNames.map(p => path.resolve(testSupport.basePath, p)), options, host);
|
ts.createProgram(rootNames.map(p => path.resolve(testSupport.basePath, p)), options, host);
|
||||||
|
@ -70,7 +70,7 @@ export class NgtscTestEnvironment {
|
|||||||
"typeRoots": ["node_modules/@types"]
|
"typeRoots": ["node_modules/@types"]
|
||||||
},
|
},
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"enableIvy": "ngtsc"
|
"enableIvy": true
|
||||||
}
|
}
|
||||||
}`);
|
}`);
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ export class NgtscTestEnvironment {
|
|||||||
tsconfig(extraOpts: {[key: string]: string | boolean} = {}, extraRootDirs?: string[]): void {
|
tsconfig(extraOpts: {[key: string]: string | boolean} = {}, extraRootDirs?: string[]): void {
|
||||||
const tsconfig: {[key: string]: any} = {
|
const tsconfig: {[key: string]: any} = {
|
||||||
extends: './tsconfig-base.json',
|
extends: './tsconfig-base.json',
|
||||||
angularCompilerOptions: {...extraOpts, enableIvy: 'ngtsc'},
|
angularCompilerOptions: {...extraOpts, enableIvy: true},
|
||||||
};
|
};
|
||||||
if (extraRootDirs !== undefined) {
|
if (extraRootDirs !== undefined) {
|
||||||
tsconfig.compilerOptions = {
|
tsconfig.compilerOptions = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user