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":
|
||||
return False
|
||||
elif strategy == "aot":
|
||||
return "ngtsc"
|
||||
return True
|
||||
else:
|
||||
fail("unreachable")
|
||||
|
||||
|
|
|
@ -54,8 +54,7 @@ export function mainDiagnosticsForTest(
|
|||
}
|
||||
|
||||
function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined {
|
||||
const transformDecorators = options.enableIvy !== 'ngtsc' && options.enableIvy !== 'tsc' &&
|
||||
options.annotationsAs !== 'decorators';
|
||||
const transformDecorators = !options.enableIvy && options.annotationsAs !== 'decorators';
|
||||
const transformTypesToClosure = options.annotateForClosureCompiler;
|
||||
if (!transformDecorators && !transformTypesToClosure) {
|
||||
return undefined;
|
||||
|
@ -192,7 +191,7 @@ function reportErrorsAndExit(
|
|||
const errorsAndWarnings = filterErrorsAndWarnings(allDiagnostics);
|
||||
if (errorsAndWarnings.length) {
|
||||
const formatHost = getFormatDiagnosticsHost(options);
|
||||
if (options && (options.enableIvy === true || options.enableIvy === 'ngtsc')) {
|
||||
if (options && options.enableIvy === true) {
|
||||
const ngDiagnostics = errorsAndWarnings.filter(api.isNgDiagnostic);
|
||||
const tsDiagnostics = errorsAndWarnings.filter(api.isTsDiagnostic);
|
||||
consoleError(replaceTsWithNgInErrors(
|
||||
|
|
|
@ -120,6 +120,10 @@ export function calcProjectFileAndBasePath(project: string):
|
|||
|
||||
export function createNgCompilerOptions(
|
||||
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};
|
||||
}
|
||||
|
||||
|
|
|
@ -194,9 +194,8 @@ export interface CompilerOptions extends ts.CompilerOptions {
|
|||
* Acceptable values are as follows:
|
||||
*
|
||||
* `false` - run ngc normally
|
||||
* `true` - run ngc with its usual global analysis, but compile decorators to Ivy fields instead
|
||||
* of running the View Engine compilers
|
||||
* `ngtsc` - run the ngtsc compiler instead of the normal ngc compiler
|
||||
* `true` - run the ngtsc compiler instead of the normal ngc compiler
|
||||
* `ngtsc` - alias for `true`
|
||||
* `tsc` - behave like plain tsc as much as possible (used for testing JIT code)
|
||||
*
|
||||
* @publicApi
|
||||
|
|
|
@ -263,11 +263,10 @@ class AngularCompilerProgram implements Program {
|
|||
emitCallback?: TsEmitCallback,
|
||||
mergeEmitResultsCallback?: TsMergeEmitResultsCallback,
|
||||
} = {}): 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');
|
||||
}
|
||||
return this.options.enableIvy === true ? this._emitRender3(parameters) :
|
||||
this._emitRender2(parameters);
|
||||
return this._emitRender2(parameters);
|
||||
}
|
||||
|
||||
private _emitRender3(
|
||||
|
@ -899,7 +898,7 @@ export function createProgram({rootNames, options, host, oldProgram}: {
|
|||
options: CompilerOptions,
|
||||
host: CompilerHost, oldProgram?: Program
|
||||
}): Program {
|
||||
if (options.enableIvy === 'ngtsc') {
|
||||
if (options.enableIvy === true) {
|
||||
return new NgtscProgram(rootNames, options, host, oldProgram);
|
||||
} else if (options.enableIvy === 'tsc') {
|
||||
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', () => {
|
||||
|
||||
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', () => {
|
||||
// first only generate .d.ts / .js / .metadata.json files
|
||||
writeConfig(`{
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('ngtools_api (deprecated)', () => {
|
|||
beforeEach(() => { testSupport = setup(); });
|
||||
|
||||
function createProgram(rootNames: string[]) {
|
||||
const options = testSupport.createCompilerOptions({enableIvy: ivyEnabled && 'ngtsc'});
|
||||
const options = testSupport.createCompilerOptions({enableIvy: ivyEnabled});
|
||||
const host = ts.createCompilerHost(options, true);
|
||||
const program =
|
||||
ts.createProgram(rootNames.map(p => path.resolve(testSupport.basePath, p)), options, host);
|
||||
|
|
|
@ -70,7 +70,7 @@ export class NgtscTestEnvironment {
|
|||
"typeRoots": ["node_modules/@types"]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableIvy": "ngtsc"
|
||||
"enableIvy": true
|
||||
}
|
||||
}`);
|
||||
|
||||
|
@ -100,7 +100,7 @@ export class NgtscTestEnvironment {
|
|||
tsconfig(extraOpts: {[key: string]: string | boolean} = {}, extraRootDirs?: string[]): void {
|
||||
const tsconfig: {[key: string]: any} = {
|
||||
extends: './tsconfig-base.json',
|
||||
angularCompilerOptions: {...extraOpts, enableIvy: 'ngtsc'},
|
||||
angularCompilerOptions: {...extraOpts, enableIvy: true},
|
||||
};
|
||||
if (extraRootDirs !== undefined) {
|
||||
tsconfig.compilerOptions = {
|
||||
|
|
Loading…
Reference in New Issue