feat(compiler-cli): lower metadata useValue
and data
literal fields (#18905)
With this commit the compiler will "lower" expressions into exported variables for values the compiler does not need to know statically in order to be able to generate a factory. For example: ``` providers: [{provider: 'token', useValue: calculated()}] ``` produced an error as the expression `calculated()` is not supported by the compiler because `calculated` is not a [known function](https://angular.io/guide/metadata#annotationsdecorators) With this commit this is rewritten, during emit of the .js file, into something like: ``` export var ɵ0 = calculated(); ... provdiers: [{provider: 'token', useValue: ɵ0}] ``` The compiler then will now generate a reference to the exported `ɵ0` instead of failing to evaluate `calculated()`. PR Close #18905
This commit is contained in:

committed by
Jason Aden

parent
c7e1bda32f
commit
0e64261f26
@ -12,6 +12,7 @@ import * as ts from 'typescript';
|
||||
|
||||
import {TypeChecker} from '../../src/diagnostics/check_types';
|
||||
import {Diagnostic} from '../../src/transformers/api';
|
||||
import {LowerMetadataCache} from '../../src/transformers/lower_expressions';
|
||||
|
||||
function compile(
|
||||
rootDirs: MockData, options: AotCompilerOptions = {},
|
||||
@ -19,7 +20,7 @@ function compile(
|
||||
const rootDirArr = toMockFileArray(rootDirs);
|
||||
const scriptNames = rootDirArr.map(entry => entry.fileName).filter(isSource);
|
||||
const host = new MockCompilerHost(scriptNames, arrayToMockDir(rootDirArr));
|
||||
const aotHost = new MockAotCompilerHost(host);
|
||||
const aotHost = new MockAotCompilerHost(host, new LowerMetadataCache({}));
|
||||
const tsSettings = {...settings, ...tsOptions};
|
||||
const program = ts.createProgram(host.scriptNames.slice(0), tsSettings, host);
|
||||
const ngChecker = new TypeChecker(program, tsSettings, host, aotHost, options);
|
||||
@ -80,6 +81,12 @@ describe('ng type checker', () => {
|
||||
it('should accept a safe property access of a nullable field reference of a method result',
|
||||
() => { a('{{getMaybePerson()?.name}}'); });
|
||||
});
|
||||
|
||||
describe('with lowered expressions', () => {
|
||||
it('should not report lowered expressions as errors', () => {
|
||||
expectNoDiagnostics(compile([angularFiles, LOWERING_QUICKSTART]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function appComponentSource(template: string): string {
|
||||
@ -134,8 +141,38 @@ const QUICKSTART: MockDirectory = {
|
||||
}
|
||||
};
|
||||
|
||||
const LOWERING_QUICKSTART: MockDirectory = {
|
||||
quickstart: {
|
||||
app: {
|
||||
'app.component.ts': appComponentSource('<h1>Hello {{name}}</h1>'),
|
||||
'app.module.ts': `
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
import { toString } from './utils';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
class Foo {}
|
||||
|
||||
@Component({
|
||||
template: '',
|
||||
providers: [
|
||||
{provide: 'someToken', useFactory: () => new Foo()}
|
||||
]
|
||||
})
|
||||
export class Bar {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [ AppComponent, Bar ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
`
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function expectNoDiagnostics(diagnostics: Diagnostic[]) {
|
||||
if (diagnostics && diagnostics.length) {
|
||||
throw new Error(diagnostics.map(d => `${d.span}: ${d.message}`).join('\n'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user