From 92a2e064d0fede84d2a25926fb7a6cb67b232eb0 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 12 Feb 2023 18:28:21 +0100 Subject: [PATCH 1/2] Correctly handle custom extensions during module resolution --- src/transpilation/output-collector.ts | 6 ++--- src/transpilation/resolve.ts | 30 ++++++++++++++++-------- test/transpile/module-resolution.spec.ts | 8 +++++++ test/util.ts | 11 +++++---- 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/transpilation/output-collector.ts b/src/transpilation/output-collector.ts index 866c1f79a..1458b2403 100644 --- a/src/transpilation/output-collector.ts +++ b/src/transpilation/output-collector.ts @@ -14,7 +14,7 @@ export interface TranspiledFile { jsSourceMap?: string; } -export function createEmitOutputCollector() { +export function createEmitOutputCollector(luaExtension = ".lua") { const files: TranspiledFile[] = []; const writeFile: ts.WriteFileCallback = (fileName, data, _bom, _onError, sourceFiles = []) => { let file = files.find(f => intersection(f.sourceFiles, sourceFiles).length > 0); @@ -25,9 +25,9 @@ export function createEmitOutputCollector() { file.sourceFiles = union(file.sourceFiles, sourceFiles); } - if (fileName.endsWith(".lua")) { + if (fileName.endsWith(luaExtension)) { file.lua = data; - } else if (fileName.endsWith(".lua.map")) { + } else if (fileName.endsWith(`${luaExtension}.map`)) { file.luaSourceMap = data; } else if (fileName.endsWith(".js")) { file.js = data; diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index ad1aa1c84..1a7191f54 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -49,8 +49,8 @@ class ResolutionContext { // Remove @NoResolution prefix if not building in library mode if (!isBuildModeLibrary(this.program)) { const path = required.requirePath.replace("@NoResolution:", ""); - replaceRequireInCode(file, required, path); - replaceRequireInSourceMap(file, required, path); + replaceRequireInCode(file, required, path, this.options.extension); + replaceRequireInSourceMap(file, required, path, this.options.extension); } // Skip @@ -88,8 +88,8 @@ class ResolutionContext { // Figure out resolved require path and dependency output path if (shouldRewriteRequires(dependencyPath, this.program)) { const resolvedRequire = getEmitPathRelativeToOutDir(dependencyPath, this.program); - replaceRequireInCode(file, required, resolvedRequire); - replaceRequireInSourceMap(file, required, resolvedRequire); + replaceRequireInCode(file, required, resolvedRequire, this.options.extension); + replaceRequireInSourceMap(file, required, resolvedRequire, this.options.extension); } } @@ -117,8 +117,8 @@ class ResolutionContext { private couldNotResolveImport(required: LuaRequire, file: ProcessedFile): void { const fallbackRequire = fallbackResolve(required, getSourceDir(this.program), path.dirname(file.fileName)); - replaceRequireInCode(file, required, fallbackRequire); - replaceRequireInSourceMap(file, required, fallbackRequire); + replaceRequireInCode(file, required, fallbackRequire, this.options.extension); + replaceRequireInSourceMap(file, required, fallbackRequire, this.options.extension); this.diagnostics.push( couldNotResolveRequire(required.requirePath, path.relative(getProjectRoot(this.program), file.fileName)) @@ -310,16 +310,26 @@ function isBuildModeLibrary(program: ts.Program) { return program.getCompilerOptions().buildMode === BuildMode.Library; } -function replaceRequireInCode(file: ProcessedFile, originalRequire: LuaRequire, newRequire: string): void { - const requirePath = formatPathToLuaPath(newRequire.replace(".lua", "")); +function replaceRequireInCode( + file: ProcessedFile, + originalRequire: LuaRequire, + newRequire: string, + extension = ".lua" +): void { + const requirePath = formatPathToLuaPath(newRequire.substring(0, newRequire.length - extension.length)); file.code = file.code = file.code.substring(0, originalRequire.from) + `require("${requirePath}")` + file.code.substring(originalRequire.to + 1); } -function replaceRequireInSourceMap(file: ProcessedFile, originalRequire: LuaRequire, newRequire: string): void { - const requirePath = formatPathToLuaPath(newRequire.replace(".lua", "")); +function replaceRequireInSourceMap( + file: ProcessedFile, + originalRequire: LuaRequire, + newRequire: string, + extension = ".lua" +): void { + const requirePath = formatPathToLuaPath(newRequire.substring(0, newRequire.length - extension.length)); if (file.sourceMapNode) { replaceInSourceMap( file.sourceMapNode, diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index bde9e434d..f95771687 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -174,6 +174,14 @@ describe("module resolution with sourceDir", () => { .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) .expectToEqual(expectedResult); }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1394 + test("can resolve files with non-standard extension (#1394)", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "src", "main.ts")) + .setOptions({ outDir: "tstl-out", extension: ".script" }) + .expectToEqual(expectedResult); + }); }); describe("module resolution project with lua sources", () => { diff --git a/test/util.ts b/test/util.ts index 8650f88ca..ed2e2686e 100644 --- a/test/util.ts +++ b/test/util.ts @@ -236,7 +236,7 @@ export abstract class TestBuilder { public getLuaResult(): tstl.TranspileVirtualProjectResult { const program = this.getProgram(); const preEmitDiagnostics = ts.getPreEmitDiagnostics(program); - const collector = createEmitOutputCollector(); + const collector = createEmitOutputCollector(this.options.extension); const { diagnostics: transpileDiagnostics } = new tstl.Transpiler({ emitHost: this.getEmitHost() }).emit({ program, customTransformers: this.customTransformers, @@ -277,7 +277,7 @@ export abstract class TestBuilder { const program = this.getProgram(); program.getCompilerOptions().module = ts.ModuleKind.CommonJS; - const collector = createEmitOutputCollector(); + const collector = createEmitOutputCollector(this.options.extension); const { diagnostics } = program.emit(undefined, collector.writeFile); return { transpiledFiles: collector.files, diagnostics: [...diagnostics] }; } @@ -468,7 +468,10 @@ end)());`; } private injectLuaFile(state: LuaState, lua: Lua, lauxlib: LauxLib, fileName: string, fileContent: string) { - const modName = formatPathToLuaPath(fileName.replace(".lua", "")); + const extension = this.options.extension ?? ".lua"; + const modName = fileName.includes(".") + ? formatPathToLuaPath(fileName.substring(0, fileName.length - extension.length)) + : fileName; if (this.options.luaTarget === tstl.LuaTarget.Lua50) { // Adding source Lua to the _LOADED cache will allow require to find it lua.lua_getglobal(state, "_LOADED"); @@ -610,7 +613,7 @@ class ProjectTestBuilder extends ModuleTestBuilder { @memoize public getLuaResult(): tstl.TranspileVirtualProjectResult { // Override getLuaResult to use transpileProject with tsconfig.json instead - const collector = createEmitOutputCollector(); + const collector = createEmitOutputCollector(this.options.extension); const { diagnostics } = transpileProject(this.tsConfig, this.options, collector.writeFile); return { diagnostics: [...diagnostics], transpiledFiles: collector.files }; From f43713359581bfadc552b546d6f5d3dae3f32ee3 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 12 Feb 2023 21:26:46 +0100 Subject: [PATCH 2/2] Fix tests --- src/transpilation/resolve.ts | 16 ++++++++++++---- test/util.ts | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index 1a7191f54..558870f21 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -314,9 +314,9 @@ function replaceRequireInCode( file: ProcessedFile, originalRequire: LuaRequire, newRequire: string, - extension = ".lua" + extension: string | undefined ): void { - const requirePath = formatPathToLuaPath(newRequire.substring(0, newRequire.length - extension.length)); + const requirePath = requirePathForFile(newRequire, extension); file.code = file.code = file.code.substring(0, originalRequire.from) + `require("${requirePath}")` + @@ -327,9 +327,9 @@ function replaceRequireInSourceMap( file: ProcessedFile, originalRequire: LuaRequire, newRequire: string, - extension = ".lua" + extension?: string | undefined ): void { - const requirePath = formatPathToLuaPath(newRequire.substring(0, newRequire.length - extension.length)); + const requirePath = requirePathForFile(newRequire, extension); if (file.sourceMapNode) { replaceInSourceMap( file.sourceMapNode, @@ -340,6 +340,14 @@ function replaceRequireInSourceMap( } } +function requirePathForFile(filePath: string, extension = ".lua"): string { + if (filePath.endsWith(extension)) { + return formatPathToLuaPath(filePath.substring(0, filePath.length - extension.length)); + } else { + return formatPathToLuaPath(filePath); + } +} + function replaceInSourceMap(node: SourceNode, parent: SourceNode, require: string, resolvedRequire: string): boolean { if ((!node.children || node.children.length === 0) && node.toString() === require) { parent.children = [new SourceNode(node.line, node.column, node.source, [resolvedRequire])]; diff --git a/test/util.ts b/test/util.ts index ed2e2686e..c86d40453 100644 --- a/test/util.ts +++ b/test/util.ts @@ -469,7 +469,7 @@ end)());`; private injectLuaFile(state: LuaState, lua: Lua, lauxlib: LauxLib, fileName: string, fileContent: string) { const extension = this.options.extension ?? ".lua"; - const modName = fileName.includes(".") + const modName = fileName.endsWith(extension) ? formatPathToLuaPath(fileName.substring(0, fileName.length - extension.length)) : fileName; if (this.options.luaTarget === tstl.LuaTarget.Lua50) {