Skip to content

Commit 763ca60

Browse files
authored
feat(compiler): Support default parameters in static reflector (angular#10370)
Closes: angular#10369
1 parent 0eca7ab commit 763ca60

File tree

5 files changed

+88
-18
lines changed

5 files changed

+88
-18
lines changed

modules/@angular/compiler-cli/src/static_reflector.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,12 +308,17 @@ export class StaticReflector implements ReflectorReader {
308308
}
309309
calling.set(functionSymbol, true);
310310
try {
311-
let value = targetFunction['value'];
311+
const value = targetFunction['value'];
312312
if (value && (depth != 0 || value.__symbolic != 'error')) {
313313
// Determine the arguments
314-
let args = (expression['arguments'] || []).map((arg: any) => simplify(arg));
315-
let parameters: string[] = targetFunction['parameters'];
316-
let functionScope = BindingScope.build();
314+
const args: any[] =
315+
(expression['arguments'] || []).map((arg: any) => simplify(arg));
316+
const parameters: string[] = targetFunction['parameters'];
317+
const defaults: any[] = targetFunction.defaults;
318+
if (defaults && defaults.length > args.length) {
319+
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
320+
}
321+
const functionScope = BindingScope.build();
317322
for (let i = 0; i < parameters.length; i++) {
318323
functionScope.define(parameters[i], args[i]);
319324
}

modules/@angular/compiler-cli/test/static_reflector_spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,14 @@ describe('StaticReflector', () => {
421421
[{provider: 'a', useValue: '1'}], [{provider: 'a', useValue: '2'}]
422422
]);
423423
});
424+
425+
it('should be able to get the metadata for a class calling a method with default parameters',
426+
() => {
427+
const annotations = reflector.annotations(
428+
host.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyDefaultsComponent'));
429+
expect(annotations.length).toBe(1);
430+
expect(annotations[0].providers).toEqual([['a', true, false]]);
431+
});
424432
});
425433

426434
class MockReflectorHost implements StaticReflectorHost {
@@ -973,6 +981,9 @@ class MockReflectorHost implements StaticReflectorHost {
973981
static condMethod(cond: boolean) {
974982
return [{ provider: 'a', useValue: cond ? '1' : '2'}];
975983
}
984+
static defaultsMethod(a, b = true, c = false) {
985+
return [a, b, c];
986+
}
976987
}
977988
`,
978989
'/tmp/src/static-method-call.ts': `
@@ -988,6 +999,11 @@ class MockReflectorHost implements StaticReflectorHost {
988999
providers: [MyModule.condMethod(true), MyModule.condMethod(false)]
9891000
})
9901001
export class MyCondComponent { }
1002+
1003+
@Component({
1004+
providers: [MyModule.defaultsMethod('a')]
1005+
})
1006+
export class MyDefaultsComponent { }
9911007
`,
9921008
'/tmp/src/static-field.ts': `
9931009
import {Injectable} from 'angular2/core';

tools/@angular/tsc-wrapped/src/collector.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as ts from 'typescript';
22

33
import {Evaluator, errorSymbol, isPrimitive} from './evaluator';
4-
import {ClassMetadata, ConstructorMetadata, MemberMetadata, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataValue, MethodMetadata, ModuleMetadata, VERSION, isMetadataError, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression} from './schema';
4+
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataValue, MethodMetadata, ModuleMetadata, VERSION, isMetadataError, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression} from './schema';
55
import {Symbols} from './symbols';
66

77

