| OLD | NEW |
| 1 import * as ts from 'typescript'; | 1 import * as ts from 'typescript'; |
| 2 |
| 2 import * as base from './base'; | 3 import * as base from './base'; |
| 3 import {Transpiler} from './main'; | |
| 4 import {FacadeConverter} from './facade_converter'; | 4 import {FacadeConverter} from './facade_converter'; |
| 5 import {OutputContext, Transpiler} from './main'; |
| 5 | 6 |
| 6 export default class ModuleTranspiler extends base.TranspilerBase { | 7 export default class ModuleTranspiler extends base.TranspilerBase { |
| 7 constructor(tr: Transpiler, private fc: FacadeConverter, private generateLibra
ryName: boolean) { | 8 constructor(tr: Transpiler, private fc: FacadeConverter, private generateLibra
ryName: boolean) { |
| 8 super(tr); | 9 super(tr); |
| 9 } | 10 } |
| 10 | 11 |
| 11 visitNode(node: ts.Node): boolean { | 12 visitNode(node: ts.Node): boolean { |
| 12 switch (node.kind) { | 13 switch (node.kind) { |
| 13 case ts.SyntaxKind.SourceFile: | 14 case ts.SyntaxKind.SourceFile: |
| 14 if (this.generateLibraryName) { | 15 if (this.generateLibraryName) { |
| 16 this.pushContext(OutputContext.Import); |
| 17 this.emit('@JS()'); |
| 15 this.emit('library'); | 18 this.emit('library'); |
| 16 this.emit(this.getLibraryName()); | 19 this.emit(this.getLibraryName()); |
| 17 this.emit(';'); | 20 this.emit(';'); |
| 21 this.popContext(); |
| 22 |
| 23 this.emitImport('package:js/js.dart'); |
| 18 } | 24 } |
| 19 this.fc.emitExtraImports(<ts.SourceFile>node); | |
| 20 ts.forEachChild(node, this.visit.bind(this)); | 25 ts.forEachChild(node, this.visit.bind(this)); |
| 21 break; | 26 break; |
| 22 case ts.SyntaxKind.EndOfFileToken: | 27 case ts.SyntaxKind.EndOfFileToken: |
| 23 ts.forEachChild(node, this.visit.bind(this)); | 28 ts.forEachChild(node, this.visit.bind(this)); |
| 24 break; | 29 break; |
| 25 case ts.SyntaxKind.ImportDeclaration: | 30 case ts.SyntaxKind.ImportDeclaration: |
| 26 let importDecl = <ts.ImportDeclaration>node; | 31 let importDecl = <ts.ImportDeclaration>node; |
| 27 if (importDecl.importClause) { | 32 if (importDecl.importClause) { |
| 28 if (this.isEmptyImport(importDecl)) return true; | 33 if (this.isEmptyImport(importDecl)) return true; |
| 29 this.emit('import'); | 34 this.emit('import'); |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 if (exportDecl.moduleSpecifier) { | 79 if (exportDecl.moduleSpecifier) { |
| 75 this.visitExternalModuleReferenceExpr(exportDecl.moduleSpecifier); | 80 this.visitExternalModuleReferenceExpr(exportDecl.moduleSpecifier); |
| 76 } else { | 81 } else { |
| 77 this.reportError(node, 're-exports must have a module URL (export x fr
om "./y").'); | 82 this.reportError(node, 're-exports must have a module URL (export x fr
om "./y").'); |
| 78 } | 83 } |
| 79 if (exportDecl.exportClause) this.visit(exportDecl.exportClause); | 84 if (exportDecl.exportClause) this.visit(exportDecl.exportClause); |
| 80 this.emit(';'); | 85 this.emit(';'); |
| 81 break; | 86 break; |
| 82 case ts.SyntaxKind.ImportEqualsDeclaration: | 87 case ts.SyntaxKind.ImportEqualsDeclaration: |
| 83 let importEqDecl = <ts.ImportEqualsDeclaration>node; | 88 let importEqDecl = <ts.ImportEqualsDeclaration>node; |
| 89 this.pushContext(OutputContext.Import); |
| 84 this.emit('import'); | 90 this.emit('import'); |
| 85 this.visit(importEqDecl.moduleReference); | 91 this.visit(importEqDecl.moduleReference); |
| 86 this.emit('as'); | 92 this.emit('as'); |
| 87 this.fc.visitTypeName(importEqDecl.name); | 93 this.fc.visitTypeName(importEqDecl.name); |
| 88 this.emit(';'); | 94 this.emit(';'); |
| 95 this.popContext(); |
| 89 break; | 96 break; |
| 90 case ts.SyntaxKind.ExternalModuleReference: | 97 case ts.SyntaxKind.ExternalModuleReference: |
| 91 this.visitExternalModuleReferenceExpr((<ts.ExternalModuleReference>node)
.expression); | 98 this.visitExternalModuleReferenceExpr((<ts.ExternalModuleReference>node)
.expression); |
| 92 break; | 99 break; |
| 93 | 100 |
| 94 default: | 101 default: |
| 95 return false; | 102 return false; |
| 96 } | 103 } |
| 97 return true; | 104 return true; |
| 98 } | 105 } |
| 99 | 106 |
| 100 private static isIgnoredImport(e: ts.ImportSpecifier) { | 107 private static isIgnoredImport(e: ts.ImportSpecifier) { return false; } |
| 101 // TODO: unify with facade_converter.ts | |
| 102 let name = base.ident(e.name); | |
| 103 switch (name) { | |
| 104 case 'CONST': | |
| 105 case 'CONST_EXPR': | |
| 106 case 'normalizeBlank': | |
| 107 case 'forwardRef': | |
| 108 case 'ABSTRACT': | |
| 109 case 'IMPLEMENTS': | |
| 110 return true; | |
| 111 default: | |
| 112 return false; | |
| 113 } | |
| 114 } | |
| 115 | 108 |
| 116 private visitExternalModuleReferenceExpr(expr: ts.Expression) { | 109 private visitExternalModuleReferenceExpr(expr: ts.Expression) { |
| 117 // TODO: what if this isn't a string literal? | 110 // TODO: what if this isn't a string literal? |
| 118 let moduleName = <ts.StringLiteral>expr; | 111 let moduleName = <ts.StringLiteral>expr; |
| 119 let text = moduleName.text; | 112 let text = moduleName.text; |
| 120 if (text.match(/^\.\//)) { | 113 if (text.match(/^\.\//)) { |
| 121 // Strip './' to be more Dart-idiomatic. | 114 // Strip './' to be more Dart-idiomatic. |
| 122 text = text.substring(2); | 115 text = text.substring(2); |
| 123 } else if (!text.match(/^\.\.\//)) { | 116 } else if (!text.match(/^\.\.\//)) { |
| 124 // Unprefixed imports are package imports. | 117 // Unprefixed imports are package imports. |
| 125 text = 'package:' + text; | 118 text = 'package:' + text; |
| 126 } | 119 } |
| 127 this.emit(JSON.stringify(text + '.dart')); | 120 this.emit(JSON.stringify(text + '.dart')); |
| 128 } | 121 } |
| 129 | 122 |
| 130 private isEmptyImport(n: ts.ImportDeclaration): boolean { | 123 private isEmptyImport(n: ts.ImportDeclaration): boolean { |
| 131 let bindings = n.importClause.namedBindings; | 124 let bindings = n.importClause.namedBindings; |
| 132 if (bindings.kind !== ts.SyntaxKind.NamedImports) return false; | 125 if (bindings.kind !== ts.SyntaxKind.NamedImports) return false; |
| 133 let elements = (<ts.NamedImports>bindings).elements; | 126 let elements = (<ts.NamedImports>bindings).elements; |
| 134 // An import list being empty *after* filtering is ok, but if it's empty in
the code itself, | 127 if (elements.length === 0) return true; |
| 135 // it's nonsensical code, so probably a programming error. | |
| 136 if (elements.length === 0) this.reportError(n, 'empty import list'); | |
| 137 return elements.every(ModuleTranspiler.isIgnoredImport); | 128 return elements.every(ModuleTranspiler.isIgnoredImport); |
| 138 } | 129 } |
| 139 | 130 |
| 140 private filterImports(ns: ts.ImportOrExportSpecifier[]) { | 131 private filterImports(ns: ts.ImportOrExportSpecifier[]) { |
| 141 return ns.filter((e) => !ModuleTranspiler.isIgnoredImport(e)); | 132 return ns.filter((e) => !ModuleTranspiler.isIgnoredImport(e)); |
| 142 } | 133 } |
| 143 | 134 |
| 144 // For the Dart keyword list see | |
| 145 // https://www.dartlang.org/docs/dart-up-and-running/ch02.html#keywords | |
| 146 private static DART_RESERVED_WORDS = | |
| 147 ('assert break case catch class const continue default do else enum extend
s false final ' + | |
| 148 'finally for if in is new null rethrow return super switch this throw tru
e try let void ' + | |
| 149 'while with') | |
| 150 .split(/ /); | |
| 151 | |
| 152 getLibraryName(nameForTest?: string) { | 135 getLibraryName(nameForTest?: string) { |
| 153 let fileName = this.getRelativeFileName(nameForTest); | 136 let fileName = this.getRelativeFileName(nameForTest); |
| 154 let parts = fileName.split('/'); | 137 let parts = fileName.split('/'); |
| 155 return parts.filter((p) => p.length > 0) | 138 return parts.filter((p) => p.length > 0) |
| 156 .map((p) => p.replace(/[^\w.]/g, '_')) | 139 .map((p) => p.replace(/[^\w.]/g, '_')) |
| 140 .map((p) => p.replace(/\.d\.ts$/g, '')) |
| 157 .map((p) => p.replace(/\.[jt]s$/g, '')) | 141 .map((p) => p.replace(/\.[jt]s$/g, '')) |
| 158 .map((p) => ModuleTranspiler.DART_RESERVED_WORDS.indexOf(p) !== -1 ? '_'
+ p : p) | 142 .map((p) => FacadeConverter.DART_RESERVED_WORDS.indexOf(p) !== -1 ? '_'
+ p : p) |
| 159 .join('.'); | 143 .join('.'); |
| 160 } | 144 } |
| 161 } | 145 } |
| OLD | NEW |