feat(compiler): Added support for references to static fields. (#10334)
Closes: #10332
This commit is contained in:
parent
422effdd18
commit
b58e9ea775
@ -438,8 +438,17 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
return null;
|
return null;
|
||||||
case 'select':
|
case 'select':
|
||||||
let selectTarget = simplify(expression['expression']);
|
let selectTarget = simplify(expression['expression']);
|
||||||
let member = simplify(expression['member']);
|
if (selectTarget instanceof StaticSymbol) {
|
||||||
if (selectTarget && isPrimitive(member)) return selectTarget[member];
|
// 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;
|
return null;
|
||||||
case 'reference':
|
case 'reference':
|
||||||
if (!expression.module) {
|
if (!expression.module) {
|
||||||
|
@ -404,6 +404,13 @@ describe('StaticReflector', () => {
|
|||||||
expect(annotations.length).toBe(1);
|
expect(annotations.length).toBe(1);
|
||||||
expect(annotations[0].providers).toEqual({provider: 'a', useValue: 100});
|
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 {
|
class MockReflectorHost implements StaticReflectorHost {
|
||||||
@ -963,6 +970,23 @@ class MockReflectorHost implements StaticReflectorHost {
|
|||||||
providers: MyModule.with(100)
|
providers: MyModule.with(100)
|
||||||
})
|
})
|
||||||
export class MyComponent { }
|
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 { }
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -150,9 +150,20 @@ export class MetadataCollector {
|
|||||||
case ts.SyntaxKind.GetAccessor:
|
case ts.SyntaxKind.GetAccessor:
|
||||||
case ts.SyntaxKind.SetAccessor:
|
case ts.SyntaxKind.SetAccessor:
|
||||||
const property = <ts.PropertyDeclaration>member;
|
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);
|
const propertyDecorators = getDecorators(property.decorators);
|
||||||
if (propertyDecorators) {
|
if (propertyDecorators) {
|
||||||
let name = evaluator.nameOf(property.name);
|
const name = evaluator.nameOf(property.name);
|
||||||
if (!isMetadataError(name)) {
|
if (!isMetadataError(name)) {
|
||||||
recordMember(name, {__symbolic: 'property', decorators: propertyDecorators});
|
recordMember(name, {__symbolic: 'property', decorators: propertyDecorators});
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,8 @@ describe('Collector', () => {
|
|||||||
host = new Host(FILES, [
|
host = new Host(FILES, [
|
||||||
'/app/app.component.ts', '/app/cases-data.ts', '/app/error-cases.ts', '/promise.ts',
|
'/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',
|
'/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);
|
service = ts.createLanguageService(host, documentRegistry);
|
||||||
program = service.getProgram();
|
program = service.getProgram();
|
||||||
@ -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
|
// TODO: Do not use \` in a template literal as it confuses clang-format
|
||||||
@ -642,6 +674,23 @@ const FILES: Directory = {
|
|||||||
})
|
})
|
||||||
export class Foo { }
|
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': {
|
'node_modules': {
|
||||||
'angular2': {
|
'angular2': {
|
||||||
'core.d.ts': `
|
'core.d.ts': `
|
||||||
|
Loading…
x
Reference in New Issue
Block a user