From 40d8d9c3e3c1a9f4226a7f18ec453e3ecabdb6ec Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Wed, 14 Dec 2016 11:37:01 -0800 Subject: [PATCH] fix(tsc-wrapped): generate metadata for exports without module specifier fixes #13327 --- .../compiler-cli/test/aot_host_spec.ts | 5 +++ tools/@angular/tsc-wrapped/src/collector.ts | 36 ++++++++++++++----- .../tsc-wrapped/test/collector.spec.ts | 24 +++++++++++++ 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/modules/@angular/compiler-cli/test/aot_host_spec.ts b/modules/@angular/compiler-cli/test/aot_host_spec.ts index 65e2e3eee6..88334085d0 100644 --- a/modules/@angular/compiler-cli/test/aot_host_spec.ts +++ b/modules/@angular/compiler-cli/test/aot_host_spec.ts @@ -175,6 +175,7 @@ describe('CompilerHost', () => { foo: {__symbolic: 'class'}, Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}}, BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}}, + ReExport: {__symbolic: 'reference', module: './lib/utils2', name: 'ReExport'}, }, exports: [{from: './lib/utils2', export: ['Export']}], } @@ -212,7 +213,11 @@ const FILES: Entry = { }, 'metadata_versions': { 'v1.d.ts': ` + import {ReExport} from './lib/utils2'; + export {ReExport}; + export {Export} from './lib/utils2'; + export declare class Bar { ngOnInit() {} } diff --git a/tools/@angular/tsc-wrapped/src/collector.ts b/tools/@angular/tsc-wrapped/src/collector.ts index b9be3bca3e..3b5c5d2409 100644 --- a/tools/@angular/tsc-wrapped/src/collector.ts +++ b/tools/@angular/tsc-wrapped/src/collector.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {Evaluator, errorSymbol, isPrimitive} from './evaluator'; -import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema'; +import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema'; import {Symbols} from './symbols'; @@ -234,6 +234,7 @@ export class MetadataCollector { } } break; + case ts.SyntaxKind.FunctionDeclaration: if (!(node.flags & ts.NodeFlags.Export)) { // Report references to this function as an error. @@ -249,22 +250,36 @@ export class MetadataCollector { break; } }); + ts.forEachChild(sourceFile, node => { switch (node.kind) { case ts.SyntaxKind.ExportDeclaration: // Record export declarations const exportDeclaration = node; - const moduleSpecifier = exportDeclaration.moduleSpecifier; + const {moduleSpecifier, exportClause} = exportDeclaration; + + if (!moduleSpecifier) { + // no module specifier -> export {propName as name}; + if (exportClause) { + exportClause.elements.forEach(spec => { + const name = spec.name.text; + const propNode = spec.propertyName || spec.name; + const value: MetadataValue = evaluator.evaluateNode(propNode); + if (!metadata) metadata = {}; + metadata[name] = recordEntry(value, node); + }); + } + } + 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 = (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 (exportClause) { + moduleExport.export = exportClause.elements.map( + spec => spec.propertyName ? {name: spec.propertyName.text, as: spec.name.text} : + spec.name.text); } if (!exports) exports = []; exports.push(moduleExport); @@ -281,6 +296,7 @@ export class MetadataCollector { } // Otherwise don't record metadata for the class. break; + case ts.SyntaxKind.FunctionDeclaration: // Record functions that return a single value. Record the parameter // names substitution will be performed by the StaticReflector. @@ -297,6 +313,7 @@ export class MetadataCollector { } } break; + case ts.SyntaxKind.EnumDeclaration: if (node.flags & ts.NodeFlags.Export) { const enumDeclaration = node; @@ -332,7 +349,7 @@ export class MetadataCollector { } else { nextDefaultValue = recordEntry(errorSym('Unsuppported enum member name', member.name), node); - }; + } } if (writtenMembers) { if (!metadata) metadata = {}; @@ -340,6 +357,7 @@ export class MetadataCollector { } } break; + case ts.SyntaxKind.VariableStatement: const variableStatement = node; for (const variableDeclaration of variableStatement.declarationList.declarations) { @@ -373,7 +391,7 @@ export class MetadataCollector { } } else { // Destructuring (or binding) declarations are not supported, - // var {[, ]+} = ; + // var {[, ]+} = ; // or // var [[, ; // are not supported. diff --git a/tools/@angular/tsc-wrapped/test/collector.spec.ts b/tools/@angular/tsc-wrapped/test/collector.spec.ts index c3aba82e9f..1b8fe9ac24 100644 --- a/tools/@angular/tsc-wrapped/test/collector.spec.ts +++ b/tools/@angular/tsc-wrapped/test/collector.spec.ts @@ -39,6 +39,7 @@ describe('Collector', () => { 'local-symbol-ref-func-dynamic.ts', 'private-enum.ts', 're-exports.ts', + 're-exports-2.ts', 'static-field-reference.ts', 'static-method.ts', 'static-method-call.ts', @@ -527,6 +528,23 @@ describe('Collector', () => { ]); }); + it('should be able to collect exports with no module specifier', () => { + const source = program.getSourceFile('/re-exports-2.ts'); + const metadata = collector.getMetadata(source); + expect(metadata.metadata).toEqual({ + OtherModule: {__symbolic: 'reference', module: './static-field-reference', name: 'Foo'}, + MyOtherModule: {__symbolic: 'reference', module: './static-field', name: 'MyModule'}, + // TODO(vicb): support exported symbols - https://github.com/angular/angular/issues/13473 + MyClass: { + __symbolic: 'error', + message: 'Reference to non-exported class', + line: 3, + character: 4, + context: {className: 'MyClass'} + }, + }); + }); + it('should collect an error symbol if collecting a reference to a non-exported symbol', () => { const source = program.getSourceFile('/local-symbol-ref.ts'); const metadata = collector.getMetadata(source); @@ -983,6 +1001,12 @@ const FILES: Directory = { export {Foo as OtherModule} from './static-field-reference'; export * from 'angular2/core'; `, + 're-exports-2.ts': ` + import {MyModule} from './static-field'; + import {Foo as OtherModule} from './static-field-reference'; + class MyClass {} + export {OtherModule, MyModule as MyOtherModule, MyClass}; + `, 'local-symbol-ref.ts': ` import {Component, Validators} from 'angular2/core';