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 |