11import * as path from "path" ;
2- import { Mapping , SourceMapGenerator , SourceNode } from "source-map" ;
2+ import { SourceNode } from "source-map" ;
33import * as ts from "typescript" ;
44import { CompilerOptions , LuaLibImportKind } from "./CompilerOptions" ;
55import * as lua from "./LuaAST" ;
66import { loadLuaLibFeatures , LuaLibFeature } from "./LuaLib" ;
77import { isValidLuaIdentifier } from "./transformation/utils/safe-names" ;
88import { TranspilerHost } from "./transpilation" ;
9- import { assert , intersperse , invertObject , normalizeSlashes , trimExtension } from "./utils" ;
9+ import { assert , intersperse , invertObject , normalizeSlashes } from "./utils" ;
1010
1111// https://www.lua.org/pil/2.4.html
1212// https://www.ecma-international.org/ecma-262/10.0/index.html#table-34
@@ -91,13 +91,7 @@ export type Printer = (
9191 fileName : string ,
9292 block : lua . Block ,
9393 luaLibFeatures : Set < LuaLibFeature >
94- ) => PrintResult ;
95-
96- export interface PrintResult {
97- code : string ;
98- sourceMap : string ;
99- sourceMapNode : SourceNode ;
100- }
94+ ) => SourceNode ;
10195
10296export class LuaPrinter {
10397 private static operatorMap : Record < lua . Operator , string > = {
@@ -129,7 +123,7 @@ export class LuaPrinter {
129123 } ;
130124
131125 private currentIndent = "" ;
132- private sourceFile : string ;
126+ private fileName : string ;
133127 private options : CompilerOptions ;
134128
135129 constructor ( private host : TranspilerHost , program : ts . Program , fileName : string ) {
@@ -139,81 +133,31 @@ export class LuaPrinter {
139133 const relativeFileName = path . relative ( program . getCommonSourceDirectory ( ) , fileName ) ;
140134 if ( this . options . sourceRoot ) {
141135 // When sourceRoot is specified, just use relative path inside rootDir
142- this . sourceFile = relativeFileName ;
136+ this . fileName = relativeFileName ;
143137 } else {
144138 // Calculate relative path from rootDir to outDir
145139 const outputPath = path . resolve ( this . options . outDir , relativeFileName ) ;
146- this . sourceFile = path . relative ( path . dirname ( outputPath ) , fileName ) ;
140+ this . fileName = path . relative ( path . dirname ( outputPath ) , fileName ) ;
147141 }
148142 // We want forward slashes, even in windows
149- this . sourceFile = normalizeSlashes ( this . sourceFile ) ;
143+ this . fileName = normalizeSlashes ( this . fileName ) ;
150144 } else {
151- this . sourceFile = path . basename ( fileName ) ; // File will be in same dir as source
145+ this . fileName = path . basename ( fileName ) ; // File will be in same dir as source
152146 }
153147 }
154148
155- public print ( block : lua . Block , luaLibFeatures : Set < LuaLibFeature > ) : PrintResult {
156- // Add traceback lualib if sourcemap traceback option is enabled
157- if ( this . options . sourceMapTraceback ) {
158- luaLibFeatures . add ( LuaLibFeature . SourceMapTraceBack ) ;
159- }
160-
161- const sourceRoot = this . options . sourceRoot
162- ? // According to spec, sourceRoot is simply prepended to the source name, so the slash should be included
163- this . options . sourceRoot . replace ( / [ \\ / ] + $ / , "" ) + "/"
164- : "" ;
165- const rootSourceNode = this . printImplementation ( block , luaLibFeatures ) ;
166- const sourceMap = this . buildSourceMap ( sourceRoot , rootSourceNode ) ;
167-
168- let code = rootSourceNode . toString ( ) ;
169-
170- if ( this . options . inlineSourceMap ) {
171- code += "\n" + this . printInlineSourceMap ( sourceMap ) ;
172- }
173-
174- if ( this . options . sourceMapTraceback ) {
175- const stackTraceOverride = this . printStackTraceOverride ( rootSourceNode ) ;
176- code = code . replace ( "{#SourceMapTraceback}" , stackTraceOverride ) ;
177- }
178-
179- return { code, sourceMap : sourceMap . toString ( ) , sourceMapNode : rootSourceNode } ;
180- }
181-
182- private printInlineSourceMap ( sourceMap : SourceMapGenerator ) : string {
183- const map = sourceMap . toString ( ) ;
184- const base64Map = Buffer . from ( map ) . toString ( "base64" ) ;
185-
186- return `--# sourceMappingURL=data:application/json;base64,${ base64Map } \n` ;
187- }
188-
189- private printStackTraceOverride ( rootNode : SourceNode ) : string {
190- let currentLine = 1 ;
191- const map : Record < number , number > = { } ;
192- rootNode . walk ( ( chunk , mappedPosition ) => {
193- if ( mappedPosition . line !== undefined && mappedPosition . line > 0 ) {
194- if ( map [ currentLine ] === undefined ) {
195- map [ currentLine ] = mappedPosition . line ;
196- } else {
197- map [ currentLine ] = Math . min ( map [ currentLine ] , mappedPosition . line ) ;
198- }
199- }
200-
201- currentLine += chunk . split ( "\n" ) . length - 1 ;
202- } ) ;
203-
204- const mapItems = Object . entries ( map ) . map ( ( [ line , original ] ) => `["${ line } "] = ${ original } ` ) ;
205- const mapString = "{" + mapItems . join ( "," ) + "}" ;
206-
207- return `__TS__SourceMapTraceBack(debug.getinfo(1).short_src, ${ mapString } );` ;
208- }
209-
210- private printImplementation ( block : lua . Block , luaLibFeatures : Set < LuaLibFeature > ) : SourceNode {
149+ public print ( block : lua . Block , luaLibFeatures : Set < LuaLibFeature > ) : SourceNode {
211150 let header = "" ;
212151
213152 if ( ! this . options . noHeader ) {
214153 header += "--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]\n" ;
215154 }
216155
156+ // Add traceback lualib if sourcemap traceback option is enabled
157+ if ( this . options . sourceMapTraceback ) {
158+ luaLibFeatures . add ( LuaLibFeature . SourceMapTraceBack ) ;
159+ }
160+
217161 const luaLibImport = this . options . luaLibImport ?? LuaLibImportKind . Require ;
218162 if (
219163 luaLibImport === LuaLibImportKind . Always ||
@@ -252,12 +196,12 @@ export class LuaPrinter {
252196 const { line, column } = lua . getOriginalPos ( node ) ;
253197
254198 return line !== undefined && column !== undefined
255- ? new SourceNode ( line + 1 , column , this . sourceFile , chunks , name )
256- : new SourceNode ( null , null , this . sourceFile , chunks , name ) ;
199+ ? new SourceNode ( line + 1 , column , this . fileName , chunks , name )
200+ : new SourceNode ( null , null , this . fileName , chunks , name ) ;
257201 }
258202
259203 protected concatNodes ( ...chunks : SourceChunk [ ] ) : SourceNode {
260- return new SourceNode ( null , null , this . sourceFile , chunks ) ;
204+ return new SourceNode ( null , null , this . fileName , chunks ) ;
261205 }
262206
263207 protected printBlock ( block : lua . Block ) : SourceNode {
@@ -743,7 +687,7 @@ export class LuaPrinter {
743687 }
744688
745689 public printOperator ( kind : lua . Operator ) : SourceNode {
746- return new SourceNode ( null , null , this . sourceFile , LuaPrinter . operatorMap [ kind ] ) ;
690+ return new SourceNode ( null , null , this . fileName , LuaPrinter . operatorMap [ kind ] ) ;
747691 }
748692
749693 protected joinChunksWithComma ( chunks : SourceChunk [ ] ) : SourceChunk [ ] {
@@ -768,67 +712,4 @@ export class LuaPrinter {
768712
769713 return chunks ;
770714 }
771-
772- // The key difference between this and SourceNode.toStringWithSourceMap() is that SourceNodes with null line/column
773- // will not generate 'empty' mappings in the source map that point to nothing in the original TS.
774- private buildSourceMap ( sourceRoot : string , rootSourceNode : SourceNode ) : SourceMapGenerator {
775- const map = new SourceMapGenerator ( {
776- file : trimExtension ( this . sourceFile ) + ".lua" ,
777- sourceRoot,
778- } ) ;
779-
780- let generatedLine = 1 ;
781- let generatedColumn = 0 ;
782- let currentMapping : Mapping | undefined ;
783-
784- const isNewMapping = ( sourceNode : SourceNode ) => {
785- if ( sourceNode . line === null ) {
786- return false ;
787- }
788- if ( currentMapping === undefined ) {
789- return true ;
790- }
791- if (
792- currentMapping . generated . line === generatedLine &&
793- currentMapping . generated . column === generatedColumn &&
794- currentMapping . name === sourceNode . name
795- ) {
796- return false ;
797- }
798- return (
799- currentMapping . original . line !== sourceNode . line ||
800- currentMapping . original . column !== sourceNode . column ||
801- currentMapping . name !== sourceNode . name
802- ) ;
803- } ;
804-
805- const build = ( sourceNode : SourceNode ) => {
806- if ( isNewMapping ( sourceNode ) ) {
807- currentMapping = {
808- source : sourceNode . source ,
809- original : { line : sourceNode . line , column : sourceNode . column } ,
810- generated : { line : generatedLine , column : generatedColumn } ,
811- name : sourceNode . name ,
812- } ;
813- map . addMapping ( currentMapping ) ;
814- }
815-
816- for ( const chunk of sourceNode . children ) {
817- if ( typeof chunk === "string" ) {
818- const lines = ( chunk as string ) . split ( "\n" ) ;
819- if ( lines . length > 1 ) {
820- generatedLine += lines . length - 1 ;
821- generatedColumn = 0 ;
822- currentMapping = undefined ; // Mappings end at newlines
823- }
824- generatedColumn += lines [ lines . length - 1 ] . length ;
825- } else {
826- build ( chunk ) ;
827- }
828- }
829- } ;
830- build ( rootSourceNode ) ;
831-
832- return map ;
833- }
834715}
0 commit comments