@@ -38,6 +38,16 @@ interface Scope {
3838 loopContinued ?: boolean ;
3939}
4040
41+ export interface EmitResolver {
42+ isValueAliasDeclaration ( node : ts . Node ) : boolean ;
43+ isReferencedAliasDeclaration ( node : ts . Node , checkChildren ?: boolean ) : boolean ;
44+ moduleExportsSomeValue ( moduleReferenceExpression : ts . Expression ) : boolean ;
45+ }
46+
47+ export interface DiagnosticsProducingTypeChecker extends ts . TypeChecker {
48+ getEmitResolver ( sourceFile ?: ts . SourceFile , cancellationToken ?: ts . CancellationToken ) : EmitResolver ;
49+ }
50+
4151export class LuaTransformer {
4252 public luaKeywords : Set < string > = new Set ( [
4353 "_G" , "and" , "assert" , "break" , "coroutine" , "debug" , "do" , "else" , "elseif" , "end" , "error" , "false" , "for" ,
@@ -49,11 +59,13 @@ export class LuaTransformer {
4959 private isStrict : boolean ;
5060 private luaTarget : LuaTarget ;
5161
52- private checker : ts . TypeChecker ;
62+ private checker : DiagnosticsProducingTypeChecker ;
5363 protected options : CompilerOptions ;
5464
55- private isModule = false ;
65+ // Resolver is lazy-initialized in transformSourceFile to avoid type-checking all files
66+ private resolver ! : EmitResolver ;
5667
68+ private isModule = false ;
5769 private currentSourceFile ?: ts . SourceFile ;
5870
5971 private currentNamespace : ts . ModuleDeclaration | undefined ;
@@ -72,7 +84,7 @@ export class LuaTransformer {
7284 private readonly typeValidationCache : Map < ts . Type , Set < ts . Type > > = new Map < ts . Type , Set < ts . Type > > ( ) ;
7385
7486 public constructor ( protected program : ts . Program ) {
75- this . checker = program . getTypeChecker ( ) ;
87+ this . checker = ( program as any ) . getDiagnosticsProducingTypeChecker ( ) ;
7688 this . options = program . getCompilerOptions ( ) ;
7789 this . isStrict = this . options . alwaysStrict !== undefined
7890 || ( this . options . strict !== undefined && this . options . alwaysStrict !== false )
@@ -102,6 +114,7 @@ export class LuaTransformer {
102114 this . setupState ( ) ;
103115
104116 this . currentSourceFile = node ;
117+ this . resolver = this . checker . getEmitResolver ( node ) ;
105118
106119 let statements : tstl . Statement [ ] = [ ] ;
107120 if ( node . flags & ts . NodeFlags . JsonFile ) {
@@ -233,36 +246,6 @@ export class LuaTransformer {
233246 }
234247
235248 public transformExportDeclaration ( statement : ts . ExportDeclaration ) : StatementVisitResult {
236- if ( statement . moduleSpecifier === undefined ) {
237- if ( statement . exportClause === undefined ) {
238- throw TSTLErrors . InvalidExportDeclaration ( statement ) ;
239- }
240-
241- const result = [ ] ;
242- for ( const exportElement of statement . exportClause . elements ) {
243- let exportedIdentifier : tstl . Expression | undefined ;
244- if ( exportElement . propertyName !== undefined ) {
245- exportedIdentifier = this . transformIdentifier ( exportElement . propertyName ) ;
246-
247- } else {
248- const exportedSymbol = this . checker . getExportSpecifierLocalTargetSymbol ( exportElement ) ;
249- if ( exportedSymbol !== undefined ) {
250- exportedIdentifier = this . createIdentifierFromSymbol ( exportedSymbol , exportElement . name ) ;
251- } else {
252- exportedIdentifier = this . transformIdentifier ( exportElement . name ) ;
253- }
254- }
255-
256- result . push (
257- tstl . createAssignmentStatement (
258- this . createExportedIdentifier ( this . transformIdentifier ( exportElement . name ) ) ,
259- exportedIdentifier
260- )
261- ) ;
262- }
263- return result ;
264- }
265-
266249 if ( statement . exportClause ) {
267250 if ( statement . exportClause . elements . some ( e =>
268251 ( e . name !== undefined && e . name . originalKeywordKind === ts . SyntaxKind . DefaultKeyword )
@@ -272,11 +255,40 @@ export class LuaTransformer {
272255 throw TSTLErrors . UnsupportedDefaultExport ( statement ) ;
273256 }
274257
258+ if ( ! this . resolver . isValueAliasDeclaration ( statement ) ) {
259+ return undefined ;
260+ }
261+
262+ const exportSpecifiers = statement . exportClause . elements . filter ( e =>
263+ this . resolver . isValueAliasDeclaration ( e )
264+ ) ;
265+
266+ if ( statement . moduleSpecifier === undefined ) {
267+ return exportSpecifiers . map ( specifier => {
268+ let exportedIdentifier : tstl . Expression | undefined ;
269+ if ( specifier . propertyName !== undefined ) {
270+ exportedIdentifier = this . transformIdentifier ( specifier . propertyName ) ;
271+ } else {
272+ const exportedSymbol = this . checker . getExportSpecifierLocalTargetSymbol ( specifier ) ;
273+ if ( exportedSymbol !== undefined ) {
274+ exportedIdentifier = this . createIdentifierFromSymbol ( exportedSymbol , specifier . name ) ;
275+ } else {
276+ exportedIdentifier = this . transformIdentifier ( specifier . name ) ;
277+ }
278+ }
279+
280+ return tstl . createAssignmentStatement (
281+ this . createExportedIdentifier ( this . transformIdentifier ( specifier . name ) ) ,
282+ exportedIdentifier
283+ ) ;
284+ } ) ;
285+ }
286+
275287 // First transpile as import clause
276288 const importClause = ts . createImportClause (
277289 undefined ,
278- ts . createNamedImports ( statement . exportClause . elements
279- . map ( e => ts . createImportSpecifier ( e . propertyName , e . name ) )
290+ ts . createNamedImports (
291+ exportSpecifiers . map ( s => ts . createImportSpecifier ( s . propertyName , s . name ) )
280292 )
281293 ) ;
282294
@@ -292,18 +304,26 @@ export class LuaTransformer {
292304 const result = this . transformBlock ( block ) . statements ;
293305
294306 // Now the module is imported, add the imports to the export table
295- for ( const exportVariable of statement . exportClause . elements ) {
307+ for ( const specifier of exportSpecifiers ) {
296308 result . push (
297309 tstl . createAssignmentStatement (
298- this . createExportedIdentifier ( this . transformIdentifier ( exportVariable . name ) ) ,
299- this . transformIdentifier ( exportVariable . name )
310+ this . createExportedIdentifier ( this . transformIdentifier ( specifier . name ) ) ,
311+ this . transformIdentifier ( specifier . name )
300312 )
301313 ) ;
302314 }
303315
304316 // Wrap this in a DoStatement to prevent polluting the scope.
305317 return tstl . createDoStatement ( this . filterUndefined ( result ) , statement ) ;
306318 } else {
319+ if ( statement . moduleSpecifier === undefined ) {
320+ throw TSTLErrors . InvalidExportDeclaration ( statement ) ;
321+ }
322+
323+ if ( ! this . resolver . moduleExportsSomeValue ( statement . moduleSpecifier ) ) {
324+ return undefined ;
325+ }
326+
307327 const moduleRequire = this . createModuleRequire ( statement . moduleSpecifier as ts . StringLiteral ) ;
308328 const tempModuleIdentifier = tstl . createIdentifier ( "__TSTL_export" ) ;
309329
@@ -375,7 +395,11 @@ export class LuaTransformer {
375395 if ( ts . isNamedImports ( imports ) ) {
376396 const filteredElements = imports . elements . filter ( e => {
377397 const decorators = tsHelper . getCustomDecorators ( this . checker . getTypeAtLocation ( e ) , this . checker ) ;
378- return ! decorators . has ( DecoratorKind . Extension ) && ! decorators . has ( DecoratorKind . MetaExtension ) ;
398+ return (
399+ this . resolver . isReferencedAliasDeclaration ( e )
400+ && ! decorators . has ( DecoratorKind . Extension )
401+ && ! decorators . has ( DecoratorKind . MetaExtension )
402+ ) ;
379403 } ) ;
380404
381405 // Elide import if all imported types are extension classes
@@ -418,6 +442,10 @@ export class LuaTransformer {
418442 }
419443
420444 } else if ( ts . isNamespaceImport ( imports ) ) {
445+ if ( ! this . resolver . isReferencedAliasDeclaration ( imports ) ) {
446+ return undefined ;
447+ }
448+
421449 const requireStatement = tstl . createVariableDeclarationStatement (
422450 this . transformIdentifier ( imports . name ) ,
423451 requireCall ,
0 commit comments