@@ -19,7 +19,7 @@ export class MetadataCollector {
1919
public getMetadata(sourceFile: ts.SourceFile): ModuleMetadata {
2020
const locals = new Symbols(sourceFile);
2121
const evaluator = new Evaluator(locals);
22-
let metadata: {[name: string]: MetadataValue | ClassMetadata}|undefined;
22+
let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined;
2323

2424
function objFromDecorator(decoratorNode: ts.Decorator): MetadataSymbolicExpression {
2525
return <MetadataSymbolicExpression>evaluator.evaluateNode(decoratorNode.expression);
@@ -32,7 +32,7 @@ export class MetadataCollector {
3232

3333
function maybeGetSimpleFunction(
3434
functionDeclaration: ts.FunctionDeclaration |
35-
ts.MethodDeclaration): {func: MetadataValue, name: string}|undefined {
35+
ts.MethodDeclaration): {func: FunctionMetadata, name: string}|undefined {
3636
if (functionDeclaration.name.kind == ts.SyntaxKind.Identifier) {
3737
const nameNode = <ts.Identifier>functionDeclaration.name;
3838
const functionName = nameNode.text;
@@ -42,13 +42,17 @@ export class MetadataCollector {
4242
if (statement.kind === ts.SyntaxKind.ReturnStatement) {
4343
const returnStatement = <ts.ReturnStatement>statement;
4444
if (returnStatement.expression) {
45-
return {
46-
name: functionName, func: {
47-
__symbolic: 'function',
48-
parameters: namesOf(functionDeclaration.parameters),
49-
value: evaluator.evaluateNode(returnStatement.expression)
50-
}
45+
const func: FunctionMetadata = {
46+
__symbolic: 'function',
47+
parameters: namesOf(functionDeclaration.parameters),
48+
value: evaluator.evaluateNode(returnStatement.expression)
49+
};
50+
if (functionDeclaration.parameters.some(p => p.initializer != null)) {
51+
const defaults: MetadataValue[] = [];
52+
func.defaults = functionDeclaration.parameters.map(
53+
p => p.initializer && evaluator.evaluateNode(p.initializer));
5154
}
55+
return { func, name: functionName }
5256
}
5357
}
5458
}
@@ -90,8 +94,8 @@ export class MetadataCollector {
9094
}
9195

9296
// static member
93-
let statics: MetadataObject = null;
94-
function recordStaticMember(name: string, value: MetadataValue) {
97+
let statics: {[name: string]: MetadataValue | FunctionMetadata} = null;
98+
function recordStaticMember(name: string, value: MetadataValue | FunctionMetadata) {
9599
if (!statics) statics = {};
96100
statics[name] = value;
97101
}

tools/@angular/tsc-wrapped/src/schema.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const VERSION = 1;
1212
export interface ModuleMetadata {
1313
__symbolic: 'module';
1414
version: number;
15-
metadata: {[name: string]: (ClassMetadata | MetadataValue)};
15+
metadata: {[name: string]: (ClassMetadata | FunctionMetadata | MetadataValue)};
1616
}
1717
export function isModuleMetadata(value: any): value is ModuleMetadata {
1818
return value && value.__symbolic === 'module';
@@ -22,7 +22,7 @@ export interface ClassMetadata {
2222
__symbolic: 'class';
2323
decorators?: (MetadataSymbolicExpression|MetadataError)[];
2424
members?: MetadataMap;
25-
statics?: MetadataObject;
25+
statics?: {[name: string]: MetadataValue | FunctionMetadata};
2626
}
2727
export function isClassMetadata(value: any): value is ClassMetadata {
2828
return value && value.__symbolic === 'class';
@@ -65,7 +65,8 @@ export function isConstructorMetadata(value: any): value is ConstructorMetadata
6565
export interface FunctionMetadata {
6666
__symbolic: 'function';
6767
parameters: string[];
68-
result: MetadataValue;
68+
defaults?: MetadataValue[];
69+
value: MetadataValue;
6970
}
7071
export function isFunctionMetadata(value: any): value is FunctionMetadata {
7172
return value && value.__symbolic === 'function';

tools/@angular/tsc-wrapped/test/collector.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('Collector', () => {
2828
'static-method.ts',
2929
'static-method-call.ts',
3030
'static-method-with-if.ts',
31+
'static-method-with-default.ts',
3132
]);
3233
service = ts.createLanguageService(host, documentRegistry);
3334
program = service.getProgram();
@@ -445,6 +446,35 @@ describe('Collector', () => {
445446
}
446447
});
447448
});
449+
450+
it('should be able to collect a method with a default parameter', () => {
451+
let source = program.getSourceFile('/static-method-with-default.ts');
452+
let metadata = collector.getMetadata(source);
453+
expect(metadata).toBeDefined();
454+
let classData = <ClassMetadata>metadata.metadata['MyModule'];
455+
expect(classData).toBeDefined();
456+
expect(classData.statics).toEqual({
457+
with: {
458+
__symbolic: 'function',
459+
parameters: ['comp', 'foo', 'bar'],
460+
defaults: [undefined, true, false],
461+
value: [
462+
{__symbolic: 'reference', name: 'MyModule'}, {
463+
__symbolic: 'if',
464+
condition: {__symbolic: 'reference', name: 'foo'},
465+
thenExpression: {provider: 'a', useValue: {__symbolic: 'reference', name: 'comp'}},
466+
elseExpression: {provider: 'b', useValue: {__symbolic: 'reference', name: 'comp'}}
467+
},
468+
{
469+
__symbolic: 'if',
470+
condition: {__symbolic: 'reference', name: 'bar'},
471+
thenExpression: {provider: 'c', useValue: {__symbolic: 'reference', name: 'comp'}},
472+
elseExpression: {provider: 'd', useValue: {__symbolic: 'reference', name: 'comp'}}
473+
}
474+
]
475+
}
476+
});
477+
});
448478
});
449479

450480
// TODO: Do not use \` in a template literal as it confuses clang-format
@@ -700,6 +730,20 @@ const FILES: Directory = {
700730
}
701731
}
702732
`,
733+
'static-method-with-default.ts': `
734+
import {Injectable} from 'angular2/core';
735+
736+
@Injectable()
737+
export class MyModule {
738+
static with(comp: any, foo: boolean = true, bar: boolean = false): any[] {
739+
return [
740+
MyModule,
741+
foo ? { provider: 'a', useValue: comp } : {provider: 'b', useValue: comp},
742+
bar ? { provider: 'c', useValue: comp } : {provider: 'd', useValue: comp}
743+
];
744+
}
745+
}
746+
`,
703747
'static-method-call.ts': `
704748
import {Component} from 'angular2/core';
705749
import {MyModule} from './static-method.ts';

0 commit comments

Comments
 (0)