fix(compiler): StaticReflect now resolves re-exported symbols (#10453)

Fixes: #10451
This commit is contained in:
Chuck Jazdzewski
2016-08-02 11:45:14 -07:00
committed by GitHub
parent 3d53b33391
commit 82e7ecd611
5 changed files with 208 additions and 14 deletions

View File

@ -1,7 +1,7 @@
import * as ts from 'typescript';
import {Evaluator, errorSymbol, isPrimitive} from './evaluator';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataValue, MethodMetadata, ModuleMetadata, VERSION, isMetadataError, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression} from './schema';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isMetadataError, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression} from './schema';
import {Symbols} from './symbols';
@ -20,6 +20,7 @@ export class MetadataCollector {
const locals = new Symbols(sourceFile);
const evaluator = new Evaluator(locals);
let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined;
let exports: ModuleExportMetadata[];
function objFromDecorator(decoratorNode: ts.Decorator): MetadataSymbolicExpression {
return <MetadataSymbolicExpression>evaluator.evaluateNode(decoratorNode.expression);
@ -202,6 +203,25 @@ export class MetadataCollector {
});
ts.forEachChild(sourceFile, node => {
switch (node.kind) {
case ts.SyntaxKind.ExportDeclaration:
// Record export declarations
const exportDeclaration = <ts.ExportDeclaration>node;
const moduleSpecifier = exportDeclaration.moduleSpecifier;
if (moduleSpecifier && moduleSpecifier.kind == ts.SyntaxKind.StringLiteral) {
// Ignore exports that don't have string literals as exports.
// This is allowed by the syntax but will be flagged as an error by the type checker.
const from = (<ts.StringLiteral>moduleSpecifier).text;
const moduleExport: ModuleExportMetadata = {from};
if (exportDeclaration.exportClause) {
moduleExport.export = exportDeclaration.exportClause.elements.map(
element => element.propertyName ?
{name: element.propertyName.text, as: element.name.text} :
element.name.text)
}
if (!exports) exports = [];
exports.push(moduleExport);
}
break;
case ts.SyntaxKind.ClassDeclaration:
const classDeclaration = <ts.ClassDeclaration>node;
const className = classDeclaration.name.text;
@ -320,7 +340,12 @@ export class MetadataCollector {
}
});
return metadata && {__symbolic: 'module', version: VERSION, metadata};
if (metadata || exports) {
if (!metadata) metadata = {};
const result: ModuleMetadata = {__symbolic: 'module', version: VERSION, metadata};
if (exports) result.exports = exports;
return result;
}
}
}

View File

@ -12,12 +12,18 @@ export const VERSION = 1;
export interface ModuleMetadata {
__symbolic: 'module';
version: number;
exports?: ModuleExportMetadata[];
metadata: {[name: string]: (ClassMetadata | FunctionMetadata | MetadataValue)};
}
export function isModuleMetadata(value: any): value is ModuleMetadata {
return value && value.__symbolic === 'module';
}
export interface ModuleExportMetadata {
export?: (string|{name: string, as: string})[];
from: string;
}
export interface ClassMetadata {
__symbolic: 'class';
decorators?: (MetadataSymbolicExpression|MetadataError)[];

View File

@ -24,6 +24,7 @@ describe('Collector', () => {
'exported-functions.ts',
'exported-enum.ts',
'exported-consts.ts',
're-exports.ts',
'static-field-reference.ts',
'static-method.ts',
'static-method-call.ts',
@ -475,6 +476,16 @@ describe('Collector', () => {
}
});
});
it('should be able to collect re-exported symbols', () => {
let source = program.getSourceFile('/re-exports.ts');
let metadata = collector.getMetadata(source);
expect(metadata.exports).toEqual([
{from: './static-field', export: ['MyModule']},
{from: './static-field-reference.ts', export: [{name: 'Foo', as: 'OtherModule'}]},
{from: 'angular2/core'}
]);
});
});
// TODO: Do not use \` in a template literal as it confuses clang-format
@ -783,6 +794,11 @@ const FILES: Directory = {
}
}
`,
're-exports.ts': `
export {MyModule} from './static-field';
export {Foo as OtherModule} from './static-field-reference.ts';
export * from 'angular2/core';
`,
'node_modules': {
'angular2': {
'core.d.ts': `