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 this.pushContext(OutputContext.Import); |
15 this.emit('library'); | 16 this.emit('@JS()'); |
16 this.emit(this.getLibraryName()); | 17 this.emit('library'); |
17 this.emit(';'); | 18 this.emit(this.getLibraryName()); |
18 } | 19 this.emit(';'); |
19 this.fc.emitExtraImports(<ts.SourceFile>node); | 20 this.popContext(); |
| 21 |
| 22 this.emitImport('package:js/js.dart'); |
20 ts.forEachChild(node, this.visit.bind(this)); | 23 ts.forEachChild(node, this.visit.bind(this)); |
21 break; | 24 break; |
22 case ts.SyntaxKind.EndOfFileToken: | 25 case ts.SyntaxKind.EndOfFileToken: |
23 ts.forEachChild(node, this.visit.bind(this)); | 26 ts.forEachChild(node, this.visit.bind(this)); |
24 break; | 27 break; |
25 case ts.SyntaxKind.ImportDeclaration: | 28 case ts.SyntaxKind.ImportDeclaration: |
26 let importDecl = <ts.ImportDeclaration>node; | 29 let importDecl = <ts.ImportDeclaration>node; |
27 if (importDecl.importClause) { | 30 if (importDecl.importClause) { |
28 if (this.isEmptyImport(importDecl)) return true; | 31 if (this.isEmptyImport(importDecl)) return true; |
29 this.emit('import'); | 32 this.emit('import'); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 if (exportDecl.moduleSpecifier) { | 77 if (exportDecl.moduleSpecifier) { |
75 this.visitExternalModuleReferenceExpr(exportDecl.moduleSpecifier); | 78 this.visitExternalModuleReferenceExpr(exportDecl.moduleSpecifier); |
76 } else { | 79 } else { |
77 this.reportError(node, 're-exports must have a module URL (export x fr
om "./y").'); | 80 this.reportError(node, 're-exports must have a module URL (export x fr
om "./y").'); |
78 } | 81 } |
79 if (exportDecl.exportClause) this.visit(exportDecl.exportClause); | 82 if (exportDecl.exportClause) this.visit(exportDecl.exportClause); |
80 this.emit(';'); | 83 this.emit(';'); |
81 break; | 84 break; |
82 case ts.SyntaxKind.ImportEqualsDeclaration: | 85 case ts.SyntaxKind.ImportEqualsDeclaration: |
83 let importEqDecl = <ts.ImportEqualsDeclaration>node; | 86 let importEqDecl = <ts.ImportEqualsDeclaration>node; |
| 87 this.pushContext(OutputContext.Import); |
84 this.emit('import'); | 88 this.emit('import'); |
85 this.visit(importEqDecl.moduleReference); | 89 this.visit(importEqDecl.moduleReference); |
86 this.emit('as'); | 90 this.emit('as'); |
87 this.fc.visitTypeName(importEqDecl.name); | 91 this.fc.visitTypeName(importEqDecl.name); |
88 this.emit(';'); | 92 this.emit(';'); |
| 93 this.popContext(); |
89 break; | 94 break; |
90 case ts.SyntaxKind.ExternalModuleReference: | 95 case ts.SyntaxKind.ExternalModuleReference: |
91 this.visitExternalModuleReferenceExpr((<ts.ExternalModuleReference>node)
.expression); | 96 this.visitExternalModuleReferenceExpr((<ts.ExternalModuleReference>node)
.expression); |
92 break; | 97 break; |
93 | 98 |
94 default: | 99 default: |
95 return false; | 100 return false; |
96 } | 101 } |
97 return true; | 102 return true; |
98 } | 103 } |
99 | 104 |
100 private static isIgnoredImport(e: ts.ImportSpecifier) { | 105 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 | 106 |
116 private visitExternalModuleReferenceExpr(expr: ts.Expression) { | 107 private visitExternalModuleReferenceExpr(expr: ts.Expression) { |
117 // TODO: what if this isn't a string literal? | 108 // TODO: what if this isn't a string literal? |
118 let moduleName = <ts.StringLiteral>expr; | 109 let moduleName = <ts.StringLiteral>expr; |
119 let text = moduleName.text; | 110 let text = moduleName.text; |
120 if (text.match(/^\.\//)) { | 111 if (text.match(/^\.\//)) { |
121 // Strip './' to be more Dart-idiomatic. | 112 // Strip './' to be more Dart-idiomatic. |
122 text = text.substring(2); | 113 text = text.substring(2); |
123 } else if (!text.match(/^\.\.\//)) { | 114 } else if (!text.match(/^\.\.\//)) { |
124 // Unprefixed imports are package imports. | 115 // Unprefixed imports are package imports. |
125 text = 'package:' + text; | 116 text = 'package:' + text; |
126 } | 117 } |
127 this.emit(JSON.stringify(text + '.dart')); | 118 this.emit(JSON.stringify(text + '.dart')); |
128 } | 119 } |
129 | 120 |
130 private isEmptyImport(n: ts.ImportDeclaration): boolean { | 121 private isEmptyImport(n: ts.ImportDeclaration): boolean { |
131 let bindings = n.importClause.namedBindings; | 122 let bindings = n.importClause.namedBindings; |
132 if (bindings.kind !== ts.SyntaxKind.NamedImports) return false; | 123 if (bindings.kind !== ts.SyntaxKind.NamedImports) return false; |
133 let elements = (<ts.NamedImports>bindings).elements; | 124 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, | 125 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); | 126 return elements.every(ModuleTranspiler.isIgnoredImport); |
138 } | 127 } |
139 | 128 |
140 private filterImports(ns: ts.ImportOrExportSpecifier[]) { | 129 private filterImports(ns: ts.ImportOrExportSpecifier[]) { |
141 return ns.filter((e) => !ModuleTranspiler.isIgnoredImport(e)); | 130 return ns.filter((e) => !ModuleTranspiler.isIgnoredImport(e)); |
142 } | 131 } |
143 | 132 |
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) { | 133 getLibraryName(nameForTest?: string) { |
153 let fileName = this.getRelativeFileName(nameForTest); | 134 let fileName = this.getRelativeFileName(nameForTest); |
154 let parts = fileName.split('/'); | 135 let parts = fileName.split('/'); |
155 return parts.filter((p) => p.length > 0) | 136 return parts.filter((p) => p.length > 0) |
156 .map((p) => p.replace(/[^\w.]/g, '_')) | 137 .map((p) => p.replace(/[^\w.]/g, '_')) |
| 138 .map((p) => p.replace(/\.d\.ts$/g, '')) |
157 .map((p) => p.replace(/\.[jt]s$/g, '')) | 139 .map((p) => p.replace(/\.[jt]s$/g, '')) |
158 .map((p) => ModuleTranspiler.DART_RESERVED_WORDS.indexOf(p) !== -1 ? '_'
+ p : p) | 140 .map((p) => p.replace(/\./g, '')) |
| 141 .map((p) => FacadeConverter.DART_RESERVED_WORDS.indexOf(p) !== -1 ? '_'
+ p : p) |
| 142 .filter((p) => p.length > 0) |
159 .join('.'); | 143 .join('.'); |
160 } | 144 } |
161 } | 145 } |
OLD | NEW |