diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index 65c33b88a..4bfe53f5c 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -1,11 +1,13 @@ +import * as path from "path"; import { Mapping, SourceMapGenerator, SourceNode } from "source-map"; +import { getEmitPath } from "."; import * as ts from "typescript"; import { CompilerOptions, isBundleEnabled, LuaLibImportKind } from "./CompilerOptions"; import * as lua from "./LuaAST"; import { loadLuaLibFeatures, LuaLibFeature } from "./LuaLib"; import { isValidLuaIdentifier } from "./transformation/utils/safe-names"; import { EmitHost } from "./transpilation"; -import { intersperse, trimExtension } from "./utils"; +import { intersperse, normalizeSlashes } from "./utils"; // https://www.lua.org/pil/2.4.html // https://www.ecma-international.org/ecma-262/10.0/index.html#table-34 @@ -120,14 +122,17 @@ export class LuaPrinter { }; private currentIndent = ""; - private sourceFile: string; + private luaFile: string; + private relativeSourcePath: string; private options: CompilerOptions; public static readonly sourceMapTracebackPlaceholder = "{#SourceMapTraceback}"; - constructor(private emitHost: EmitHost, program: ts.Program, fileName: string) { + constructor(private emitHost: EmitHost, private program: ts.Program, private sourceFile: string) { this.options = program.getCompilerOptions(); - this.sourceFile = fileName; + this.luaFile = normalizeSlashes(getEmitPath(this.sourceFile, this.program)); + // Source nodes contain relative path from mapped lua file to original TS source file + this.relativeSourcePath = normalizeSlashes(path.relative(path.dirname(this.luaFile), this.sourceFile)); } public print(file: lua.File): PrintResult { @@ -230,12 +235,12 @@ export class LuaPrinter { const { line, column } = lua.getOriginalPos(node); return line !== undefined && column !== undefined - ? new SourceNode(line + 1, column, this.sourceFile, chunks, name) - : new SourceNode(null, null, this.sourceFile, chunks, name); + ? new SourceNode(line + 1, column, this.relativeSourcePath, chunks, name) + : new SourceNode(null, null, this.relativeSourcePath, chunks, name); } protected concatNodes(...chunks: SourceChunk[]): SourceNode { - return new SourceNode(null, null, this.sourceFile, chunks); + return new SourceNode(null, null, this.relativeSourcePath, chunks); } protected printBlock(block: lua.Block): SourceNode { @@ -757,7 +762,7 @@ export class LuaPrinter { } public printOperator(kind: lua.Operator): SourceNode { - return new SourceNode(null, null, this.sourceFile, LuaPrinter.operatorMap[kind]); + return new SourceNode(null, null, this.relativeSourcePath, LuaPrinter.operatorMap[kind]); } protected joinChunksWithComma(chunks: SourceChunk[]): SourceChunk[] { @@ -787,7 +792,7 @@ export class LuaPrinter { // will not generate 'empty' mappings in the source map that point to nothing in the original TS. private buildSourceMap(sourceRoot: string, rootSourceNode: SourceNode): SourceMapGenerator { const map = new SourceMapGenerator({ - file: trimExtension(this.sourceFile) + ".lua", + file: path.basename(this.luaFile), sourceRoot, }); diff --git a/src/transpilation/diagnostics.ts b/src/transpilation/diagnostics.ts index 098d78740..0aa772cd3 100644 --- a/src/transpilation/diagnostics.ts +++ b/src/transpilation/diagnostics.ts @@ -8,7 +8,7 @@ const createDiagnosticFactory = ( export const couldNotResolveRequire = createDiagnosticFactory( (requirePath: string, containingFile: string) => - `Could not resolve require path '${requirePath}' in file ${containingFile}.` + `Could not resolve lua source files for require path '${requirePath}' in file ${containingFile}.` ); export const couldNotReadDependency = createDiagnosticFactory( diff --git a/test/unit/modules/__snapshots__/resolution.spec.ts.snap b/test/unit/modules/__snapshots__/resolution.spec.ts.snap index 4db74c57f..a250dbd46 100644 --- a/test/unit/modules/__snapshots__/resolution.spec.ts.snap +++ b/test/unit/modules/__snapshots__/resolution.spec.ts.snap @@ -7,4 +7,4 @@ local ____ = module return ____exports" `; -exports[`doesn't resolve paths out of root dir: diagnostics 1`] = `"error TSTL: Could not resolve require path '../module' in file main.ts."`; +exports[`doesn't resolve paths out of root dir: diagnostics 1`] = `"error TSTL: Could not resolve lua source files for require path '../module' in file main.ts."`; diff --git a/test/unit/printer/sourcemaps.spec.ts b/test/unit/printer/sourcemaps.spec.ts index 6c5857120..fdc21a13a 100644 --- a/test/unit/printer/sourcemaps.spec.ts +++ b/test/unit/printer/sourcemaps.spec.ts @@ -163,25 +163,35 @@ test.each([ }); test.each([ - { fileName: "/proj/foo.ts", config: {} }, + { + fileName: "/proj/foo.ts", + config: {}, + expectedSourcePath: "foo.ts", // ts and lua will be emitted to same directory + }, { fileName: "/proj/src/foo.ts", - config: { outDir: "/proj/dst" }, + config: { + outDir: "/proj/dst", + }, + expectedSourcePath: "../src/foo.ts", // path from proj/dst outDir to proj/src/foo.ts }, { fileName: "/proj/src/foo.ts", config: { rootDir: "/proj/src", outDir: "/proj/dst" }, + expectedSourcePath: "../src/foo.ts", // path from proj/dst outDir to proj/src/foo.ts }, { fileName: "/proj/src/sub/foo.ts", config: { rootDir: "/proj/src", outDir: "/proj/dst" }, + expectedSourcePath: "../../src/sub/foo.ts", // path from proj/dst/sub outDir to proj/src/sub/foo.ts }, { fileName: "/proj/src/sub/main.ts", - config: { rootDir: "/proj/src", outDir: "/proj/dst", sourceRoot: "bin" }, - fullSource: "bin/proj/src/sub/main.ts", + config: { rootDir: "/proj/src", outDir: "/proj/dst", sourceRoot: "bin/binsub/binsubsub" }, + expectedSourcePath: "../../src/sub/main.ts", // path from proj/dst/sub outDir to proj/src/sub/foo.ts + fullSource: "bin/src/sub/main.ts", }, -])("Source map has correct sources (%p)", async ({ fileName, config, fullSource }) => { +])("Source map has correct sources (%p)", async ({ fileName, config, fullSource, expectedSourcePath }) => { const file = util.testModule` const foo = "foo" ` @@ -191,11 +201,11 @@ test.each([ const sourceMap = JSON.parse(file.luaSourceMap); expect(sourceMap.sources).toHaveLength(1); - expect(sourceMap.sources[0]).toBe(fileName); + expect(sourceMap.sources[0]).toBe(expectedSourcePath); const consumer = await new SourceMapConsumer(file.luaSourceMap); expect(consumer.sources).toHaveLength(1); - expect(consumer.sources[0]).toBe(fullSource ?? fileName); + expect(consumer.sources[0]).toBe(fullSource ?? expectedSourcePath); }); test.each([