Skip to content

Commit

Permalink
feat(compiler): Added support for references to static fields. (angul…
Browse files Browse the repository at this point in the history
  • Loading branch information
chuckjaz authored Jul 28, 2016
1 parent 422effd commit b58e9ea
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 4 deletions.
13 changes: 11 additions & 2 deletions modules/@angular/compiler-cli/src/static_reflector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,17 @@ export class StaticReflector implements ReflectorReader {
return null;
case 'select':
let selectTarget = simplify(expression['expression']);
let member = simplify(expression['member']);
if (selectTarget && isPrimitive(member)) return selectTarget[member];
if (selectTarget instanceof StaticSymbol) {
// Access to a static instance variable
const declarationValue = resolveReferenceValue(selectTarget);
if (declarationValue && declarationValue.statics) {
selectTarget = declarationValue.statics;
} else {
return null;
}
}
const member = simplify(expression['member']);
if (selectTarget && isPrimitive(member)) return simplify(selectTarget[member]);
return null;
case 'reference':
if (!expression.module) {
Expand Down
24 changes: 24 additions & 0 deletions modules/@angular/compiler-cli/test/static_reflector_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,13 @@ describe('StaticReflector', () => {
expect(annotations.length).toBe(1);
expect(annotations[0].providers).toEqual({provider: 'a', useValue: 100});
});

it('should be able to get metadata for a class containing a static field reference', () => {
const annotations =
reflector.annotations(host.getStaticSymbol('/tmp/src/static-field-reference.ts', 'Foo'));
expect(annotations.length).toBe(1);
expect(annotations[0].providers).toEqual([{provider: 'a', useValue: 'Some string'}]);
});
});

class MockReflectorHost implements StaticReflectorHost {
Expand Down Expand Up @@ -963,6 +970,23 @@ class MockReflectorHost implements StaticReflectorHost {
providers: MyModule.with(100)
})
export class MyComponent { }
`,
'/tmp/src/static-field.ts': `
import {Injectable} from 'angular2/core';
@Injectable()
export class MyModule {
static VALUE = 'Some string';
}
`,
'/tmp/src/static-field-reference.ts': `
import {Component} from 'angular2/src/core/metadata';
import {MyModule} from './static-field';
@Component({
providers: [ { provider: 'a', useValue: MyModule.VALUE } ]
})
export class Foo { }
`
};

Expand Down
13 changes: 12 additions & 1 deletion tools/@angular/tsc-wrapped/src/collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,20 @@ export class MetadataCollector {
case ts.SyntaxKind.GetAccessor:
case ts.SyntaxKind.SetAccessor:
const property = <ts.PropertyDeclaration>member;
if (property.flags & ts.NodeFlags.Static) {
const name = evaluator.nameOf(property.name);
if (!isMetadataError(name)) {
if (property.initializer) {
const value = evaluator.evaluateNode(property.initializer);
recordStaticMember(name, value);
} else {
recordStaticMember(name, errorSym('Variable not initialized', property.name));
}
}
}
const propertyDecorators = getDecorators(property.decorators);
if (propertyDecorators) {
let name = evaluator.nameOf(property.name);
const name = evaluator.nameOf(property.name);
if (!isMetadataError(name)) {
recordMember(name, {__symbolic: 'property', decorators: propertyDecorators});
}
Expand Down
51 changes: 50 additions & 1 deletion tools/@angular/tsc-wrapped/test/collector.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ describe('Collector', () => {
host = new Host(FILES, [
'/app/app.component.ts', '/app/cases-data.ts', '/app/error-cases.ts', '/promise.ts',
'/unsupported-1.ts', '/unsupported-2.ts', 'import-star.ts', 'exported-functions.ts',
'exported-enum.ts', 'exported-consts.ts', 'static-method.ts', 'static-method-call.ts'
'exported-enum.ts', 'exported-consts.ts', 'static-method.ts', 'static-method-call.ts',
'static-field-reference.ts'
]);
service = ts.createLanguageService(host, documentRegistry);
program = service.getProgram();
Expand Down Expand Up @@ -378,6 +379,37 @@ describe('Collector', () => {
}]
}]);
});

it('should be able to collect a static field', () => {
let staticSource = program.getSourceFile('/static-field.ts');
let metadata = collector.getMetadata(staticSource);
expect(metadata).toBeDefined();
let classData = <ClassMetadata>metadata.metadata['MyModule'];
expect(classData).toBeDefined();
expect(classData.statics).toEqual({VALUE: 'Some string'});
});

it('should be able to collect a reference to a static field', () => {
let staticSource = program.getSourceFile('/static-field-reference.ts');
let metadata = collector.getMetadata(staticSource);
expect(metadata).toBeDefined();
let classData = <ClassMetadata>metadata.metadata['Foo'];
expect(classData).toBeDefined();
expect(classData.decorators).toEqual([{
__symbolic: 'call',
expression: {__symbolic: 'reference', module: 'angular2/core', name: 'Component'},
arguments: [{
providers: [{
provide: 'a',
useValue: {
__symbolic: 'select',
expression: {__symbolic: 'reference', module: './static-field.ts', name: 'MyModule'},
member: 'VALUE'
}
}]
}]
}]);
});
});

// TODO: Do not use \` in a template literal as it confuses clang-format
Expand Down Expand Up @@ -642,6 +674,23 @@ const FILES: Directory = {
})
export class Foo { }
`,
'static-field.ts': `
import {Injectable} from 'angular2/core';
@Injectable()
export class MyModule {
static VALUE = 'Some string';
}
`,
'static-field-reference.ts': `
import {Component} from 'angular2/core';
import {MyModule} from './static-field.ts';
@Component({
providers: [ { provide: 'a', useValue: MyModule.VALUE } ]
})
export class Foo { }
`,
'node_modules': {
'angular2': {
'core.d.ts': `
Expand Down

0 comments on commit b58e9ea

Please sign in to comment.