From 006f706c8697fbeb636a3f9f38e5fdaa854bfbdf Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 17 Apr 2021 18:46:54 +0200 Subject: [PATCH 01/34] Create test project with node_modules --- .eslintignore | 1 + .../lua-global-without-decls.d.ts | 4 ++ .../lua-module-without-decls.d.ts | 9 +++++ .../project-with-node-modules/main.ts | 37 +++++++++++++++++++ .../lua-global-with-decls/baz.d.ts | 1 + .../lua-global-with-decls/baz.lua | 3 ++ .../lua-global-with-decls/index.d.ts | 3 ++ .../lua-global-with-decls/index.lua | 6 +++ .../lua-global-without-decls/baz.lua | 3 ++ .../lua-global-without-decls/index.lua | 6 +++ .../lua-module-with-decls/baz.d.ts | 2 + .../lua-module-with-decls/baz.lua | 5 +++ .../lua-module-with-decls/index.d.ts | 3 ++ .../lua-module-with-decls/index.lua | 8 ++++ .../lua-module-with-dependency/index.d.ts | 2 + .../lua-module-with-dependency/index.lua | 7 ++++ .../lua-module-without-decls/baz.lua | 5 +++ .../lua-module-without-decls/index.lua | 8 ++++ .../project-with-node-modules/tsconfig.json | 12 ++++++ 19 files changed, 125 insertions(+) create mode 100644 test/transpile/module-resolution/project-with-node-modules/lua-global-without-decls.d.ts create mode 100644 test/transpile/module-resolution/project-with-node-modules/lua-module-without-decls.d.ts create mode 100644 test/transpile/module-resolution/project-with-node-modules/main.ts create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/baz.d.ts create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/baz.lua create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/index.d.ts create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/index.lua create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/baz.d.ts create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/baz.lua create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.d.ts create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.lua create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-dependency/index.d.ts create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-dependency/index.lua create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/baz.lua create mode 100644 test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/index.lua create mode 100644 test/transpile/module-resolution/project-with-node-modules/tsconfig.json diff --git a/.eslintignore b/.eslintignore index 707cb8140..f0ab43bbf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,4 +3,5 @@ /test/cli/errors /test/cli/watch /test/transpile/directories +/test/transpile/module-resolution/node_modules /test/transpile/outFile diff --git a/test/transpile/module-resolution/project-with-node-modules/lua-global-without-decls.d.ts b/test/transpile/module-resolution/project-with-node-modules/lua-global-without-decls.d.ts new file mode 100644 index 000000000..26774ee25 --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/lua-global-without-decls.d.ts @@ -0,0 +1,4 @@ +/** @noSelfInFile */ +declare function fooGlobalWithoutDecls(): string; +declare function barGlobalWithoutDecls(param: string): string; +declare function bazGlobalWithoutDecls(): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/lua-module-without-decls.d.ts b/test/transpile/module-resolution/project-with-node-modules/lua-module-without-decls.d.ts new file mode 100644 index 000000000..07295d96b --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/lua-module-without-decls.d.ts @@ -0,0 +1,9 @@ +/** @noSelfInFile */ +declare module "lua-module-without-decls" { + function foo(this: void): string; + function bar(this: void, param: string): string; +} + +declare module "lua-module-without-decls/baz" { + function baz(this: void): string; +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/main.ts b/test/transpile/module-resolution/project-with-node-modules/main.ts new file mode 100644 index 000000000..0f6a14e7c --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/main.ts @@ -0,0 +1,37 @@ +import "lua-global-with-decls"; +import "lua-global-with-decls/baz"; + +import "lua-global-without-decls"; +import "lua-global-without-decls/baz"; + +import * as moduleWithDeclarations from "lua-module-with-decls"; +import * as moduleWithDeclarationsBaz from "lua-module-with-decls/baz"; + +import * as moduleWithoutDeclarations from "lua-module-without-decls"; +import * as moduleWithoutDeclarationsBaz from "lua-module-without-decls/baz"; + +import * as moduleWithDependency from "lua-module-with-dependency"; + +export const testResult = [ + fooGlobal(), + barGlobal("global with declarations!"), + bazGlobal(), + + fooGlobalWithoutDecls(), + barGlobalWithoutDecls("global without declarations!"), + bazGlobalWithoutDecls(), + + moduleWithDeclarations.foo(), + moduleWithDeclarations.bar("module with declarations!"), + moduleWithDeclarationsBaz.baz(), + + moduleWithoutDeclarations.foo(), + moduleWithoutDeclarations.bar("module without declarations!"), + moduleWithoutDeclarationsBaz.baz(), + + moduleWithDependency.callDependency() +]; + +export function sup() { + return 3; +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/baz.d.ts b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/baz.d.ts new file mode 100644 index 000000000..23f55b1ad --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/baz.d.ts @@ -0,0 +1 @@ +declare function bazGlobal(): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/baz.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/baz.lua new file mode 100644 index 000000000..952807f1a --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/baz.lua @@ -0,0 +1,3 @@ +function bazGlobal() + return "baz from lua global with decls" +end \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/index.d.ts b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/index.d.ts new file mode 100644 index 000000000..1339056b5 --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/index.d.ts @@ -0,0 +1,3 @@ +/** @noSelfInFile */ +declare function fooGlobal(): string; +declare function barGlobal(param: string): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/index.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/index.lua new file mode 100644 index 000000000..d10edd189 --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-with-decls/index.lua @@ -0,0 +1,6 @@ +function fooGlobal() + return "foo from lua global with decls" +end +function barGlobal(param) + return "bar from lua global with decls: " .. param +end \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua new file mode 100644 index 000000000..952807f1a --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua @@ -0,0 +1,3 @@ +function bazGlobal() + return "baz from lua global with decls" +end \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua new file mode 100644 index 000000000..dc55e6071 --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua @@ -0,0 +1,6 @@ +function fooGlobalWithoutDecl() + return "foo from lua global with decls" +end +function barGlobalWithDecl(param) + return "bar from lua global with decls: " .. param +end \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/baz.d.ts b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/baz.d.ts new file mode 100644 index 000000000..5f46a6ce2 --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/baz.d.ts @@ -0,0 +1,2 @@ +/** @noSelfInFile */ +export declare function baz(): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/baz.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/baz.lua new file mode 100644 index 000000000..f4e16a0d7 --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/baz.lua @@ -0,0 +1,5 @@ +return { + baz = function() + return "baz from lua module with decls" + end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.d.ts b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.d.ts new file mode 100644 index 000000000..aa83ce4a6 --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.d.ts @@ -0,0 +1,3 @@ +/** @noSelfInFile */ +export declare function foo(): string; +export declare function bar(param: string): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.lua new file mode 100644 index 000000000..057552b13 --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.lua @@ -0,0 +1,8 @@ +return { + foo = function() + return "foo from lua module with decls" + end + bar = function(param) + return "bar from lua module with decls: " .. param + end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-dependency/index.d.ts b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-dependency/index.d.ts new file mode 100644 index 000000000..381596acd --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-dependency/index.d.ts @@ -0,0 +1,2 @@ +/** @noSelf */ +export declare function callDependency(): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-dependency/index.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-dependency/index.lua new file mode 100644 index 000000000..9bc475e5b --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-dependency/index.lua @@ -0,0 +1,7 @@ +local dependency = require("lua-module-with-decls") + +return { + callDependency = function() + return "Calling dependency: " .. dependency.foo() + end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/baz.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/baz.lua new file mode 100644 index 000000000..7358f341b --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/baz.lua @@ -0,0 +1,5 @@ +return { + baz = function() + return "baz from lua module without decls" + end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/index.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/index.lua new file mode 100644 index 000000000..9055b3335 --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/index.lua @@ -0,0 +1,8 @@ +return { + foo = function() + return "foo from lua module without decls" + end + bar = function(param) + return "bar from lua module without decls: " .. param + end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/tsconfig.json b/test/transpile/module-resolution/project-with-node-modules/tsconfig.json new file mode 100644 index 000000000..935b64af6 --- /dev/null +++ b/test/transpile/module-resolution/project-with-node-modules/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "strict": true, + "moduleResolution": "Node", + "noUnusedLocals": true, + "noUnusedParameters": true, + "target": "esnext", + "lib": ["esnext"], + "types": [], + "rootDir": "." + } +} From 9fd7b5232d64ad2ece0a469b901710aa5f894eb3 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Thu, 13 May 2021 14:56:54 +0200 Subject: [PATCH 02/34] testProject test util --- test/transpile/module-resolution.spec.ts | 11 +++++++++ .../project-with-node-modules/main.ts | 4 ---- test/util.ts | 24 ++++++++++++++++--- 3 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 test/transpile/module-resolution.spec.ts diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts new file mode 100644 index 000000000..708629774 --- /dev/null +++ b/test/transpile/module-resolution.spec.ts @@ -0,0 +1,11 @@ +import * as path from "path"; +import * as util from "../util"; + +const projectPath = path.resolve(__dirname, "module-resolution", "project-with-node-modules"); + +test("moduleResolution", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")) + .debug() + .expectToEqual({}); +}) \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/main.ts b/test/transpile/module-resolution/project-with-node-modules/main.ts index 0f6a14e7c..f9f8b448a 100644 --- a/test/transpile/module-resolution/project-with-node-modules/main.ts +++ b/test/transpile/module-resolution/project-with-node-modules/main.ts @@ -31,7 +31,3 @@ export const testResult = [ moduleWithDependency.callDependency() ]; - -export function sup() { - return 3; -} \ No newline at end of file diff --git a/test/util.ts b/test/util.ts index b553ef359..aa4f76259 100644 --- a/test/util.ts +++ b/test/util.ts @@ -9,6 +9,7 @@ import * as ts from "typescript"; import * as vm from "vm"; import * as tstl from "../src"; import { createEmitOutputCollector } from "../src/transpilation/output-collector"; +import { transpileProject } from "../src"; const jsonLib = fs.readFileSync(path.join(__dirname, "json.lua"), "utf8"); const luaLib = fs.readFileSync(path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"), "utf8"); @@ -128,7 +129,7 @@ export abstract class TestBuilder { return this; } - private options: tstl.CompilerOptions = { + protected options: tstl.CompilerOptions = { luaTarget: tstl.LuaTarget.Lua54, noHeader: true, skipLibCheck: true, @@ -148,7 +149,7 @@ export abstract class TestBuilder { protected mainFileName = "main.ts"; public setMainFileName(mainFileName: string): this { expect(this.hasProgram).toBe(false); - this.mainFileName = mainFileName; + this.mainFileName = path.normalize(mainFileName); return this; } @@ -202,7 +203,8 @@ export abstract class TestBuilder { const { transpiledFiles } = this.getLuaResult(); const mainFile = this.options.luaBundle ? transpiledFiles[0] - : transpiledFiles.find(({ sourceFiles }) => sourceFiles.some(f => f.fileName === this.mainFileName)); + : transpiledFiles.find(({ sourceFiles }) => sourceFiles.some(f => path.normalize(f.fileName) === this.mainFileName)); + expect(mainFile).toMatchObject({ lua: expect.any(String), luaSourceMap: expect.any(String) }); return mainFile as ExecutableTranspiledFile; } @@ -539,6 +541,21 @@ class ExpressionTestBuilder extends AccessorTestBuilder { } } +class ProjectTestBuilder extends ModuleTestBuilder { + constructor(private tsConfig: string) { + super(""); + } + + @memoize + public getLuaResult(): tstl.TranspileVirtualProjectResult { + // Override getLuaResult to use transpileProject with tsconfig.json instead + const collector = createEmitOutputCollector(); + const { diagnostics } = transpileProject(this.tsConfig, this.options, collector.writeFile); + + return { diagnostics: [...diagnostics], transpiledFiles: collector.files }; + } +} + const createTestBuilderFactory = ( builder: new (_tsCode: string) => T, serializeSubstitutions: boolean @@ -566,3 +583,4 @@ export const testFunction = createTestBuilderFactory(FunctionTestBuilder, false) export const testFunctionTemplate = createTestBuilderFactory(FunctionTestBuilder, true); export const testExpression = createTestBuilderFactory(ExpressionTestBuilder, false); export const testExpressionTemplate = createTestBuilderFactory(ExpressionTestBuilder, true); +export const testProject = createTestBuilderFactory(ProjectTestBuilder, false); From 07e5a78d78f36aabd780eea94cdedd5b574c3666 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Fri, 14 May 2021 13:17:52 +0200 Subject: [PATCH 03/34] Fix typos in module resolution test --- .../node_modules/lua-global-without-decls/baz.lua | 2 +- .../node_modules/lua-global-without-decls/index.lua | 4 ++-- .../node_modules/lua-module-with-decls/index.lua | 2 +- .../node_modules/lua-module-without-decls/index.lua | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua index 952807f1a..300a3e3b2 100644 --- a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua @@ -1,3 +1,3 @@ -function bazGlobal() +function bazGlobalWithoutDecls() return "baz from lua global with decls" end \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua index dc55e6071..f8f9723ce 100644 --- a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua @@ -1,6 +1,6 @@ -function fooGlobalWithoutDecl() +function fooGlobalWithoutDecls() return "foo from lua global with decls" end -function barGlobalWithDecl(param) +function barGlobalWithoutDecls(param) return "bar from lua global with decls: " .. param end \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.lua index 057552b13..0c5d47fe6 100644 --- a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.lua +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-with-decls/index.lua @@ -1,7 +1,7 @@ return { foo = function() return "foo from lua module with decls" - end + end, bar = function(param) return "bar from lua module with decls: " .. param end diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/index.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/index.lua index 9055b3335..e73581589 100644 --- a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/index.lua +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-module-without-decls/index.lua @@ -1,7 +1,7 @@ return { foo = function() return "foo from lua module without decls" - end + end, bar = function(param) return "bar from lua module without decls: " .. param end From 3ff3b49d3c48e52ced76a8b0d28da9b0d1a5d950 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Fri, 14 May 2021 23:42:28 +0200 Subject: [PATCH 04/34] Base case module resolution --- package-lock.json | 353 +++--------------- package.json | 1 + src/LuaPrinter.ts | 1 + src/transpilation/index.ts | 2 +- src/transpilation/output-collector.ts | 3 +- src/transpilation/resolve.ts | 79 ++++ src/transpilation/transpiler.ts | 6 +- test/transpile/module-resolution.spec.ts | 52 ++- .../lua-global-without-decls.d.ts | 2 +- .../lua-module-without-decls.d.ts | 2 +- .../project-with-node-modules/main.ts | 44 ++- .../lua-global-without-decls/baz.lua | 2 +- .../lua-global-without-decls/index.lua | 4 +- test/util.ts | 54 +-- 14 files changed, 239 insertions(+), 366 deletions(-) create mode 100644 src/transpilation/resolve.ts diff --git a/package-lock.json b/package-lock.json index 683dd5035..c68c95c7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.39.2", "license": "MIT", "dependencies": { + "enhanced-resolve": "^5.8.2", "resolve": "^1.15.1", "source-map": "^0.7.3", "typescript": ">=4.0.2" @@ -905,12 +906,6 @@ "node": ">= 10.14.2" } }, - "node_modules/@jest/core/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/@jest/core/node_modules/jest-diff": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", @@ -1307,12 +1302,6 @@ "node": ">= 10.14.2" } }, - "node_modules/@jest/globals/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/@jest/globals/node_modules/jest-diff": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", @@ -1542,12 +1531,6 @@ "node": ">=8" } }, - "node_modules/@jest/reporters/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/@jest/reporters/node_modules/jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -1699,12 +1682,6 @@ "node": ">= 10.14.2" } }, - "node_modules/@jest/source-map/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/@jest/source-map/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1828,12 +1805,6 @@ "node": ">=8" } }, - "node_modules/@jest/test-sequencer/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/@jest/test-sequencer/node_modules/jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -1935,12 +1906,6 @@ "node": ">=10" } }, - "node_modules/@jest/transform/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/@jest/transform/node_modules/jest-regex-util": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", @@ -2743,12 +2708,6 @@ "node": ">=10" } }, - "node_modules/babel-jest/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/babel-plugin-istanbul": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", @@ -3505,6 +3464,18 @@ "once": "^1.4.0" } }, + "node_modules/enhanced-resolve": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", + "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4578,12 +4549,6 @@ "node": ">=6 <7 || >=8" } }, - "node_modules/fs-extra/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4737,10 +4702,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, "node_modules/growly": { "version": "1.3.0", @@ -4865,9 +4829,9 @@ } }, "node_modules/hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "node_modules/html-encoding-sniffer": { @@ -5674,12 +5638,6 @@ "node": ">=10" } }, - "node_modules/jest-config/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-config/node_modules/jest-get-type": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", @@ -5976,12 +5934,6 @@ "node": ">=8" } }, - "node_modules/jest-environment-jsdom/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-environment-jsdom/node_modules/jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -6124,12 +6076,6 @@ "node": ">=8" } }, - "node_modules/jest-environment-node/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-environment-node/node_modules/jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -6253,12 +6199,6 @@ "node": ">=10" } }, - "node_modules/jest-haste-map/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-haste-map/node_modules/jest-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.0.1.tgz", @@ -6448,12 +6388,6 @@ "node": ">= 10.14.2" } }, - "node_modules/jest-jasmine2/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-jasmine2/node_modules/jest-diff": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", @@ -6825,12 +6759,6 @@ "node": ">= 8.3" } }, - "node_modules/jest-message-util/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-mock": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.5.0.tgz", @@ -6979,12 +6907,6 @@ "node": ">= 10.14.2" } }, - "node_modules/jest-resolve-dependencies/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-resolve-dependencies/node_modules/jest-diff": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", @@ -7229,12 +7151,6 @@ "node": ">= 8.3" } }, - "node_modules/jest-resolve/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-resolve/node_modules/parse-json": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", @@ -7425,12 +7341,6 @@ "node": ">=8" } }, - "node_modules/jest-runner/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-runner/node_modules/jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -7741,12 +7651,6 @@ "node": ">= 10.14.2" } }, - "node_modules/jest-runtime/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-runtime/node_modules/jest-diff": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", @@ -8000,12 +7904,6 @@ "node": ">= 10.14.2" } }, - "node_modules/jest-serializer/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-snapshot": { "version": "25.5.1", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.5.1.tgz", @@ -8056,12 +7954,6 @@ "node": ">= 8.3" } }, - "node_modules/jest-snapshot/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-snapshot/node_modules/jest-diff": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", @@ -8156,12 +8048,6 @@ "node": ">= 8.3" } }, - "node_modules/jest-util/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-validate": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.0.1.tgz", @@ -8325,12 +8211,6 @@ "node": ">=8" } }, - "node_modules/jest-watcher/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest-watcher/node_modules/jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -8459,12 +8339,6 @@ "node": ">=8" } }, - "node_modules/jest/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/jest/node_modules/jest-cli": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.0.1.tgz", @@ -10793,6 +10667,14 @@ "node": ">=6" } }, + "node_modules/tapable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", + "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", + "engines": { + "node": ">=6" + } + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -11001,12 +10883,6 @@ "node": ">= 8.3" } }, - "node_modules/ts-jest/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "node_modules/ts-jest/node_modules/jest-diff": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", @@ -12382,12 +12258,6 @@ "jest-regex-util": "^26.0.0" } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-diff": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", @@ -12708,12 +12578,6 @@ "jest-regex-util": "^26.0.0" } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-diff": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", @@ -12897,12 +12761,6 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -13028,12 +12886,6 @@ "source-map": "^0.6.0" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -13134,12 +12986,6 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -13225,12 +13071,6 @@ "supports-color": "^7.1.0" } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-regex-util": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", @@ -13903,12 +13743,6 @@ "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true } } }, @@ -14543,6 +14377,15 @@ "once": "^1.4.0" } }, + "enhanced-resolve": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", + "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -15409,14 +15252,6 @@ "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - } } }, "fs.realpath": { @@ -15538,10 +15373,9 @@ } }, "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, "growly": { "version": "1.3.0", @@ -15640,9 +15474,9 @@ } }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "html-encoding-sniffer": { @@ -16140,12 +15974,6 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-cli": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.0.1.tgz", @@ -16396,12 +16224,6 @@ "supports-color": "^7.1.0" } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-get-type": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", @@ -16641,12 +16463,6 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -16761,12 +16577,6 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -16865,12 +16675,6 @@ "supports-color": "^7.1.0" } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.0.1.tgz", @@ -17023,12 +16827,6 @@ "jest-regex-util": "^26.0.0" } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-diff": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", @@ -17327,12 +17125,6 @@ "@types/yargs": "^15.0.0", "chalk": "^3.0.0" } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true } } }, @@ -17400,12 +17192,6 @@ "chalk": "^3.0.0" } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "parse-json": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", @@ -17525,12 +17311,6 @@ "jest-regex-util": "^26.0.0" } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-diff": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", @@ -17820,12 +17600,6 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -18080,12 +17854,6 @@ "jest-regex-util": "^26.0.0" } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-diff": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", @@ -18287,14 +18055,6 @@ "dev": true, "requires": { "graceful-fs": "^4.2.4" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - } } }, "jest-snapshot": { @@ -18338,12 +18098,6 @@ "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", "dev": true }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-diff": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", @@ -18418,12 +18172,6 @@ "@types/yargs": "^15.0.0", "chalk": "^3.0.0" } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true } } }, @@ -18556,12 +18304,6 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-message-util": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", @@ -20429,6 +20171,11 @@ } } }, + "tapable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", + "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==" + }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -20597,12 +20344,6 @@ "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", "dev": true }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "jest-diff": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", diff --git a/package.json b/package.json index 790013fa5..019a72dfe 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "node": ">=12.13.0" }, "dependencies": { + "enhanced-resolve": "^5.8.2", "resolve": "^1.15.1", "source-map": "^0.7.3", "typescript": ">=4.0.2" diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index b1cfb7f46..780c1ad89 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -125,6 +125,7 @@ export class LuaPrinter { constructor(private emitHost: EmitHost, program: ts.Program, fileName: string) { this.options = program.getCompilerOptions(); + // TODO remove? if (this.options.outDir) { const relativeFileName = path.relative(program.getCommonSourceDirectory(), fileName); if (this.options.sourceRoot) { diff --git a/src/transpilation/index.ts b/src/transpilation/index.ts index 608b42818..59e83233a 100644 --- a/src/transpilation/index.ts +++ b/src/transpilation/index.ts @@ -45,7 +45,7 @@ const libCache: { [key: string]: ts.SourceFile } = {}; /** @internal */ export function createVirtualProgram(input: Record, options: CompilerOptions = {}): ts.Program { const compilerHost: ts.CompilerHost = { - fileExists: () => true, + fileExists: fileName => fileName in input || ts.sys.fileExists(fileName), getCanonicalFileName: fileName => fileName, getCurrentDirectory: () => "", getDefaultLibFileName: ts.getDefaultLibFileName, diff --git a/src/transpilation/output-collector.ts b/src/transpilation/output-collector.ts index d18137c5a..866c1f79a 100644 --- a/src/transpilation/output-collector.ts +++ b/src/transpilation/output-collector.ts @@ -2,6 +2,7 @@ import * as ts from "typescript"; import { intersection, union } from "../utils"; export interface TranspiledFile { + outPath: string; sourceFiles: ts.SourceFile[]; lua?: string; luaSourceMap?: string; @@ -18,7 +19,7 @@ export function createEmitOutputCollector() { const writeFile: ts.WriteFileCallback = (fileName, data, _bom, _onError, sourceFiles = []) => { let file = files.find(f => intersection(f.sourceFiles, sourceFiles).length > 0); if (!file) { - file = { sourceFiles: [...sourceFiles] }; + file = { outPath: fileName, sourceFiles: [...sourceFiles] }; files.push(file); } else { file.sourceFiles = union(file.sourceFiles, sourceFiles); diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts new file mode 100644 index 000000000..a8e50bc55 --- /dev/null +++ b/src/transpilation/resolve.ts @@ -0,0 +1,79 @@ +import * as path from "path"; +import * as resolve from "enhanced-resolve"; +import * as ts from "typescript"; +import * as fs from "fs"; +import { EmitHost, ProcessedFile } from "./utils"; + +const resolver = resolve.ResolverFactory.createResolver({ + extensions: [".lua", ".ts"], + fileSystem: { ...new resolve.CachedInputFileSystem(fs) }, + useSyncFileSystemCalls: true, +}); + +export function resolveDependencies(program: ts.Program, files: ProcessedFile[], emitHost: EmitHost): ProcessedFile[] { + const outFiles = []; + + for (const file of files) { + outFiles.push(file, ...resolveFileDependencies(file, program.getCommonSourceDirectory(), emitHost)); + } + + return outFiles; +} + +function resolveFileDependencies(file: ProcessedFile, rootDir: string, emitHost: EmitHost): ProcessedFile[] { + const fileDir = path.dirname(file.fileName); + const dependencies: ProcessedFile[] = []; + for (const required of findRequiredPaths(file.code)) { + const resolvedDependency = resolveDependency(fileDir, required); + if (resolvedDependency) { + const dependencyContent = emitHost.readFile(resolvedDependency); + if (dependencyContent === undefined) { + throw `TODO: FAILED TO READ ${resolvedDependency}`; + } + + let relativePath = path.relative(fileDir, resolvedDependency); + let outPath = resolvedDependency; + if (relativePath.includes("..")) { + relativePath = path.relative(rootDir, resolvedDependency); + outPath = path.join(fileDir, relativePath); + } + const requirePath = relativePath.replace(".lua", "").replace(/\\/g, "."); + file.code = file.code.replace(`require("${required}")`, `require("${requirePath}")`); + + const dependency = { + fileName: outPath, + code: dependencyContent, + }; + + dependencies.push(dependency, ...resolveFileDependencies(dependency, rootDir, emitHost)); + } else { + //throw `TODO: COULD NOT RESOLVE ${required}`; + } + } + return dependencies; +} + +function findRequiredPaths(code: string): string[] { + const paths: string[] = []; + const pattern = /require\("(.+)"\)/g; + // eslint-disable-next-line @typescript-eslint/ban-types + let match: RegExpExecArray | null; + while ((match = pattern.exec(code))) { + paths.push(match[1]); + } + + return paths; +} + +function resolveDependency(fromDirectory: string, dependency: string): string | undefined { + try { + const resolveResult = resolver.resolveSync({}, fromDirectory, dependency.replace(".", "/")); + if (resolveResult) { + return resolveResult; + } + } catch { + // TODO + } + + return undefined; +} diff --git a/src/transpilation/transpiler.ts b/src/transpilation/transpiler.ts index 205b6b2db..b332ce28f 100644 --- a/src/transpilation/transpiler.ts +++ b/src/transpilation/transpiler.ts @@ -4,6 +4,7 @@ import { isBundleEnabled } from "../CompilerOptions"; import { getLuaLibBundle } from "../LuaLib"; import { normalizeSlashes, trimExtension } from "../utils"; import { getBundleResult } from "./bundle"; +import { resolveDependencies } from "./resolve"; import { getProgramTranspileResult, TranspileOptions } from "./transpile"; import { EmitFile, EmitHost, ProcessedFile } from "./utils"; @@ -33,7 +34,10 @@ export class Transpiler { writeFile, emitOptions ); - const { emitPlan } = this.getEmitPlan(program, diagnostics, freshFiles); + + const resolvedFiles = resolveDependencies(program, freshFiles, this.emitHost); + + const { emitPlan } = this.getEmitPlan(program, diagnostics, resolvedFiles); const options = program.getCompilerOptions(); const emitBOM = options.emitBOM ?? false; diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 708629774..aaefe554d 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -3,9 +3,49 @@ import * as util from "../util"; const projectPath = path.resolve(__dirname, "module-resolution", "project-with-node-modules"); -test("moduleResolution", () => { - util.testProject(path.join(projectPath, "tsconfig.json")) - .setMainFileName(path.join(projectPath, "main.ts")) - .debug() - .expectToEqual({}); -}) \ No newline at end of file +const projectWithNodeModules = util + .testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")); + +test("can resolve global dependencies with declarations", () => { + // Declarations in the node_modules directory + expect(projectWithNodeModules.getLuaExecutionResult().globalWithDeclarationsResults).toEqual({ + foo: "foo from lua global with decls", + bar: "bar from lua global with decls: global with declarations!", + baz: "baz from lua global with decls", + }); +}); + +test("can resolve global dependencies with hand-written declarations", () => { + // No declarations in the node_modules directory, but written by hand in project dir + expect(projectWithNodeModules.getLuaExecutionResult().globalWithoutDeclarationsResults).toEqual({ + foo: "foo from lua global without decls", + bar: "bar from lua global without decls: global without declarations!", + baz: "baz from lua global without decls", + }); +}); + +test("can resolve module dependencies with declarations", () => { + // Declarations in the node_modules directory + expect(projectWithNodeModules.getLuaExecutionResult().moduleWithDeclarationsResults).toEqual({ + foo: "foo from lua module with decls", + bar: "bar from lua module with decls: module with declarations!", + baz: "baz from lua module with decls", + }); +}); + +test("can resolve module dependencies with hand-written declarations", () => { + // Declarations in the node_modules directory + expect(projectWithNodeModules.getLuaExecutionResult().moduleWithoutDeclarationsResults).toEqual({ + foo: "foo from lua module without decls", + bar: "bar from lua module without decls: module without declarations!", + baz: "baz from lua module without decls", + }); +}); + +test("can resolve package depencency with a dependency on another package", () => { + // Declarations in the node_modules directory + expect(projectWithNodeModules.getLuaExecutionResult().moduleWithDependencyResult).toEqual( + "Calling dependency: foo from lua module with decls" + ); +}); diff --git a/test/transpile/module-resolution/project-with-node-modules/lua-global-without-decls.d.ts b/test/transpile/module-resolution/project-with-node-modules/lua-global-without-decls.d.ts index 26774ee25..4d1c1db1b 100644 --- a/test/transpile/module-resolution/project-with-node-modules/lua-global-without-decls.d.ts +++ b/test/transpile/module-resolution/project-with-node-modules/lua-global-without-decls.d.ts @@ -1,4 +1,4 @@ /** @noSelfInFile */ declare function fooGlobalWithoutDecls(): string; declare function barGlobalWithoutDecls(param: string): string; -declare function bazGlobalWithoutDecls(): string; \ No newline at end of file +declare function bazGlobalWithoutDecls(): string; diff --git a/test/transpile/module-resolution/project-with-node-modules/lua-module-without-decls.d.ts b/test/transpile/module-resolution/project-with-node-modules/lua-module-without-decls.d.ts index 07295d96b..9ccfc9bfe 100644 --- a/test/transpile/module-resolution/project-with-node-modules/lua-module-without-decls.d.ts +++ b/test/transpile/module-resolution/project-with-node-modules/lua-module-without-decls.d.ts @@ -6,4 +6,4 @@ declare module "lua-module-without-decls" { declare module "lua-module-without-decls/baz" { function baz(this: void): string; -} \ No newline at end of file +} diff --git a/test/transpile/module-resolution/project-with-node-modules/main.ts b/test/transpile/module-resolution/project-with-node-modules/main.ts index f9f8b448a..8d9332190 100644 --- a/test/transpile/module-resolution/project-with-node-modules/main.ts +++ b/test/transpile/module-resolution/project-with-node-modules/main.ts @@ -12,22 +12,28 @@ import * as moduleWithoutDeclarationsBaz from "lua-module-without-decls/baz"; import * as moduleWithDependency from "lua-module-with-dependency"; -export const testResult = [ - fooGlobal(), - barGlobal("global with declarations!"), - bazGlobal(), - - fooGlobalWithoutDecls(), - barGlobalWithoutDecls("global without declarations!"), - bazGlobalWithoutDecls(), - - moduleWithDeclarations.foo(), - moduleWithDeclarations.bar("module with declarations!"), - moduleWithDeclarationsBaz.baz(), - - moduleWithoutDeclarations.foo(), - moduleWithoutDeclarations.bar("module without declarations!"), - moduleWithoutDeclarationsBaz.baz(), - - moduleWithDependency.callDependency() -]; +export const globalWithDeclarationsResults = { + foo: fooGlobal(), + bar: barGlobal("global with declarations!"), + baz: bazGlobal(), +}; + +export const globalWithoutDeclarationsResults = { + foo: fooGlobalWithoutDecls(), + bar: barGlobalWithoutDecls("global without declarations!"), + baz: bazGlobalWithoutDecls(), +}; + +export const moduleWithDeclarationsResults = { + foo: moduleWithDeclarations.foo(), + bar: moduleWithDeclarations.bar("module with declarations!"), + baz: moduleWithDeclarationsBaz.baz(), +}; + +export const moduleWithoutDeclarationsResults = { + foo: moduleWithoutDeclarations.foo(), + bar: moduleWithoutDeclarations.bar("module without declarations!"), + baz: moduleWithoutDeclarationsBaz.baz(), +}; + +export const moduleWithDependencyResult = moduleWithDependency.callDependency(); diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua index 300a3e3b2..02980e662 100644 --- a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/baz.lua @@ -1,3 +1,3 @@ function bazGlobalWithoutDecls() - return "baz from lua global with decls" + return "baz from lua global without decls" end \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua index f8f9723ce..46dd970a0 100644 --- a/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua +++ b/test/transpile/module-resolution/project-with-node-modules/node_modules/lua-global-without-decls/index.lua @@ -1,6 +1,6 @@ function fooGlobalWithoutDecls() - return "foo from lua global with decls" + return "foo from lua global without decls" end function barGlobalWithoutDecls(param) - return "bar from lua global with decls: " .. param + return "bar from lua global without decls: " .. param end \ No newline at end of file diff --git a/test/util.ts b/test/util.ts index aa4f76259..10585ef11 100644 --- a/test/util.ts +++ b/test/util.ts @@ -1,6 +1,6 @@ /* eslint-disable jest/no-standalone-expect */ import * as nativeAssert from "assert"; -import { LauxLib, Lua, LuaLib, LUA_OK } from "lua-wasm-bindings/dist/lua"; +import { LauxLib, Lua, LuaLib, LuaState, LUA_OK } from "lua-wasm-bindings/dist/lua"; import * as fs from "fs"; import { stringify } from "javascript-stringify"; import * as path from "path"; @@ -10,6 +10,7 @@ import * as vm from "vm"; import * as tstl from "../src"; import { createEmitOutputCollector } from "../src/transpilation/output-collector"; import { transpileProject } from "../src"; +import { normalizeSlashes } from "../src/utils"; const jsonLib = fs.readFileSync(path.join(__dirname, "json.lua"), "utf8"); const luaLib = fs.readFileSync(path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"), "utf8"); @@ -149,7 +150,7 @@ export abstract class TestBuilder { protected mainFileName = "main.ts"; public setMainFileName(mainFileName: string): this { expect(this.hasProgram).toBe(false); - this.mainFileName = path.normalize(mainFileName); + this.mainFileName = normalizeSlashes(mainFileName); return this; } @@ -177,7 +178,10 @@ export abstract class TestBuilder { @memoize public getProgram(): ts.Program { this.hasProgram = true; - return tstl.createVirtualProgram({ ...this.extraFiles, [this.mainFileName]: this.getTsCode() }, this.options); + return tstl.createVirtualProgram( + { ...this.extraFiles, [normalizeSlashes(this.mainFileName)]: this.getTsCode() }, + this.options + ); } @memoize @@ -203,7 +207,9 @@ export abstract class TestBuilder { const { transpiledFiles } = this.getLuaResult(); const mainFile = this.options.luaBundle ? transpiledFiles[0] - : transpiledFiles.find(({ sourceFiles }) => sourceFiles.some(f => path.normalize(f.fileName) === this.mainFileName)); + : transpiledFiles.find(({ sourceFiles }) => + sourceFiles.some(f => normalizeSlashes(f.fileName) === this.mainFileName) + ); expect(mainFile).toMatchObject({ lua: expect.any(String), luaSourceMap: expect.any(String) }); return mainFile as ExecutableTranspiledFile; @@ -261,9 +267,7 @@ export abstract class TestBuilder { public debug(): this { const transpiledFiles = this.getLuaResult().transpiledFiles; - const luaCode = transpiledFiles.map( - f => `[${f.sourceFiles.map(sf => sf.fileName).join(",")}]:\n${f.lua?.replace(/^/gm, " ")}` - ); + const luaCode = transpiledFiles.map(f => `[${f.outPath}]:\n${f.lua?.replace(/^/gm, " ")}`); const value = prettyFormat(this.getLuaExecutionResult()).replace(/^/gm, " "); console.log(`Lua Code:\n${luaCode.join("\n")}\n\nValue:\n${value}`); return this; @@ -368,35 +372,23 @@ export abstract class TestBuilder { // Load modules // Json - lua.lua_getglobal(L, "package"); - lua.lua_getfield(L, -1, "preload"); - lauxlib.luaL_loadstring(L, jsonLib); - lua.lua_setfield(L, -2, "json"); + this.packagePreloadLuaFile(L, lua, lauxlib, "json", jsonLib); // Lua lib if ( this.options.luaLibImport === tstl.LuaLibImportKind.Require || mainFile.includes('require("lualib_bundle")') ) { - lua.lua_getglobal(L, "package"); - lua.lua_getfield(L, -1, "preload"); - lauxlib.luaL_loadstring(L, luaLib); - lua.lua_setfield(L, -2, "lualib_bundle"); + this.packagePreloadLuaFile(L, lua, lauxlib, "lualib_bundle", luaLib); } - // Extra files + // Load all transpiled files into Lua's package cache const { transpiledFiles } = this.getLuaResult(); - - Object.keys(this.extraFiles).forEach(fileName => { - const transpiledExtraFile = transpiledFiles.find(({ sourceFiles }) => - sourceFiles.some(f => f.fileName === fileName) - ); - if (transpiledExtraFile?.lua) { - lua.lua_getglobal(L, "package"); - lua.lua_getfield(L, -1, "preload"); - lauxlib.luaL_loadstring(L, transpiledExtraFile.lua); - lua.lua_setfield(L, -2, fileName.replace(".ts", "")); + for (const transpiledFile of transpiledFiles) { + if (transpiledFile.lua) { + const filePath = path.relative(path.dirname(this.mainFileName), transpiledFile.outPath); + this.packagePreloadLuaFile(L, lua, lauxlib, filePath, transpiledFile.lua); } - }); + } // Execute Main const wrappedMainCode = ` @@ -425,6 +417,14 @@ end)());`; } } + private packagePreloadLuaFile(state: LuaState, lua: Lua, lauxlib: LauxLib, fileName: string, fileContent: string) { + // Adding source Lua to the package.preload cache will allow require to find it + lua.lua_getglobal(state, "package"); + lua.lua_getfield(state, -1, "preload"); + lauxlib.luaL_loadstring(state, fileContent); + lua.lua_setfield(state, -2, fileName.replace(".lua", "").replace(/\\/g, ".")); + } + private executeJs(): any { const { transpiledFiles } = this.getJsResult(); // Custom require for extra files. Really basic. Global support is hacky From 01e24e4e56124078046d7e8d66e20ddc0fa614c9 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 15 May 2021 15:08:28 +0200 Subject: [PATCH 05/34] Replace requires in source maps too --- .eslintignore | 2 +- src/transpilation/bundle.ts | 2 +- src/transpilation/resolve.ts | 57 ++++++-- src/transpilation/transpiler.ts | 11 +- test/transpile/module-resolution.spec.ts | 123 +++++++++++++----- .../project-with-dependency-chain/main.ts | 3 + .../node_modules/dependency1/index.d.ts | 2 + .../node_modules/dependency1/index.lua | 5 + .../node_modules/dependency2/index.lua | 5 + .../node_modules/dependency3/index.lua | 3 + .../tsconfig.json | 12 ++ 11 files changed, 172 insertions(+), 53 deletions(-) create mode 100644 test/transpile/module-resolution/project-with-dependency-chain/main.ts create mode 100644 test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.d.ts create mode 100644 test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.lua create mode 100644 test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency2/index.lua create mode 100644 test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency3/index.lua create mode 100644 test/transpile/module-resolution/project-with-dependency-chain/tsconfig.json diff --git a/.eslintignore b/.eslintignore index f0ab43bbf..492195f96 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,5 +3,5 @@ /test/cli/errors /test/cli/watch /test/transpile/directories -/test/transpile/module-resolution/node_modules +/test/transpile/module-resolution/*/node_modules /test/transpile/outFile diff --git a/src/transpilation/bundle.ts b/src/transpilation/bundle.ts index c696c6900..39c0ad75a 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -83,7 +83,7 @@ export function getBundleResult( function moduleSourceNode({ code, sourceMapNode }: ProcessedFile, modulePath: string): SourceNode { const tableEntryHead = `[${modulePath}] = function() `; - const tableEntryTail = "end,\n"; + const tableEntryTail = " end,\n"; return joinSourceChunks([tableEntryHead, sourceMapNode ?? code, tableEntryTail]); } diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index a8e50bc55..49b747906 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -3,6 +3,7 @@ import * as resolve from "enhanced-resolve"; import * as ts from "typescript"; import * as fs from "fs"; import { EmitHost, ProcessedFile } from "./utils"; +import { SourceNode } from "source-map"; const resolver = resolve.ResolverFactory.createResolver({ extensions: [".lua", ".ts"], @@ -24,27 +25,33 @@ function resolveFileDependencies(file: ProcessedFile, rootDir: string, emitHost: const fileDir = path.dirname(file.fileName); const dependencies: ProcessedFile[] = []; for (const required of findRequiredPaths(file.code)) { + // Try to resolve the import starting from the directory `file` is in const resolvedDependency = resolveDependency(fileDir, required); if (resolvedDependency) { + // If dependency resolved successfully, read its content const dependencyContent = emitHost.readFile(resolvedDependency); if (dependencyContent === undefined) { throw `TODO: FAILED TO READ ${resolvedDependency}`; } - let relativePath = path.relative(fileDir, resolvedDependency); - let outPath = resolvedDependency; - if (relativePath.includes("..")) { - relativePath = path.relative(rootDir, resolvedDependency); - outPath = path.join(fileDir, relativePath); + // Figure out resolved require path and dependency output path + let resolvedRequire = path.relative(fileDir, resolvedDependency); + let dependencyOutPath = resolvedDependency; + if (resolvedRequire.includes("..")) { + // If the resolved require includes a parent, copy the dependency to a new path + // to avoid require paths with parent directories + resolvedRequire = path.relative(rootDir, resolvedDependency); + dependencyOutPath = path.join(fileDir, resolvedRequire); } - const requirePath = relativePath.replace(".lua", "").replace(/\\/g, "."); - file.code = file.code.replace(`require("${required}")`, `require("${requirePath}")`); + replaceRequireInCode(file, required, resolvedRequire); + replaceRequireInSourceMap(file, required, resolvedRequire); + + // Add dependency to output and resolve its dependencies recursively const dependency = { - fileName: outPath, + fileName: dependencyOutPath, code: dependencyContent, }; - dependencies.push(dependency, ...resolveFileDependencies(dependency, rootDir, emitHost)); } else { //throw `TODO: COULD NOT RESOLVE ${required}`; @@ -53,7 +60,37 @@ function resolveFileDependencies(file: ProcessedFile, rootDir: string, emitHost: return dependencies; } +function replaceRequireInCode(file: ProcessedFile, originalRequire: string, newRequire: string) { + const requirePath = newRequire.replace(".lua", "").replace(/\\/g, "."); + file.code = file.code.replace(`require("${originalRequire}")`, `require("${requirePath}")`); +} + +function replaceRequireInSourceMap(file: ProcessedFile, originalRequire: string, newRequire: string) { + const requirePath = newRequire.replace(".lua", "").replace(/\\/g, "."); + if (file.sourceMapNode) { + replaceInSourceMap(file.sourceMapNode, file.sourceMapNode, `"${originalRequire}"`, `"${requirePath}"`); + } +} + +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])]; + return true; // Stop after finding the first occurrence + } + + if (node.children) { + for (const c of node.children) { + if (replaceInSourceMap(c, node, require, resolvedRequire)) { + return true; // Occurrence found in one of the children + } + } + } + + return false; // Did not find the require +} + function findRequiredPaths(code: string): string[] { + // Find all require("") paths in the code const paths: string[] = []; const pattern = /require\("(.+)"\)/g; // eslint-disable-next-line @typescript-eslint/ban-types @@ -72,7 +109,7 @@ function resolveDependency(fromDirectory: string, dependency: string): string | return resolveResult; } } catch { - // TODO + // resolveSync errors if it fails to resolve } return undefined; diff --git a/src/transpilation/transpiler.ts b/src/transpilation/transpiler.ts index b332ce28f..9cb5c0d24 100644 --- a/src/transpilation/transpiler.ts +++ b/src/transpilation/transpiler.ts @@ -35,9 +35,7 @@ export class Transpiler { emitOptions ); - const resolvedFiles = resolveDependencies(program, freshFiles, this.emitHost); - - const { emitPlan } = this.getEmitPlan(program, diagnostics, resolvedFiles); + const { emitPlan } = this.getEmitPlan(program, diagnostics, freshFiles); const options = program.getCompilerOptions(); const emitBOM = options.emitBOM ?? false; @@ -66,13 +64,16 @@ export class Transpiler { files.unshift({ fileName, code: getLuaLibBundle(this.emitHost) }); } + // Resolve imported modules and modify output Lua + const resolvedFiles = resolveDependencies(program, files, this.emitHost); + let emitPlan: EmitFile[]; if (isBundleEnabled(options)) { - const [bundleDiagnostics, bundleFile] = getBundleResult(program, this.emitHost, files); + const [bundleDiagnostics, bundleFile] = getBundleResult(program, this.emitHost, resolvedFiles); diagnostics.push(...bundleDiagnostics); emitPlan = [bundleFile]; } else { - emitPlan = files.map(file => { + emitPlan = resolvedFiles.map(file => { const pathInOutDir = path.resolve(outDir, path.relative(rootDir, file.fileName)); const outputPath = normalizeSlashes(trimExtension(pathInOutDir) + ".lua"); return { ...file, outputPath }; diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index aaefe554d..3cfcf165a 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -1,51 +1,102 @@ import * as path from "path"; import * as util from "../util"; -const projectPath = path.resolve(__dirname, "module-resolution", "project-with-node-modules"); +describe("basic module resolution", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-node-modules"); -const projectWithNodeModules = util - .testProject(path.join(projectPath, "tsconfig.json")) - .setMainFileName(path.join(projectPath, "main.ts")); + const projectWithNodeModules = util + .testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")); -test("can resolve global dependencies with declarations", () => { - // Declarations in the node_modules directory - expect(projectWithNodeModules.getLuaExecutionResult().globalWithDeclarationsResults).toEqual({ - foo: "foo from lua global with decls", - bar: "bar from lua global with decls: global with declarations!", - baz: "baz from lua global with decls", + test("can resolve global dependencies with declarations", () => { + // Declarations in the node_modules directory + expect(projectWithNodeModules.getLuaExecutionResult().globalWithDeclarationsResults).toEqual({ + foo: "foo from lua global with decls", + bar: "bar from lua global with decls: global with declarations!", + baz: "baz from lua global with decls", + }); }); -}); -test("can resolve global dependencies with hand-written declarations", () => { - // No declarations in the node_modules directory, but written by hand in project dir - expect(projectWithNodeModules.getLuaExecutionResult().globalWithoutDeclarationsResults).toEqual({ - foo: "foo from lua global without decls", - bar: "bar from lua global without decls: global without declarations!", - baz: "baz from lua global without decls", + test("can resolve global dependencies with hand-written declarations", () => { + // No declarations in the node_modules directory, but written by hand in project dir + expect(projectWithNodeModules.getLuaExecutionResult().globalWithoutDeclarationsResults).toEqual({ + foo: "foo from lua global without decls", + bar: "bar from lua global without decls: global without declarations!", + baz: "baz from lua global without decls", + }); }); -}); -test("can resolve module dependencies with declarations", () => { - // Declarations in the node_modules directory - expect(projectWithNodeModules.getLuaExecutionResult().moduleWithDeclarationsResults).toEqual({ - foo: "foo from lua module with decls", - bar: "bar from lua module with decls: module with declarations!", - baz: "baz from lua module with decls", + test("can resolve module dependencies with declarations", () => { + // Declarations in the node_modules directory + expect(projectWithNodeModules.getLuaExecutionResult().moduleWithDeclarationsResults).toEqual({ + foo: "foo from lua module with decls", + bar: "bar from lua module with decls: module with declarations!", + baz: "baz from lua module with decls", + }); }); -}); -test("can resolve module dependencies with hand-written declarations", () => { - // Declarations in the node_modules directory - expect(projectWithNodeModules.getLuaExecutionResult().moduleWithoutDeclarationsResults).toEqual({ - foo: "foo from lua module without decls", - bar: "bar from lua module without decls: module without declarations!", - baz: "baz from lua module without decls", + test("can resolve module dependencies with hand-written declarations", () => { + // Declarations in the node_modules directory + expect(projectWithNodeModules.getLuaExecutionResult().moduleWithoutDeclarationsResults).toEqual({ + foo: "foo from lua module without decls", + bar: "bar from lua module without decls: module without declarations!", + baz: "baz from lua module without decls", + }); + }); + + test("can resolve package depencency with a dependency on another package", () => { + // Declarations in the node_modules directory + expect(projectWithNodeModules.getLuaExecutionResult().moduleWithDependencyResult).toEqual( + "Calling dependency: foo from lua module with decls" + ); + }); + + test("resolved package dependency included in bundle", () => { + const mainFile = path.join(projectPath, "main.ts"); + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual({ + globalWithDeclarationsResults: { + foo: "foo from lua global with decls", + bar: "bar from lua global with decls: global with declarations!", + baz: "baz from lua global with decls", + }, + globalWithoutDeclarationsResults: { + foo: "foo from lua global without decls", + bar: "bar from lua global without decls: global without declarations!", + baz: "baz from lua global without decls", + }, + moduleWithDeclarationsResults: { + foo: "foo from lua module with decls", + bar: "bar from lua module with decls: module with declarations!", + baz: "baz from lua module with decls", + }, + moduleWithDependencyResult: "Calling dependency: foo from lua module with decls", + moduleWithoutDeclarationsResults: { + foo: "foo from lua module without decls", + bar: "bar from lua module without decls: module without declarations!", + baz: "baz from lua module without decls", + }, + }); }); }); -test("can resolve package depencency with a dependency on another package", () => { - // Declarations in the node_modules directory - expect(projectWithNodeModules.getLuaExecutionResult().moduleWithDependencyResult).toEqual( - "Calling dependency: foo from lua module with decls" - ); +describe("module resolution with chained dependencies", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); + + test("can resolve dependencies in chain", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")) + .expectToEqual({}); + }); + + test.only("resolved package dependency included in bundle", () => { + const mainFile = path.join(projectPath, "main.ts"); + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .debug() + .expectToEqual({}); + }); }); diff --git a/test/transpile/module-resolution/project-with-dependency-chain/main.ts b/test/transpile/module-resolution/project-with-dependency-chain/main.ts new file mode 100644 index 000000000..c98830a45 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-chain/main.ts @@ -0,0 +1,3 @@ +import * as dependency1 from "dependency1"; + +export const result = dependency1.f1(); \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.d.ts b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.d.ts new file mode 100644 index 000000000..761d6bc02 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.d.ts @@ -0,0 +1,2 @@ +/** @noSelfInFile */ +export declare function f1(): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.lua b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.lua new file mode 100644 index 000000000..dcf28d6fd --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.lua @@ -0,0 +1,5 @@ +local dependency2 = require("dependency2") + +return { + f1 = function() return dependency2.f2() end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency2/index.lua b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency2/index.lua new file mode 100644 index 000000000..10b36647e --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency2/index.lua @@ -0,0 +1,5 @@ +local dependency3 = require("dependency3") + +return { + f2 = dependency3.f3 +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency3/index.lua b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency3/index.lua new file mode 100644 index 000000000..112f9ba7e --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency3/index.lua @@ -0,0 +1,3 @@ +return { + f3 = function() return "dependency3" end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-dependency-chain/tsconfig.json b/test/transpile/module-resolution/project-with-dependency-chain/tsconfig.json new file mode 100644 index 000000000..935b64af6 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-chain/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "strict": true, + "moduleResolution": "Node", + "noUnusedLocals": true, + "noUnusedParameters": true, + "target": "esnext", + "lib": ["esnext"], + "types": [], + "rootDir": "." + } +} From 29b5d62d1ea7df0813ca1a2e4418e9314916f939 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 16 May 2021 11:46:45 +0200 Subject: [PATCH 06/34] Fixed incorrect path behavior --- src/transpilation/resolve.ts | 19 ++++++------------- test/transpile/module-resolution.spec.ts | 8 ++++---- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index 49b747906..220b3d336 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -21,12 +21,11 @@ export function resolveDependencies(program: ts.Program, files: ProcessedFile[], return outFiles; } -function resolveFileDependencies(file: ProcessedFile, rootDir: string, emitHost: EmitHost): ProcessedFile[] { - const fileDir = path.dirname(file.fileName); +function resolveFileDependencies(file: ProcessedFile, projectRootDir: string, emitHost: EmitHost): ProcessedFile[] { const dependencies: ProcessedFile[] = []; for (const required of findRequiredPaths(file.code)) { // Try to resolve the import starting from the directory `file` is in - const resolvedDependency = resolveDependency(fileDir, required); + const resolvedDependency = resolveDependency(projectRootDir, required); if (resolvedDependency) { // If dependency resolved successfully, read its content const dependencyContent = emitHost.readFile(resolvedDependency); @@ -35,26 +34,20 @@ function resolveFileDependencies(file: ProcessedFile, rootDir: string, emitHost: } // Figure out resolved require path and dependency output path - let resolvedRequire = path.relative(fileDir, resolvedDependency); - let dependencyOutPath = resolvedDependency; - if (resolvedRequire.includes("..")) { - // If the resolved require includes a parent, copy the dependency to a new path - // to avoid require paths with parent directories - resolvedRequire = path.relative(rootDir, resolvedDependency); - dependencyOutPath = path.join(fileDir, resolvedRequire); - } + const resolvedRequire = path.relative(projectRootDir, resolvedDependency); replaceRequireInCode(file, required, resolvedRequire); replaceRequireInSourceMap(file, required, resolvedRequire); // Add dependency to output and resolve its dependencies recursively const dependency = { - fileName: dependencyOutPath, + fileName: resolvedDependency, code: dependencyContent, }; - dependencies.push(dependency, ...resolveFileDependencies(dependency, rootDir, emitHost)); + dependencies.push(dependency, ...resolveFileDependencies(dependency, projectRootDir, emitHost)); } else { //throw `TODO: COULD NOT RESOLVE ${required}`; + console.error(`Failed to resolve ${required} referenced in ${file.fileName}.`); } } return dependencies; diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 3cfcf165a..4f7e6db8d 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -86,17 +86,17 @@ describe("module resolution with chained dependencies", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); test("can resolve dependencies in chain", () => { + //transpileProject(path.join(projectPath, "tsconfig.json")) util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "main.ts")) - .expectToEqual({}); + .expectToEqual({ result: "dependency3" }); }); - test.only("resolved package dependency included in bundle", () => { + test("resolved package dependency included in bundle", () => { const mainFile = path.join(projectPath, "main.ts"); util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(mainFile) .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) - .debug() - .expectToEqual({}); + .expectToEqual({ result: "dependency3" }); }); }); From af82e4e598261e83038f384ffba964b7cd675586 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 16 May 2021 20:48:22 +0200 Subject: [PATCH 07/34] More tests --- src/transpilation/resolve.ts | 31 +++++++++--- src/transpilation/utils.ts | 1 + test/transpile/module-resolution.spec.ts | 47 ++++++++++++++++++- .../project-with-sourceDir/src/main.ts | 5 ++ .../src/subdir/otherfile.ts | 3 ++ .../project-with-sourceDir/tsconfig.json | 13 +++++ test/util.ts | 2 +- 7 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 test/transpile/module-resolution/project-with-sourceDir/src/main.ts create mode 100644 test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts create mode 100644 test/transpile/module-resolution/project-with-sourceDir/tsconfig.json diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index 220b3d336..0fa55a6c6 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -6,7 +6,7 @@ import { EmitHost, ProcessedFile } from "./utils"; import { SourceNode } from "source-map"; const resolver = resolve.ResolverFactory.createResolver({ - extensions: [".lua", ".ts"], + extensions: [".lua"], fileSystem: { ...new resolve.CachedInputFileSystem(fs) }, useSyncFileSystemCalls: true, }); @@ -15,7 +15,7 @@ export function resolveDependencies(program: ts.Program, files: ProcessedFile[], const outFiles = []; for (const file of files) { - outFiles.push(file, ...resolveFileDependencies(file, program.getCommonSourceDirectory(), emitHost)); + outFiles.push(file, ...resolveFileDependencies(file, program.getCompilerOptions().rootDir ?? program.getCommonSourceDirectory(), emitHost)); } return outFiles; @@ -24,8 +24,14 @@ export function resolveDependencies(program: ts.Program, files: ProcessedFile[], function resolveFileDependencies(file: ProcessedFile, projectRootDir: string, emitHost: EmitHost): ProcessedFile[] { const dependencies: ProcessedFile[] = []; for (const required of findRequiredPaths(file.code)) { + // Do no resolve lualib + if (required === "lualib_bundle") { + continue; + } + // Try to resolve the import starting from the directory `file` is in - const resolvedDependency = resolveDependency(projectRootDir, required); + const fileDir = path.dirname(file.fileName); + const resolvedDependency = resolveDependency(fileDir, projectRootDir, required, emitHost); if (resolvedDependency) { // If dependency resolved successfully, read its content const dependencyContent = emitHost.readFile(resolvedDependency); @@ -48,6 +54,7 @@ function resolveFileDependencies(file: ProcessedFile, projectRootDir: string, em } else { //throw `TODO: COULD NOT RESOLVE ${required}`; console.error(`Failed to resolve ${required} referenced in ${file.fileName}.`); + console.error(projectRootDir); } } return dependencies; @@ -95,13 +102,25 @@ function findRequiredPaths(code: string): string[] { return paths; } -function resolveDependency(fromDirectory: string, dependency: string): string | undefined { +function resolveDependency(fileDirectory: string, rootDirectory: string, dependency: string, emitHost: EmitHost): string | undefined { + // Check if + const dependencyPath = dependency.replace(".", "/"); + const projectFilePath = path.join(fileDirectory, dependencyPath + ".ts"); + if (emitHost.fileExists(projectFilePath)) { + return projectFilePath; + } + + const projectIndexPath = path.join(fileDirectory, dependencyPath, "index.ts"); + if (emitHost.fileExists(projectIndexPath)) { + return projectIndexPath; + } + try { - const resolveResult = resolver.resolveSync({}, fromDirectory, dependency.replace(".", "/")); + const resolveResult = resolver.resolveSync({}, rootDirectory, dependencyPath); if (resolveResult) { return resolveResult; } - } catch { + } catch (e) { // resolveSync errors if it fails to resolve } diff --git a/src/transpilation/utils.ts b/src/transpilation/utils.ts index 099dfd400..a4acdfbb4 100644 --- a/src/transpilation/utils.ts +++ b/src/transpilation/utils.ts @@ -8,6 +8,7 @@ import * as lua from "../LuaAST"; import * as diagnosticFactories from "./diagnostics"; export interface EmitHost { + fileExists(path: string): boolean; getCurrentDirectory(): string; readFile(path: string): string | undefined; writeFile: ts.WriteFileCallback; diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 4f7e6db8d..e5451996b 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -86,7 +86,6 @@ describe("module resolution with chained dependencies", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); test("can resolve dependencies in chain", () => { - //transpileProject(path.join(projectPath, "tsconfig.json")) util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "main.ts")) .expectToEqual({ result: "dependency3" }); @@ -100,3 +99,49 @@ describe("module resolution with chained dependencies", () => { .expectToEqual({ result: "dependency3" }); }); }); + +describe("module resolution with outDir", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); + + test("emits files in outDir", () => { + const builder = util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")) + .setOptions({ outDir: "tstl-out" }) + .expectToEqual({ result: "dependency3" }); + + // Get the output paths relative to the project path + const outPaths = builder.getLuaResult().transpiledFiles.map(f => path.relative(projectPath, f.outPath)); + expect(outPaths).toHaveLength(4); + expect(outPaths).toContain("tstl-out/main.lua"); + expect(outPaths).toContain("tstl-out/node_modules/dependency1/index.lua"); + expect(outPaths).toContain("tstl-out/node_modules/dependency2/index.lua"); + expect(outPaths).toContain("tstl-out/node_modules/dependency3/index.lua"); + }); + + test("emits bundle in outDir", () => { + const mainFile = path.join(projectPath, "main.ts"); + const builder = util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "tstl-out/bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual({ result: "dependency3" }); + }); +}); + +describe("module resolution with sourceDir", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-sourceDir"); + + test("can resolve dependencies with sourceDir", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "src", "main.ts")) + .setOptions({ outDir: "tstl-out" }) + .expectToEqual({ result: "dependency3" }); + }); + + test("can resolve dependencies and bundle files with sourceDir", () => { + const mainFile = path.join(projectPath, "src", "main.ts"); + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual({ result: "dependency3" }); + }); +}); diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/main.ts b/test/transpile/module-resolution/project-with-sourceDir/src/main.ts new file mode 100644 index 000000000..1c3031f5f --- /dev/null +++ b/test/transpile/module-resolution/project-with-sourceDir/src/main.ts @@ -0,0 +1,5 @@ +import * as dependency1 from "dependency1"; +import { func } from "./subdir/otherfile"; + +export const result = dependency1.f1(); +export const result2 = func(); \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts new file mode 100644 index 000000000..8e3a56bac --- /dev/null +++ b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts @@ -0,0 +1,3 @@ +export function func() { + return "non-node_modules import"; +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json b/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json new file mode 100644 index 000000000..06b443a23 --- /dev/null +++ b/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "strict": true, + "moduleResolution": "Node", + "noUnusedLocals": true, + "noUnusedParameters": true, + "target": "esnext", + "lib": ["esnext"], + "types": [], + "rootDir": "src", + "outDir": "tstl-out", + } +} diff --git a/test/util.ts b/test/util.ts index 10585ef11..2ed93007c 100644 --- a/test/util.ts +++ b/test/util.ts @@ -385,7 +385,7 @@ export abstract class TestBuilder { const { transpiledFiles } = this.getLuaResult(); for (const transpiledFile of transpiledFiles) { if (transpiledFile.lua) { - const filePath = path.relative(path.dirname(this.mainFileName), transpiledFile.outPath); + const filePath = path.relative(this.options.outDir ?? this.getProgram().getCommonSourceDirectory(), transpiledFile.outPath); this.packagePreloadLuaFile(L, lua, lauxlib, filePath, transpiledFile.lua); } } From 73a350a07a8c40a6541149ceb774add5f8494511 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 22 May 2021 18:01:53 +0200 Subject: [PATCH 08/34] add module resolution with sourceDir node_modules --- .../node_modules/dependency1/index.d.ts | 2 ++ .../node_modules/dependency1/index.lua | 5 +++++ .../node_modules/dependency2/index.lua | 5 +++++ .../node_modules/dependency3/index.lua | 3 +++ 4 files changed, 15 insertions(+) create mode 100644 test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency1/index.d.ts create mode 100644 test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency1/index.lua create mode 100644 test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency2/index.lua create mode 100644 test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency3/index.lua diff --git a/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency1/index.d.ts b/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency1/index.d.ts new file mode 100644 index 000000000..761d6bc02 --- /dev/null +++ b/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency1/index.d.ts @@ -0,0 +1,2 @@ +/** @noSelfInFile */ +export declare function f1(): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency1/index.lua b/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency1/index.lua new file mode 100644 index 000000000..dcf28d6fd --- /dev/null +++ b/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency1/index.lua @@ -0,0 +1,5 @@ +local dependency2 = require("dependency2") + +return { + f1 = function() return dependency2.f2() end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency2/index.lua b/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency2/index.lua new file mode 100644 index 000000000..10b36647e --- /dev/null +++ b/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency2/index.lua @@ -0,0 +1,5 @@ +local dependency3 = require("dependency3") + +return { + f2 = dependency3.f3 +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency3/index.lua b/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency3/index.lua new file mode 100644 index 000000000..112f9ba7e --- /dev/null +++ b/test/transpile/module-resolution/project-with-sourceDir/node_modules/dependency3/index.lua @@ -0,0 +1,3 @@ +return { + f3 = function() return "dependency3" end +} \ No newline at end of file From 737e2663598a31ecb8860d50b371148afddb32e5 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 23 May 2021 13:44:16 +0200 Subject: [PATCH 09/34] Get all module-resolution testcases to work --- src/transformation/visitors/modules/import.ts | 34 ++-------- src/transpilation/bundle.ts | 22 +++---- src/transpilation/resolve.ts | 47 +++++++++----- src/transpilation/transpiler.ts | 63 ++++++++++++++++--- test/transpile/module-resolution.spec.ts | 26 +++++--- test/util.ts | 5 +- 6 files changed, 116 insertions(+), 81 deletions(-) diff --git a/src/transformation/visitors/modules/import.ts b/src/transformation/visitors/modules/import.ts index d36bb0509..d8daba8c9 100644 --- a/src/transformation/visitors/modules/import.ts +++ b/src/transformation/visitors/modules/import.ts @@ -1,7 +1,6 @@ import * as path from "path"; import * as ts from "typescript"; import * as lua from "../../../LuaAST"; -import { formatPathToLuaPath } from "../../../utils"; import { FunctionVisitor, TransformationContext } from "../../context"; import { AnnotationKind, getSymbolAnnotations } from "../../utils/annotations"; import { createDefaultExportStringLiteral } from "../../utils/export"; @@ -10,36 +9,13 @@ import { createSafeName } from "../../utils/safe-names"; import { peekScope } from "../../utils/scope"; import { transformIdentifier } from "../identifier"; import { transformPropertyName } from "../literal"; -import { unresolvableRequirePath } from "../../utils/diagnostics"; -const getAbsoluteImportPath = (relativePath: string, directoryPath: string, options: ts.CompilerOptions): string => - !relativePath.startsWith(".") && options.baseUrl - ? path.resolve(options.baseUrl, relativePath) - : path.resolve(directoryPath, relativePath); - -function getImportPath(context: TransformationContext, relativePath: string, node: ts.Node): string { - const { options, sourceFile } = context; - const { fileName } = sourceFile; - const rootDir = options.rootDir ? path.resolve(options.rootDir) : path.resolve("."); - - const absoluteImportPath = path.format( - path.parse(getAbsoluteImportPath(relativePath, path.dirname(fileName), options)) - ); - const absoluteRootDirPath = path.format(path.parse(rootDir)); - if (absoluteImportPath.includes(absoluteRootDirPath)) { - return formatPathToLuaPath(absoluteImportPath.replace(absoluteRootDirPath, "").slice(1)); - } else { - context.diagnostics.push(unresolvableRequirePath(node, relativePath)); - return relativePath; - } -} - -function shouldResolveModulePath(context: TransformationContext, moduleSpecifier: ts.Expression): boolean { +function isNoResolutionPath(context: TransformationContext, moduleSpecifier: ts.Expression): boolean { const moduleOwnerSymbol = context.checker.getSymbolAtLocation(moduleSpecifier); - if (!moduleOwnerSymbol) return true; + if (!moduleOwnerSymbol) return false; const annotations = getSymbolAnnotations(moduleOwnerSymbol); - return !annotations.has(AnnotationKind.NoResolution); + return annotations.has(AnnotationKind.NoResolution); } export function createModuleRequire( @@ -49,8 +25,8 @@ export function createModuleRequire( ): lua.CallExpression { const params: lua.Expression[] = []; if (ts.isStringLiteral(moduleSpecifier)) { - const modulePath = shouldResolveModulePath(context, moduleSpecifier) - ? getImportPath(context, moduleSpecifier.text.replace(/"/g, ""), moduleSpecifier) + const modulePath = isNoResolutionPath(context, moduleSpecifier) + ? `@NoResolution:${moduleSpecifier.text}` : moduleSpecifier.text; params.push(lua.createStringLiteral(modulePath)); diff --git a/src/transpilation/bundle.ts b/src/transpilation/bundle.ts index 39c0ad75a..338b1f618 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -5,10 +5,11 @@ import { CompilerOptions } from "../CompilerOptions"; import { escapeString } from "../LuaPrinter"; import { cast, formatPathToLuaPath, isNonNull, normalizeSlashes, trimExtension } from "../utils"; import { couldNotFindBundleEntryPoint } from "./diagnostics"; -import { EmitFile, EmitHost, ProcessedFile } from "./utils"; +import { getEmitOutDir, getEmitPathRelativeToOutDir, getSourceDir } from "./transpiler"; +import { EmitFile, ProcessedFile } from "./utils"; -const createModulePath = (baseDir: string, pathToResolve: string) => - escapeString(formatPathToLuaPath(trimExtension(path.relative(baseDir, pathToResolve)))); +const createModulePath = (pathToResolve: string, program: ts.Program) => + escapeString(formatPathToLuaPath(trimExtension(getEmitPathRelativeToOutDir(pathToResolve, program)))); // Override `require` to read from ____modules table. const requireOverride = ` @@ -34,7 +35,6 @@ end export function getBundleResult( program: ts.Program, - emitHost: EmitHost, files: ProcessedFile[] ): [ts.Diagnostic[], EmitFile] { const diagnostics: ts.Diagnostic[] = []; @@ -43,15 +43,9 @@ export function getBundleResult( const bundleFile = cast(options.luaBundle, isNonNull); const entryModule = cast(options.luaBundleEntry, isNonNull); - const rootDir = program.getCommonSourceDirectory(); - const outDir = options.outDir ?? rootDir; - const projectRootDir = options.configFilePath - ? path.dirname(options.configFilePath) - : emitHost.getCurrentDirectory(); - // Resolve project settings relative to project file. - const resolvedEntryModule = path.resolve(projectRootDir, entryModule); - const outputPath = normalizeSlashes(path.resolve(projectRootDir, bundleFile)); + const resolvedEntryModule = path.resolve(getSourceDir(program), entryModule); + const outputPath = normalizeSlashes(path.resolve(getEmitOutDir(program), bundleFile)); if (!files.some(f => f.fileName === resolvedEntryModule)) { diagnostics.push(couldNotFindBundleEntryPoint(entryModule)); @@ -59,13 +53,13 @@ export function getBundleResult( } // For each file: [""] = function() end, - const moduleTableEntries = files.map(f => moduleSourceNode(f, createModulePath(outDir, f.fileName))); + const moduleTableEntries = files.map(f => moduleSourceNode(f, createModulePath(f.fileName, program))); // Create ____modules table containing all entries from moduleTableEntries const moduleTable = createModuleTableNode(moduleTableEntries); // return require("") - const entryPoint = `return require(${createModulePath(outDir, resolvedEntryModule)})\n`; + const entryPoint = `return require(${createModulePath(resolvedEntryModule, program)})\n`; const bundleNode = joinSourceChunks([requireOverride, moduleTable, entryPoint]); const { code, map } = bundleNode.toStringWithSourceMap(); diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index 0fa55a6c6..a89df4465 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -4,6 +4,7 @@ import * as ts from "typescript"; import * as fs from "fs"; import { EmitHost, ProcessedFile } from "./utils"; import { SourceNode } from "source-map"; +import { getEmitPathRelativeToOutDir, getSourceDir } from "./transpiler"; const resolver = resolve.ResolverFactory.createResolver({ extensions: [".lua"], @@ -15,19 +16,28 @@ export function resolveDependencies(program: ts.Program, files: ProcessedFile[], const outFiles = []; for (const file of files) { - outFiles.push(file, ...resolveFileDependencies(file, program.getCompilerOptions().rootDir ?? program.getCommonSourceDirectory(), emitHost)); + outFiles.push(file, ...resolveFileDependencies(file, program, emitHost)); } return outFiles; } -function resolveFileDependencies(file: ProcessedFile, projectRootDir: string, emitHost: EmitHost): ProcessedFile[] { +function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitHost: EmitHost): ProcessedFile[] { + const projectRootDir = getSourceDir(program); const dependencies: ProcessedFile[] = []; for (const required of findRequiredPaths(file.code)) { // Do no resolve lualib if (required === "lualib_bundle") { continue; } + + // Do not resolve noResolution paths + if (required.startsWith("@NoResolution:")) { + const path = required.replace("@NoResolution:", "") + replaceRequireInCode(file, required, path); + replaceRequireInSourceMap(file, required, path); + continue; + } // Try to resolve the import starting from the directory `file` is in const fileDir = path.dirname(file.fileName); @@ -39,20 +49,21 @@ function resolveFileDependencies(file: ProcessedFile, projectRootDir: string, em throw `TODO: FAILED TO READ ${resolvedDependency}`; } - // Figure out resolved require path and dependency output path - const resolvedRequire = path.relative(projectRootDir, resolvedDependency); + // Figure out resolved require path and dependency output path + const resolvedRequire = getEmitPathRelativeToOutDir(resolvedDependency, program); replaceRequireInCode(file, required, resolvedRequire); replaceRequireInSourceMap(file, required, resolvedRequire); - // Add dependency to output and resolve its dependencies recursively - const dependency = { - fileName: resolvedDependency, - code: dependencyContent, - }; - dependencies.push(dependency, ...resolveFileDependencies(dependency, projectRootDir, emitHost)); + // If dependency is not part of sources, add dependency to output and resolve its dependencies recursively + if (!program.getSourceFile(resolvedDependency)) { + const dependency = { + fileName: resolvedDependency, + code: dependencyContent, + }; + dependencies.push(dependency, ...resolveFileDependencies(dependency, program, emitHost)); + } } else { - //throw `TODO: COULD NOT RESOLVE ${required}`; console.error(`Failed to resolve ${required} referenced in ${file.fileName}.`); console.error(projectRootDir); } @@ -103,14 +114,16 @@ function findRequiredPaths(code: string): string[] { } function resolveDependency(fileDirectory: string, rootDirectory: string, dependency: string, emitHost: EmitHost): string | undefined { - // Check if - const dependencyPath = dependency.replace(".", "/"); - const projectFilePath = path.join(fileDirectory, dependencyPath + ".ts"); - if (emitHost.fileExists(projectFilePath)) { - return projectFilePath; + // Check if file is a TS file in the project + const dependencyPath = dependency; + const resolvedPath = path.resolve(fileDirectory, dependencyPath); + const resolvedFile = resolvedPath + ".ts"; + + if (emitHost.fileExists(resolvedFile)) { + return resolvedPath + ".ts"; } - const projectIndexPath = path.join(fileDirectory, dependencyPath, "index.ts"); + const projectIndexPath = path.resolve(fileDirectory, dependencyPath, "index.ts"); if (emitHost.fileExists(projectIndexPath)) { return projectIndexPath; } diff --git a/src/transpilation/transpiler.ts b/src/transpilation/transpiler.ts index 9cb5c0d24..9ffd23e00 100644 --- a/src/transpilation/transpiler.ts +++ b/src/transpilation/transpiler.ts @@ -55,31 +55,74 @@ export class Transpiler { files: ProcessedFile[] ): { emitPlan: EmitFile[] } { const options = program.getCompilerOptions(); - const rootDir = program.getCommonSourceDirectory(); - const outDir = options.outDir ?? rootDir; const lualibRequired = files.some(f => f.code.includes('require("lualib_bundle")')); if (lualibRequired) { - const fileName = normalizeSlashes(path.resolve(rootDir, "lualib_bundle.lua")); + const fileName = normalizeSlashes(path.resolve(getEmitOutDir(program), "lualib_bundle.lua")); files.unshift({ fileName, code: getLuaLibBundle(this.emitHost) }); } - // Resolve imported modules and modify output Lua + // Resolve imported modules and modify output Lua requires const resolvedFiles = resolveDependencies(program, files, this.emitHost); let emitPlan: EmitFile[]; if (isBundleEnabled(options)) { - const [bundleDiagnostics, bundleFile] = getBundleResult(program, this.emitHost, resolvedFiles); + const [bundleDiagnostics, bundleFile] = getBundleResult(program, resolvedFiles); diagnostics.push(...bundleDiagnostics); emitPlan = [bundleFile]; } else { - emitPlan = resolvedFiles.map(file => { - const pathInOutDir = path.resolve(outDir, path.relative(rootDir, file.fileName)); - const outputPath = normalizeSlashes(trimExtension(pathInOutDir) + ".lua"); - return { ...file, outputPath }; - }); + emitPlan = resolvedFiles.map(file => ({ ...file, outputPath: getEmitPath(file.fileName, program) })); } return { emitPlan }; } } + +export function getEmitPath(file: string, program: ts.Program): string { + const relativeOutputPath = getEmitPathRelativeToOutDir(file, program); + const outDir = getEmitOutDir(program); + + return path.join(outDir, relativeOutputPath); +} + +export function getEmitPathRelativeToOutDir(fileName: string, program: ts.Program): string { + const sourceDir = getSourceDir(program); + // Default output path is relative path in source dir + let emitPath = path.relative(sourceDir, fileName).split(path.sep); + + // If source is in a parent directory of source dir, move it into the source dir + emitPath = emitPath.filter(s => s !== ".."); + + // To avoid overwriting lua sources in node_modules, emit into lua_modules + if (emitPath[0] === "node_modules") { + emitPath[0] = "lua_modules"; + } + + // Make extension lua + emitPath[emitPath.length - 1] = trimExtension(emitPath[emitPath.length - 1]) + ".lua"; + + return path.join(...emitPath); +} + +export function getSourceDir(program: ts.Program): string { + const rootDir = program.getCompilerOptions().rootDir; + if (rootDir && rootDir.length > 0) { + return path.isAbsolute(rootDir) ? rootDir : path.resolve(getProjectRoot(program), rootDir); + } + return program.getCommonSourceDirectory(); +} + +export function getEmitOutDir(program: ts.Program): string { + const outDir = program.getCompilerOptions().outDir; + if (outDir && outDir.length > 0) { + return path.isAbsolute(outDir) ? outDir : path.resolve(getProjectRoot(program), outDir); + } + return program.getCommonSourceDirectory(); +} + +export function getProjectRoot(program: ts.Program): string { + // Try to get the directory the tsconfig is in + const tsConfigPath = program.getCompilerOptions().configFilePath; + // If no tsconfig is known, use common source directory + return tsConfigPath ? path.dirname(tsConfigPath) : program.getCommonSourceDirectory(); +} diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index e5451996b..f69008e8f 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -104,7 +104,8 @@ describe("module resolution with outDir", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); test("emits files in outDir", () => { - const builder = util.testProject(path.join(projectPath, "tsconfig.json")) + const builder = util + .testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "main.ts")) .setOptions({ outDir: "tstl-out" }) .expectToEqual({ result: "dependency3" }); @@ -112,18 +113,25 @@ describe("module resolution with outDir", () => { // Get the output paths relative to the project path const outPaths = builder.getLuaResult().transpiledFiles.map(f => path.relative(projectPath, f.outPath)); expect(outPaths).toHaveLength(4); - expect(outPaths).toContain("tstl-out/main.lua"); - expect(outPaths).toContain("tstl-out/node_modules/dependency1/index.lua"); - expect(outPaths).toContain("tstl-out/node_modules/dependency2/index.lua"); - expect(outPaths).toContain("tstl-out/node_modules/dependency3/index.lua"); + expect(outPaths).toContain(path.join("tstl-out", "main.lua")); + // Note: outputs to lua_modules + expect(outPaths).toContain(path.join("tstl-out", "lua_modules", "dependency1", "index.lua")); + expect(outPaths).toContain(path.join("tstl-out", "lua_modules", "dependency2", "index.lua")); + expect(outPaths).toContain(path.join("tstl-out", "lua_modules", "dependency3", "index.lua")); }); test("emits bundle in outDir", () => { const mainFile = path.join(projectPath, "main.ts"); - const builder = util.testProject(path.join(projectPath, "tsconfig.json")) + const builder = util + .testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(mainFile) - .setOptions({ luaBundle: "tstl-out/bundle.lua", luaBundleEntry: mainFile }) + .setOptions({ outDir: "tstl-out", luaBundle: "bundle.lua", luaBundleEntry: mainFile }) .expectToEqual({ result: "dependency3" }); + + // Get the output paths relative to the project path + const outPaths = builder.getLuaResult().transpiledFiles.map(f => path.relative(projectPath, f.outPath)); + expect(outPaths).toHaveLength(1); + expect(outPaths).toContain(path.join("tstl-out", "bundle.lua")); }); }); @@ -134,7 +142,7 @@ describe("module resolution with sourceDir", () => { util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "src", "main.ts")) .setOptions({ outDir: "tstl-out" }) - .expectToEqual({ result: "dependency3" }); + .expectToEqual({ result: "dependency3", result2: "non-node_modules import" }); }); test("can resolve dependencies and bundle files with sourceDir", () => { @@ -142,6 +150,6 @@ describe("module resolution with sourceDir", () => { util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(mainFile) .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) - .expectToEqual({ result: "dependency3" }); + .expectToEqual({ result: "dependency3", result2: "non-node_modules import" }); }); }); diff --git a/test/util.ts b/test/util.ts index 2ed93007c..a4f607584 100644 --- a/test/util.ts +++ b/test/util.ts @@ -9,7 +9,7 @@ import * as ts from "typescript"; import * as vm from "vm"; import * as tstl from "../src"; import { createEmitOutputCollector } from "../src/transpilation/output-collector"; -import { transpileProject } from "../src"; +import { getEmitOutDir, transpileProject } from "../src"; import { normalizeSlashes } from "../src/utils"; const jsonLib = fs.readFileSync(path.join(__dirname, "json.lua"), "utf8"); @@ -385,7 +385,7 @@ export abstract class TestBuilder { const { transpiledFiles } = this.getLuaResult(); for (const transpiledFile of transpiledFiles) { if (transpiledFile.lua) { - const filePath = path.relative(this.options.outDir ?? this.getProgram().getCommonSourceDirectory(), transpiledFile.outPath); + const filePath = path.relative(getEmitOutDir(this.getProgram()), transpiledFile.outPath); this.packagePreloadLuaFile(L, lua, lauxlib, filePath, transpiledFile.lua); } } @@ -544,6 +544,7 @@ class ExpressionTestBuilder extends AccessorTestBuilder { class ProjectTestBuilder extends ModuleTestBuilder { constructor(private tsConfig: string) { super(""); + this.setOptions({ configFilePath: this.tsConfig }); } @memoize From 83b0dea8f0f02b941fc489112615e5ce1fe33c5d Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 24 May 2021 12:27:17 +0200 Subject: [PATCH 10/34] Restrict resolver to only lua files --- src/transpilation/resolve.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index a89df4465..923b7da5b 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -8,6 +8,7 @@ import { getEmitPathRelativeToOutDir, getSourceDir } from "./transpiler"; const resolver = resolve.ResolverFactory.createResolver({ extensions: [".lua"], + enforceExtension: true, // Must be a lua file fileSystem: { ...new resolve.CachedInputFileSystem(fs) }, useSyncFileSystemCalls: true, }); From c080110dea8f8e4810b8a46924f7ff65135a6521 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 24 May 2021 17:09:31 +0200 Subject: [PATCH 11/34] All tests working --- src/transformation/utils/diagnostics.ts | 4 - src/transpilation/bundle.ts | 5 +- src/transpilation/diagnostics.ts | 9 ++ src/transpilation/resolve.ts | 92 +++++++++++++------ src/transpilation/transpiler.ts | 13 ++- test/translation/transformation.spec.ts | 3 +- .../__snapshots__/directories.spec.ts.snap | 8 -- test/transpile/directories.spec.ts | 1 - .../baseurl/src/lib/nested/file.ts | 3 - .../transpile/directories/baseurl/src/main.ts | 3 - test/transpile/module-resolution.spec.ts | 2 +- .../project-with-dependency-chain/main.ts | 2 +- .../project-with-sourceDir/src/main.ts | 2 +- .../src/subdir/otherfile.ts | 2 +- .../project-with-sourceDir/tsconfig.json | 2 +- .../__snapshots__/resolution.spec.ts.snap | 4 +- test/unit/modules/modules.spec.ts | 3 +- test/unit/modules/resolution.spec.ts | 5 +- test/unit/printer/sourcemaps.spec.ts | 7 +- 19 files changed, 103 insertions(+), 67 deletions(-) delete mode 100644 test/transpile/directories/baseurl/src/lib/nested/file.ts delete mode 100644 test/transpile/directories/baseurl/src/main.ts diff --git a/src/transformation/utils/diagnostics.ts b/src/transformation/utils/diagnostics.ts index 422a93cac..22bd5882c 100644 --- a/src/transformation/utils/diagnostics.ts +++ b/src/transformation/utils/diagnostics.ts @@ -124,10 +124,6 @@ export const invalidAmbientIdentifierName = createErrorDiagnosticFactory( (text: string) => `Invalid ambient identifier name '${text}'. Ambient identifiers must be valid lua identifiers.` ); -export const unresolvableRequirePath = createErrorDiagnosticFactory( - (path: string) => `Cannot create require path. Module '${path}' does not exist within --rootDir.` -); - export const unsupportedVarDeclaration = createErrorDiagnosticFactory( "`var` declarations are not supported. Use `let` or `const` instead." ); diff --git a/src/transpilation/bundle.ts b/src/transpilation/bundle.ts index 338b1f618..ae17e1b4f 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -33,10 +33,7 @@ local function require(file) end `; -export function getBundleResult( - program: ts.Program, - files: ProcessedFile[] -): [ts.Diagnostic[], EmitFile] { +export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [ts.Diagnostic[], EmitFile] { const diagnostics: ts.Diagnostic[] = []; const options = program.getCompilerOptions() as CompilerOptions; diff --git a/src/transpilation/diagnostics.ts b/src/transpilation/diagnostics.ts index d0e609677..704c358b4 100644 --- a/src/transpilation/diagnostics.ts +++ b/src/transpilation/diagnostics.ts @@ -4,6 +4,15 @@ import { createSerialDiagnosticFactory } from "../utils"; const createDiagnosticFactory = (getMessage: (...args: TArgs) => string) => createSerialDiagnosticFactory((...args: TArgs) => ({ messageText: getMessage(...args) })); +export const couldNotResolveRequire = createDiagnosticFactory( + (require: string, containingFile: string) => + `Could not resolve require path '${require}' in file ${containingFile}.` +); + +export const couldNotReadDependency = createDiagnosticFactory( + (dependency: string) => `Could not read content of resolved dependency ${dependency}.` +); + export const toLoadItShouldBeTranspiled = createDiagnosticFactory( (kind: string, transform: string) => `To load "${transform}" ${kind} it should be transpiled or "ts-node" should be installed.` diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index 923b7da5b..10de869fe 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -4,7 +4,9 @@ import * as ts from "typescript"; import * as fs from "fs"; import { EmitHost, ProcessedFile } from "./utils"; import { SourceNode } from "source-map"; -import { getEmitPathRelativeToOutDir, getSourceDir } from "./transpiler"; +import { getEmitPathRelativeToOutDir, getProjectRoot, getSourceDir } from "./transpiler"; +import { formatPathToLuaPath } from "../utils"; +import { couldNotReadDependency, couldNotResolveRequire } from "./diagnostics"; const resolver = resolve.ResolverFactory.createResolver({ extensions: [".lua"], @@ -13,19 +15,38 @@ const resolver = resolve.ResolverFactory.createResolver({ useSyncFileSystemCalls: true, }); -export function resolveDependencies(program: ts.Program, files: ProcessedFile[], emitHost: EmitHost): ProcessedFile[] { - const outFiles = []; +const projectFiles = new Map(); + +interface ResolutionResult { + resolvedFiles: ProcessedFile[]; + diagnostics: ts.Diagnostic[]; +} + +export function resolveDependencies(program: ts.Program, files: ProcessedFile[], emitHost: EmitHost): ResolutionResult { + const outFiles: ProcessedFile[] = []; + const diagnostics: ts.Diagnostic[] = []; + + const projectRoot = getProjectRoot(program); + for (const sourceFile of program.getSourceFiles()) { + const filePath = path.isAbsolute(sourceFile.fileName) + ? path.normalize(sourceFile.fileName) + : path.resolve(projectRoot, sourceFile.fileName); + projectFiles.set(filePath, sourceFile.text); + } for (const file of files) { - outFiles.push(file, ...resolveFileDependencies(file, program, emitHost)); + const resolutionResult = resolveFileDependencies(file, program, emitHost); + outFiles.push(file, ...resolutionResult.resolvedFiles); + diagnostics.push(...resolutionResult.diagnostics); } - return outFiles; + return { resolvedFiles: outFiles, diagnostics }; } -function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitHost: EmitHost): ProcessedFile[] { +function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitHost: EmitHost): ResolutionResult { const projectRootDir = getSourceDir(program); const dependencies: ProcessedFile[] = []; + const diagnostics: ts.Diagnostic[] = []; for (const required of findRequiredPaths(file.code)) { // Do no resolve lualib if (required === "lualib_bundle") { @@ -34,20 +55,21 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH // Do not resolve noResolution paths if (required.startsWith("@NoResolution:")) { - const path = required.replace("@NoResolution:", "") + const path = required.replace("@NoResolution:", ""); replaceRequireInCode(file, required, path); replaceRequireInSourceMap(file, required, path); continue; } - + // Try to resolve the import starting from the directory `file` is in const fileDir = path.dirname(file.fileName); - const resolvedDependency = resolveDependency(fileDir, projectRootDir, required, emitHost); + const resolvedDependency = resolveDependency(fileDir, projectRootDir, required); if (resolvedDependency) { // If dependency resolved successfully, read its content - const dependencyContent = emitHost.readFile(resolvedDependency); + const dependencyContent = projectFiles.get(resolvedDependency) ?? emitHost.readFile(resolvedDependency); if (dependencyContent === undefined) { - throw `TODO: FAILED TO READ ${resolvedDependency}`; + diagnostics.push(couldNotReadDependency(resolvedDependency)); + continue; } // Figure out resolved require path and dependency output path @@ -57,28 +79,34 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH replaceRequireInSourceMap(file, required, resolvedRequire); // If dependency is not part of sources, add dependency to output and resolve its dependencies recursively - if (!program.getSourceFile(resolvedDependency)) { + if (!projectFiles.has(resolvedDependency)) { const dependency = { fileName: resolvedDependency, code: dependencyContent, }; - dependencies.push(dependency, ...resolveFileDependencies(dependency, program, emitHost)); + const nestedDependencies = resolveFileDependencies(dependency, program, emitHost); + dependencies.push(dependency, ...nestedDependencies.resolvedFiles); + diagnostics.push(...nestedDependencies.diagnostics); } } else { - console.error(`Failed to resolve ${required} referenced in ${file.fileName}.`); - console.error(projectRootDir); + // Could not resolve dependency, add a diagnostic and make some fallback path + diagnostics.push(couldNotResolveRequire(required, path.relative(projectRootDir, file.fileName))); + + const fallbackRequire = fallbackResolve(required, projectRootDir, fileDir); + replaceRequireInCode(file, required, fallbackRequire); + replaceRequireInSourceMap(file, required, fallbackRequire); } } - return dependencies; + return { resolvedFiles: dependencies, diagnostics }; } function replaceRequireInCode(file: ProcessedFile, originalRequire: string, newRequire: string) { - const requirePath = newRequire.replace(".lua", "").replace(/\\/g, "."); + const requirePath = formatPathToLuaPath(newRequire.replace(".lua", "")); file.code = file.code.replace(`require("${originalRequire}")`, `require("${requirePath}")`); } function replaceRequireInSourceMap(file: ProcessedFile, originalRequire: string, newRequire: string) { - const requirePath = newRequire.replace(".lua", "").replace(/\\/g, "."); + const requirePath = formatPathToLuaPath(newRequire.replace(".lua", "")); if (file.sourceMapNode) { replaceInSourceMap(file.sourceMapNode, file.sourceMapNode, `"${originalRequire}"`, `"${requirePath}"`); } @@ -114,23 +142,22 @@ function findRequiredPaths(code: string): string[] { return paths; } -function resolveDependency(fileDirectory: string, rootDirectory: string, dependency: string, emitHost: EmitHost): string | undefined { +function resolveDependency(fileDirectory: string, rootDirectory: string, dependency: string): string | undefined { // Check if file is a TS file in the project - const dependencyPath = dependency; - const resolvedPath = path.resolve(fileDirectory, dependencyPath); - const resolvedFile = resolvedPath + ".ts"; + const resolvedPath = path.resolve(fileDirectory, dependency); - if (emitHost.fileExists(resolvedFile)) { - return resolvedPath + ".ts"; + const resolvedFile = resolvedPath + ".ts"; + if (projectFiles.has(resolvedFile)) { + return resolvedFile; } - const projectIndexPath = path.resolve(fileDirectory, dependencyPath, "index.ts"); - if (emitHost.fileExists(projectIndexPath)) { + const projectIndexPath = path.resolve(resolvedPath, "index.ts"); + if (projectFiles.has(projectIndexPath)) { return projectIndexPath; } try { - const resolveResult = resolver.resolveSync({}, rootDirectory, dependencyPath); + const resolveResult = resolver.resolveSync({}, rootDirectory, dependency); if (resolveResult) { return resolveResult; } @@ -140,3 +167,14 @@ function resolveDependency(fileDirectory: string, rootDirectory: string, depende return undefined; } + +// Transform an import path to a lua require that is probably not correct, but can be used as fallback when regular resolution fails +function fallbackResolve(required: string, projectRootDir: string, fileDir: string): string { + return formatPathToLuaPath( + path + .normalize(path.join(path.relative(projectRootDir, fileDir), required)) + .split(path.sep) + .filter(s => s !== "." && s !== "..") + .join(path.sep) + ); +} diff --git a/src/transpilation/transpiler.ts b/src/transpilation/transpiler.ts index 9ffd23e00..9c6a66666 100644 --- a/src/transpilation/transpiler.ts +++ b/src/transpilation/transpiler.ts @@ -58,20 +58,25 @@ export class Transpiler { const lualibRequired = files.some(f => f.code.includes('require("lualib_bundle")')); if (lualibRequired) { - const fileName = normalizeSlashes(path.resolve(getEmitOutDir(program), "lualib_bundle.lua")); + // Add lualib bundle to source dir 'virtually', will be moved to correct output dir in emitPlan + const fileName = normalizeSlashes(path.resolve(getSourceDir(program), "lualib_bundle.lua")); files.unshift({ fileName, code: getLuaLibBundle(this.emitHost) }); } // Resolve imported modules and modify output Lua requires - const resolvedFiles = resolveDependencies(program, files, this.emitHost); + const resolutionResult = resolveDependencies(program, files, this.emitHost); + diagnostics.push(...resolutionResult.diagnostics); let emitPlan: EmitFile[]; if (isBundleEnabled(options)) { - const [bundleDiagnostics, bundleFile] = getBundleResult(program, resolvedFiles); + const [bundleDiagnostics, bundleFile] = getBundleResult(program, resolutionResult.resolvedFiles); diagnostics.push(...bundleDiagnostics); emitPlan = [bundleFile]; } else { - emitPlan = resolvedFiles.map(file => ({ ...file, outputPath: getEmitPath(file.fileName, program) })); + emitPlan = resolutionResult.resolvedFiles.map(file => ({ + ...file, + outputPath: getEmitPath(file.fileName, program), + })); } return { emitPlan }; diff --git a/test/translation/transformation.spec.ts b/test/translation/transformation.spec.ts index 4e0d7dd9d..42a4b4355 100644 --- a/test/translation/transformation.spec.ts +++ b/test/translation/transformation.spec.ts @@ -2,6 +2,7 @@ import * as fs from "fs"; import * as path from "path"; import * as tstl from "../../src"; import { annotationDeprecated } from "../../src/transformation/utils/diagnostics"; +import { couldNotResolveRequire } from "../../src/transpilation/diagnostics"; import * as util from "../util"; const fixturesPath = path.join(__dirname, "./transformation"); @@ -14,7 +15,7 @@ const fixtures = fs test.each(fixtures)("Transformation (%s)", (_name, content) => { util.testModule(content) .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require }) - .ignoreDiagnostics([annotationDeprecated.code]) + .ignoreDiagnostics([annotationDeprecated.code, couldNotResolveRequire.code]) .disableSemanticCheck() .expectLuaToMatchSnapshot(); }); diff --git a/test/transpile/__snapshots__/directories.spec.ts.snap b/test/transpile/__snapshots__/directories.spec.ts.snap index 901d885f4..eaf179a6c 100644 --- a/test/transpile/__snapshots__/directories.spec.ts.snap +++ b/test/transpile/__snapshots__/directories.spec.ts.snap @@ -1,13 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should be able to resolve ({"name": "baseurl", "options": [Object]}) 1`] = ` -Array [ - "directories/baseurl/out/lualib_bundle.lua", - "directories/baseurl/out/src/lib/nested/file.lua", - "directories/baseurl/out/src/main.lua", -] -`; - exports[`should be able to resolve ({"name": "basic", "options": [Object]}) 1`] = ` Array [ "directories/basic/src/lib/file.lua", diff --git a/test/transpile/directories.spec.ts b/test/transpile/directories.spec.ts index 9141199e5..1c0bba994 100644 --- a/test/transpile/directories.spec.ts +++ b/test/transpile/directories.spec.ts @@ -13,7 +13,6 @@ test.each([ { name: "basic", options: { outDir: "out" } }, { name: "basic", options: { rootDir: "src" } }, { name: "basic", options: { rootDir: "src", outDir: "out" } }, - { name: "baseurl", options: { baseUrl: "./src/lib", rootDir: ".", outDir: "./out" } }, ])("should be able to resolve (%p)", ({ name, options: compilerOptions }) => { const projectPath = path.join(__dirname, "directories", name); jest.spyOn(process, "cwd").mockReturnValue(projectPath); diff --git a/test/transpile/directories/baseurl/src/lib/nested/file.ts b/test/transpile/directories/baseurl/src/lib/nested/file.ts deleted file mode 100644 index 4248a042b..000000000 --- a/test/transpile/directories/baseurl/src/lib/nested/file.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function test() { - return 1; -} diff --git a/test/transpile/directories/baseurl/src/main.ts b/test/transpile/directories/baseurl/src/main.ts deleted file mode 100644 index 1665f4e08..000000000 --- a/test/transpile/directories/baseurl/src/main.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { test } from "nested/file"; - -test(); diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index f69008e8f..8039aac6b 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -127,7 +127,7 @@ describe("module resolution with outDir", () => { .setMainFileName(mainFile) .setOptions({ outDir: "tstl-out", luaBundle: "bundle.lua", luaBundleEntry: mainFile }) .expectToEqual({ result: "dependency3" }); - + // Get the output paths relative to the project path const outPaths = builder.getLuaResult().transpiledFiles.map(f => path.relative(projectPath, f.outPath)); expect(outPaths).toHaveLength(1); diff --git a/test/transpile/module-resolution/project-with-dependency-chain/main.ts b/test/transpile/module-resolution/project-with-dependency-chain/main.ts index c98830a45..f3a3a226b 100644 --- a/test/transpile/module-resolution/project-with-dependency-chain/main.ts +++ b/test/transpile/module-resolution/project-with-dependency-chain/main.ts @@ -1,3 +1,3 @@ import * as dependency1 from "dependency1"; -export const result = dependency1.f1(); \ No newline at end of file +export const result = dependency1.f1(); diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/main.ts b/test/transpile/module-resolution/project-with-sourceDir/src/main.ts index 1c3031f5f..32fc4af7c 100644 --- a/test/transpile/module-resolution/project-with-sourceDir/src/main.ts +++ b/test/transpile/module-resolution/project-with-sourceDir/src/main.ts @@ -2,4 +2,4 @@ import * as dependency1 from "dependency1"; import { func } from "./subdir/otherfile"; export const result = dependency1.f1(); -export const result2 = func(); \ No newline at end of file +export const result2 = func(); diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts index 8e3a56bac..292ff42e2 100644 --- a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts +++ b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts @@ -1,3 +1,3 @@ export function func() { return "non-node_modules import"; -} \ No newline at end of file +} diff --git a/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json b/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json index 06b443a23..7fd18481e 100644 --- a/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json +++ b/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json @@ -8,6 +8,6 @@ "lib": ["esnext"], "types": [], "rootDir": "src", - "outDir": "tstl-out", + "outDir": "tstl-out" } } diff --git a/test/unit/modules/__snapshots__/resolution.spec.ts.snap b/test/unit/modules/__snapshots__/resolution.spec.ts.snap index d4228141d..4db74c57f 100644 --- a/test/unit/modules/__snapshots__/resolution.spec.ts.snap +++ b/test/unit/modules/__snapshots__/resolution.spec.ts.snap @@ -2,9 +2,9 @@ exports[`doesn't resolve paths out of root dir: code 1`] = ` "local ____exports = {} -local module = require(\\"../module\\") +local module = require(\\"module\\") local ____ = module return ____exports" `; -exports[`doesn't resolve paths out of root dir: diagnostics 1`] = `"src/main.ts(2,33): error TSTL: Cannot create require path. Module '../module' does not exist within --rootDir."`; +exports[`doesn't resolve paths out of root dir: diagnostics 1`] = `"error TSTL: Could not resolve require path '../module' in file main.ts."`; diff --git a/test/unit/modules/modules.spec.ts b/test/unit/modules/modules.spec.ts index dd3d1dd7a..ffdc36ce9 100644 --- a/test/unit/modules/modules.spec.ts +++ b/test/unit/modules/modules.spec.ts @@ -58,8 +58,7 @@ test.each(["ke-bab", "dollar$", "singlequote'", "hash#", "s p a c e", "ɥɣɎɌ import { foo } from "./${name}"; export { foo }; ` - .disableSemanticCheck() - .setLuaHeader('setmetatable(package.loaded, { __index = function() return { foo = "bar" } end })') + .addExtraFile(`${name}.ts`, 'export const foo = "bar";') .setReturnExport("foo") .expectToEqual("bar"); } diff --git a/test/unit/modules/resolution.spec.ts b/test/unit/modules/resolution.spec.ts index 541c28c56..791ae06b5 100644 --- a/test/unit/modules/resolution.spec.ts +++ b/test/unit/modules/resolution.spec.ts @@ -1,5 +1,5 @@ import * as ts from "typescript"; -import { unresolvableRequirePath } from "../../../src/transformation/utils/diagnostics"; +import { couldNotResolveRequire } from "../../../src/transpilation/diagnostics"; import * as util from "../../util"; const requireRegex = /require\("(.*?)"\)/; @@ -69,6 +69,7 @@ test.each([ module; ` .setMainFileName(filePath) + .addExtraFile(`${usedPath}.ts`, "") .setOptions(options) .tap(expectToRequire(expected)); }); @@ -81,7 +82,7 @@ test("doesn't resolve paths out of root dir", () => { .setMainFileName("src/main.ts") .setOptions({ rootDir: "./src" }) .disableSemanticCheck() - .expectDiagnosticsToMatchSnapshot([unresolvableRequirePath.code]); + .expectDiagnosticsToMatchSnapshot([couldNotResolveRequire.code]); }); test.each([ diff --git a/test/unit/printer/sourcemaps.spec.ts b/test/unit/printer/sourcemaps.spec.ts index 84a6596a6..4d4955c03 100644 --- a/test/unit/printer/sourcemaps.spec.ts +++ b/test/unit/printer/sourcemaps.spec.ts @@ -1,5 +1,6 @@ import { Position, SourceMapConsumer } from "source-map"; import * as tstl from "../../../src"; +import { couldNotResolveRequire } from "../../../src/transpilation/diagnostics"; import * as util from "../../util"; test.each([ @@ -144,7 +145,11 @@ test.each([ ], }, ])("Source map has correct mapping (%p)", async ({ code, assertPatterns }) => { - const file = util.testModule(code).expectToHaveNoDiagnostics().getMainLuaFileResult(); + const file = util + .testModule(code) + .ignoreDiagnostics([couldNotResolveRequire.code]) + .expectToHaveNoDiagnostics() + .getMainLuaFileResult(); const consumer = await new SourceMapConsumer(file.luaSourceMap); for (const { luaPattern, typeScriptPattern } of assertPatterns) { From 7a7988112957ce797d090981293411f8f09428ef Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 24 May 2021 17:23:05 +0200 Subject: [PATCH 12/34] Added more in-project dependency checks to sourceDir test --- test/transpile/module-resolution.spec.ts | 21 +++++++++++++------ .../project-with-sourceDir/src/main.ts | 8 +++++-- .../src/subdir/otherfile.ts | 2 ++ .../src/subdir/otherfile2.ts | 3 +++ .../src/subdir/subdirofsubdir/nestedfile.ts | 9 ++++++++ 5 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile2.ts create mode 100644 test/transpile/module-resolution/project-with-sourceDir/src/subdir/subdirofsubdir/nestedfile.ts diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 8039aac6b..d1f0c268c 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -84,11 +84,12 @@ describe("basic module resolution", () => { describe("module resolution with chained dependencies", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); + const expectedResult = { result: "dependency3" }; test("can resolve dependencies in chain", () => { util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "main.ts")) - .expectToEqual({ result: "dependency3" }); + .expectToEqual(expectedResult); }); test("resolved package dependency included in bundle", () => { @@ -96,19 +97,20 @@ describe("module resolution with chained dependencies", () => { util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(mainFile) .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) - .expectToEqual({ result: "dependency3" }); + .expectToEqual(expectedResult); }); }); describe("module resolution with outDir", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); + const expectedResult = { result: "dependency3" }; test("emits files in outDir", () => { const builder = util .testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "main.ts")) .setOptions({ outDir: "tstl-out" }) - .expectToEqual({ result: "dependency3" }); + .expectToEqual(expectedResult); // Get the output paths relative to the project path const outPaths = builder.getLuaResult().transpiledFiles.map(f => path.relative(projectPath, f.outPath)); @@ -126,7 +128,7 @@ describe("module resolution with outDir", () => { .testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(mainFile) .setOptions({ outDir: "tstl-out", luaBundle: "bundle.lua", luaBundleEntry: mainFile }) - .expectToEqual({ result: "dependency3" }); + .expectToEqual(expectedResult); // Get the output paths relative to the project path const outPaths = builder.getLuaResult().transpiledFiles.map(f => path.relative(projectPath, f.outPath)); @@ -137,12 +139,19 @@ describe("module resolution with outDir", () => { describe("module resolution with sourceDir", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-sourceDir"); + const expectedResult = { + result: "dependency3", + functionInSubDir: "non-node_modules import", + functionReExportedFromSubDir: "nested func result", + nestedFunctionInSubDirOfSubDir: "nested func result", + nestedFunctionUsingFunctionFromParentDir: "nested func: non-node_modules import 2", + }; test("can resolve dependencies with sourceDir", () => { util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "src", "main.ts")) .setOptions({ outDir: "tstl-out" }) - .expectToEqual({ result: "dependency3", result2: "non-node_modules import" }); + .expectToEqual(expectedResult); }); test("can resolve dependencies and bundle files with sourceDir", () => { @@ -150,6 +159,6 @@ describe("module resolution with sourceDir", () => { util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(mainFile) .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) - .expectToEqual({ result: "dependency3", result2: "non-node_modules import" }); + .expectToEqual(expectedResult); }); }); diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/main.ts b/test/transpile/module-resolution/project-with-sourceDir/src/main.ts index 32fc4af7c..9014f7b62 100644 --- a/test/transpile/module-resolution/project-with-sourceDir/src/main.ts +++ b/test/transpile/module-resolution/project-with-sourceDir/src/main.ts @@ -1,5 +1,9 @@ import * as dependency1 from "dependency1"; -import { func } from "./subdir/otherfile"; +import { func, nestedFunc } from "./subdir/otherfile"; +import { nestedFunc as nestedFuncOriginal, nestedFuncUsingParent } from "./subdir/subdirofsubdir/nestedfile"; export const result = dependency1.f1(); -export const result2 = func(); +export const functionInSubDir = func(); +export const functionReExportedFromSubDir = nestedFunc(); +export const nestedFunctionInSubDirOfSubDir = nestedFuncOriginal(); +export const nestedFunctionUsingFunctionFromParentDir = nestedFuncUsingParent(); diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts index 292ff42e2..1edcfdfd3 100644 --- a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts +++ b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts @@ -1,3 +1,5 @@ export function func() { return "non-node_modules import"; } + +export { nestedFunc } from "./subdirofsubdir/nestedfile"; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile2.ts b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile2.ts new file mode 100644 index 000000000..4d24080d2 --- /dev/null +++ b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile2.ts @@ -0,0 +1,3 @@ +export function func2() { + return "non-node_modules import 2"; +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/subdirofsubdir/nestedfile.ts b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/subdirofsubdir/nestedfile.ts new file mode 100644 index 000000000..b55a30218 --- /dev/null +++ b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/subdirofsubdir/nestedfile.ts @@ -0,0 +1,9 @@ +import { func2 } from "../otherfile2"; + +export function nestedFunc() { + return "nested func result"; +} + +export function nestedFuncUsingParent() { + return `nested func: ${func2()}`; +} \ No newline at end of file From a24708eb821118a38b77917772a92a2c6175bd69 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 24 May 2021 18:44:52 +0200 Subject: [PATCH 13/34] Fixed problem with lua sibling files --- src/transpilation/resolve.ts | 67 ++++++++++++------- test/transpile/module-resolution.spec.ts | 7 +- .../project-with-dependency-chain/main.ts | 1 + .../node_modules/dependency1/index.d.ts | 3 +- .../node_modules/dependency1/index.lua | 4 +- .../node_modules/dependency1/otherfile.lua | 5 ++ .../src/subdir/otherfile.ts | 2 +- .../src/subdir/otherfile2.ts | 2 +- .../src/subdir/subdirofsubdir/nestedfile.ts | 2 +- 9 files changed, 62 insertions(+), 31 deletions(-) create mode 100644 test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/otherfile.lua diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index 10de869fe..bf3367381 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -10,33 +10,40 @@ import { couldNotReadDependency, couldNotResolveRequire } from "./diagnostics"; const resolver = resolve.ResolverFactory.createResolver({ extensions: [".lua"], - enforceExtension: true, // Must be a lua file + enforceExtension: true, // Resolved file must be a lua file fileSystem: { ...new resolve.CachedInputFileSystem(fs) }, useSyncFileSystemCalls: true, }); -const projectFiles = new Map(); - interface ResolutionResult { resolvedFiles: ProcessedFile[]; diagnostics: ts.Diagnostic[]; } +// Cache for getting source files from the program +const projectFileCache = new Set(); +function isProjectFile(file: string): boolean { + // Check if file is in the project ts.program + return projectFileCache.has(path.normalize(file)); +} + export function resolveDependencies(program: ts.Program, files: ProcessedFile[], emitHost: EmitHost): ResolutionResult { - const outFiles: ProcessedFile[] = []; + const outFiles: ProcessedFile[] = [...files]; const diagnostics: ts.Diagnostic[] = []; + // Add files to project cache const projectRoot = getProjectRoot(program); for (const sourceFile of program.getSourceFiles()) { const filePath = path.isAbsolute(sourceFile.fileName) ? path.normalize(sourceFile.fileName) : path.resolve(projectRoot, sourceFile.fileName); - projectFiles.set(filePath, sourceFile.text); + projectFileCache.add(filePath); } + // Resolve dependencies for all processed files for (const file of files) { const resolutionResult = resolveFileDependencies(file, program, emitHost); - outFiles.push(file, ...resolutionResult.resolvedFiles); + outFiles.push(...resolutionResult.resolvedFiles); diagnostics.push(...resolutionResult.diagnostics); } @@ -44,9 +51,11 @@ export function resolveDependencies(program: ts.Program, files: ProcessedFile[], } function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitHost: EmitHost): ResolutionResult { - const projectRootDir = getSourceDir(program); const dependencies: ProcessedFile[] = []; const diagnostics: ts.Diagnostic[] = []; + + const projectRootDir = getSourceDir(program); + for (const required of findRequiredPaths(file.code)) { // Do no resolve lualib if (required === "lualib_bundle") { @@ -63,23 +72,23 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH // Try to resolve the import starting from the directory `file` is in const fileDir = path.dirname(file.fileName); - const resolvedDependency = resolveDependency(fileDir, projectRootDir, required); + const resolvedDependency = resolveDependency(fileDir, projectRootDir, required, emitHost); if (resolvedDependency) { - // If dependency resolved successfully, read its content - const dependencyContent = projectFiles.get(resolvedDependency) ?? emitHost.readFile(resolvedDependency); - if (dependencyContent === undefined) { - diagnostics.push(couldNotReadDependency(resolvedDependency)); - continue; - } - // Figure out resolved require path and dependency output path const resolvedRequire = getEmitPathRelativeToOutDir(resolvedDependency, program); replaceRequireInCode(file, required, resolvedRequire); replaceRequireInSourceMap(file, required, resolvedRequire); - // If dependency is not part of sources, add dependency to output and resolve its dependencies recursively - if (!projectFiles.has(resolvedDependency)) { + // If dependency is not part of project, add dependency to output and resolve its dependencies recursively + if (!isProjectFile(resolvedDependency)) { + // If dependency resolved successfully, read its content + const dependencyContent = emitHost.readFile(resolvedDependency); + if (dependencyContent === undefined) { + diagnostics.push(couldNotReadDependency(resolvedDependency)); + continue; + } + const dependency = { fileName: resolvedDependency, code: dependencyContent, @@ -100,12 +109,12 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH return { resolvedFiles: dependencies, diagnostics }; } -function replaceRequireInCode(file: ProcessedFile, originalRequire: string, newRequire: string) { +function replaceRequireInCode(file: ProcessedFile, originalRequire: string, newRequire: string): void { const requirePath = formatPathToLuaPath(newRequire.replace(".lua", "")); file.code = file.code.replace(`require("${originalRequire}")`, `require("${requirePath}")`); } -function replaceRequireInSourceMap(file: ProcessedFile, originalRequire: string, newRequire: string) { +function replaceRequireInSourceMap(file: ProcessedFile, originalRequire: string, newRequire: string): void { const requirePath = formatPathToLuaPath(newRequire.replace(".lua", "")); if (file.sourceMapNode) { replaceInSourceMap(file.sourceMapNode, file.sourceMapNode, `"${originalRequire}"`, `"${requirePath}"`); @@ -142,20 +151,32 @@ function findRequiredPaths(code: string): string[] { return paths; } -function resolveDependency(fileDirectory: string, rootDirectory: string, dependency: string): string | undefined { - // Check if file is a TS file in the project +function resolveDependency( + fileDirectory: string, + rootDirectory: string, + dependency: string, + emitHost: EmitHost +): string | undefined { + // Check if file is a file in the project const resolvedPath = path.resolve(fileDirectory, dependency); const resolvedFile = resolvedPath + ".ts"; - if (projectFiles.has(resolvedFile)) { + if (isProjectFile(resolvedFile)) { return resolvedFile; } const projectIndexPath = path.resolve(resolvedPath, "index.ts"); - if (projectFiles.has(projectIndexPath)) { + if (isProjectFile(projectIndexPath)) { return projectIndexPath; } + // Check if this is a sibling of a required lua file + const luaFilePath = path.resolve(fileDirectory, dependency + ".lua"); + if (emitHost.fileExists(luaFilePath)) { + return luaFilePath; + } + + // Not a TS file in our project sources, use resolver to check if we can find dependency try { const resolveResult = resolver.resolveSync({}, rootDirectory, dependency); if (resolveResult) { diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index d1f0c268c..52d61d74a 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -84,7 +84,7 @@ describe("basic module resolution", () => { describe("module resolution with chained dependencies", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); - const expectedResult = { result: "dependency3" }; + const expectedResult = { result: "dependency3", result2: "someFunc from otherfile.lua" }; test("can resolve dependencies in chain", () => { util.testProject(path.join(projectPath, "tsconfig.json")) @@ -103,7 +103,7 @@ describe("module resolution with chained dependencies", () => { describe("module resolution with outDir", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); - const expectedResult = { result: "dependency3" }; + const expectedResult = { result: "dependency3", result2: "someFunc from otherfile.lua" }; test("emits files in outDir", () => { const builder = util @@ -114,10 +114,11 @@ describe("module resolution with outDir", () => { // Get the output paths relative to the project path const outPaths = builder.getLuaResult().transpiledFiles.map(f => path.relative(projectPath, f.outPath)); - expect(outPaths).toHaveLength(4); + expect(outPaths).toHaveLength(5); expect(outPaths).toContain(path.join("tstl-out", "main.lua")); // Note: outputs to lua_modules expect(outPaths).toContain(path.join("tstl-out", "lua_modules", "dependency1", "index.lua")); + expect(outPaths).toContain(path.join("tstl-out", "lua_modules", "dependency1", "otherfile.lua")); expect(outPaths).toContain(path.join("tstl-out", "lua_modules", "dependency2", "index.lua")); expect(outPaths).toContain(path.join("tstl-out", "lua_modules", "dependency3", "index.lua")); }); diff --git a/test/transpile/module-resolution/project-with-dependency-chain/main.ts b/test/transpile/module-resolution/project-with-dependency-chain/main.ts index f3a3a226b..cda66cd55 100644 --- a/test/transpile/module-resolution/project-with-dependency-chain/main.ts +++ b/test/transpile/module-resolution/project-with-dependency-chain/main.ts @@ -1,3 +1,4 @@ import * as dependency1 from "dependency1"; export const result = dependency1.f1(); +export const result2 = dependency1.otherFileFromDependency1(); diff --git a/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.d.ts b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.d.ts index 761d6bc02..5155e72c5 100644 --- a/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.d.ts +++ b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.d.ts @@ -1,2 +1,3 @@ /** @noSelfInFile */ -export declare function f1(): string; \ No newline at end of file +export declare function f1(): string; +export declare function otherFileFromDependency1(): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.lua b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.lua index dcf28d6fd..80cae6fb8 100644 --- a/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.lua +++ b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/index.lua @@ -1,5 +1,7 @@ local dependency2 = require("dependency2") +local otherfile = require("otherfile") return { - f1 = function() return dependency2.f2() end + f1 = function() return dependency2.f2() end, + otherFileFromDependency1 = otherfile.someFunc } \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/otherfile.lua b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/otherfile.lua new file mode 100644 index 000000000..871964bf7 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-chain/node_modules/dependency1/otherfile.lua @@ -0,0 +1,5 @@ +return { + someFunc = function() + return "someFunc from otherfile.lua" + end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts index 1edcfdfd3..c12dee955 100644 --- a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts +++ b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile.ts @@ -2,4 +2,4 @@ export function func() { return "non-node_modules import"; } -export { nestedFunc } from "./subdirofsubdir/nestedfile"; \ No newline at end of file +export { nestedFunc } from "./subdirofsubdir/nestedfile"; diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile2.ts b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile2.ts index 4d24080d2..1132c2ea1 100644 --- a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile2.ts +++ b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/otherfile2.ts @@ -1,3 +1,3 @@ export function func2() { return "non-node_modules import 2"; -} \ No newline at end of file +} diff --git a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/subdirofsubdir/nestedfile.ts b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/subdirofsubdir/nestedfile.ts index b55a30218..d316f023e 100644 --- a/test/transpile/module-resolution/project-with-sourceDir/src/subdir/subdirofsubdir/nestedfile.ts +++ b/test/transpile/module-resolution/project-with-sourceDir/src/subdir/subdirofsubdir/nestedfile.ts @@ -6,4 +6,4 @@ export function nestedFunc() { export function nestedFuncUsingParent() { return `nested func: ${func2()}`; -} \ No newline at end of file +} From 4f8bec02fcc17a483cf69cac3711ab2410a87b92 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 24 May 2021 19:05:42 +0200 Subject: [PATCH 14/34] Also resolve JSON modules --- src/transpilation/resolve.ts | 5 +++++ test/unit/file.spec.ts | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index bf3367381..c1263a4f3 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -160,6 +160,11 @@ function resolveDependency( // Check if file is a file in the project const resolvedPath = path.resolve(fileDirectory, dependency); + if (isProjectFile(resolvedPath)) { + // JSON files need their extension as part of the import path, caught by this branch + return resolvedPath; + } + const resolvedFile = resolvedPath + ".ts"; if (isProjectFile(resolvedFile)) { return resolvedFile; diff --git a/test/unit/file.spec.ts b/test/unit/file.spec.ts index 3a810dc60..12a136408 100644 --- a/test/unit/file.spec.ts +++ b/test/unit/file.spec.ts @@ -10,6 +10,20 @@ describe("JSON", () => { .setMainFileName("main.json") .expectToEqual(new util.ExecutionError("Unexpected end of JSON input")); }); + + test("JSON modules can be imported", () => { + util.testModule` + import * as jsonData from "./jsonModule.json"; + export const result = jsonData; + ` + .addExtraFile("jsonModule.json", '{ "jsonField1": "hello, this is JSON", "jsonField2": ["a", "b", "c"] }') + .expectToEqual({ + result: { + jsonField1: "hello, this is JSON", + jsonField2: ["a", "b", "c"], + }, + }); + }); }); describe("shebang", () => { From 7b8bd80e2bc961037384863bf836b0c982b3372d Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 24 May 2021 20:59:02 +0200 Subject: [PATCH 15/34] Add debug to resolution test to try to figure out why CI is failing --- test/transpile/module-resolution.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 52d61d74a..5669d0f45 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -6,7 +6,8 @@ describe("basic module resolution", () => { const projectWithNodeModules = util .testProject(path.join(projectPath, "tsconfig.json")) - .setMainFileName(path.join(projectPath, "main.ts")); + .setMainFileName(path.join(projectPath, "main.ts")) + .debug(); test("can resolve global dependencies with declarations", () => { // Declarations in the node_modules directory @@ -152,6 +153,7 @@ describe("module resolution with sourceDir", () => { util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "src", "main.ts")) .setOptions({ outDir: "tstl-out" }) + .debug() .expectToEqual(expectedResult); }); From 5dd98f1baf5fec6372e3e882653786b33ba1d703 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 24 May 2021 21:08:20 +0200 Subject: [PATCH 16/34] fix test runner path preloading --- test/util.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/util.ts b/test/util.ts index a4f607584..0f2188527 100644 --- a/test/util.ts +++ b/test/util.ts @@ -10,7 +10,7 @@ import * as vm from "vm"; import * as tstl from "../src"; import { createEmitOutputCollector } from "../src/transpilation/output-collector"; import { getEmitOutDir, transpileProject } from "../src"; -import { normalizeSlashes } from "../src/utils"; +import { formatPathToLuaPath, normalizeSlashes } from "../src/utils"; const jsonLib = fs.readFileSync(path.join(__dirname, "json.lua"), "utf8"); const luaLib = fs.readFileSync(path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"), "utf8"); @@ -422,7 +422,7 @@ end)());`; lua.lua_getglobal(state, "package"); lua.lua_getfield(state, -1, "preload"); lauxlib.luaL_loadstring(state, fileContent); - lua.lua_setfield(state, -2, fileName.replace(".lua", "").replace(/\\/g, ".")); + lua.lua_setfield(state, -2, formatPathToLuaPath(fileName.replace(".lua", ""))); } private executeJs(): any { From 30216bc98bdf4f9612df129642f6071210a82244 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 24 May 2021 21:15:14 +0200 Subject: [PATCH 17/34] Revert "Add debug to resolution test to try to figure out why CI is failing" This reverts commit 7b8bd80e2bc961037384863bf836b0c982b3372d. --- test/transpile/module-resolution.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 5669d0f45..52d61d74a 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -6,8 +6,7 @@ describe("basic module resolution", () => { const projectWithNodeModules = util .testProject(path.join(projectPath, "tsconfig.json")) - .setMainFileName(path.join(projectPath, "main.ts")) - .debug(); + .setMainFileName(path.join(projectPath, "main.ts")); test("can resolve global dependencies with declarations", () => { // Declarations in the node_modules directory @@ -153,7 +152,6 @@ describe("module resolution with sourceDir", () => { util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "src", "main.ts")) .setOptions({ outDir: "tstl-out" }) - .debug() .expectToEqual(expectedResult); }); From 1aac59431bc06d51d1d87e9ff5eb86fe7d1ce3c5 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 24 May 2021 21:24:34 +0200 Subject: [PATCH 18/34] Changed resolution failure from error to warning --- src/transpilation/diagnostics.ts | 9 ++++++--- test/unit/modules/__snapshots__/resolution.spec.ts.snap | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/transpilation/diagnostics.ts b/src/transpilation/diagnostics.ts index 704c358b4..af971b6e3 100644 --- a/src/transpilation/diagnostics.ts +++ b/src/transpilation/diagnostics.ts @@ -1,12 +1,15 @@ import * as ts from "typescript"; import { createSerialDiagnosticFactory } from "../utils"; -const createDiagnosticFactory = (getMessage: (...args: TArgs) => string) => - createSerialDiagnosticFactory((...args: TArgs) => ({ messageText: getMessage(...args) })); +const createDiagnosticFactory = ( + getMessage: (...args: TArgs) => string, + category: ts.DiagnosticCategory = ts.DiagnosticCategory.Error +) => createSerialDiagnosticFactory((...args: TArgs) => ({ messageText: getMessage(...args), category })); export const couldNotResolveRequire = createDiagnosticFactory( (require: string, containingFile: string) => - `Could not resolve require path '${require}' in file ${containingFile}.` + `Could not resolve require path '${require}' in file ${containingFile}.`, + ts.DiagnosticCategory.Warning ); 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..37b7e6cd2 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`] = `"warning TSTL: Could not resolve require path '../module' in file main.ts."`; From fb6cc93244d1ef184f3b461f91af2d435044b3dc Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 24 May 2021 21:38:33 +0200 Subject: [PATCH 19/34] move json.lua from dist to src in benchmark --- benchmark/{dist => src}/json.lua | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename benchmark/{dist => src}/json.lua (100%) diff --git a/benchmark/dist/json.lua b/benchmark/src/json.lua similarity index 100% rename from benchmark/dist/json.lua rename to benchmark/src/json.lua From 6d833c8ae9f5eef3cd5d1ef41c93e43448df3f48 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Tue, 25 May 2021 22:29:01 +0200 Subject: [PATCH 20/34] Use commit version instead of master to compile benchmark scripts --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13df095ec..968cd9b87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,14 +76,14 @@ jobs: run: rm -rf ./master/benchmark && cp -rf ./commit/benchmark ./master/benchmark # Run master benchmark first and output to commit benchmark data - name: Build benchmark Lua 5.3 master - run: node ../dist/tstl.js -p tsconfig.53.json + run: node ../../commit/dist/tstl.js -p tsconfig.53.json working-directory: master/benchmark - name: Run benchmark Lua 5.3 master id: benchmark-lua-master run: lua5.3 -- run.lua ../../../commit/benchmark/data/benchmark_master_53.json working-directory: master/benchmark/dist - name: Build benchmark LuaJIT master - run: node ../dist/tstl.js -p tsconfig.jit.json + run: node ../../commit/dist/tstl.js -p tsconfig.jit.json working-directory: master/benchmark - name: Run benchmark LuaJIT master id: benchmark-jit-master @@ -91,14 +91,14 @@ jobs: working-directory: master/benchmark/dist # Run commit benchmark and compare with master - name: Build benchmark Lua 5.3 commit - run: node ../dist/tstl.js -p tsconfig.53.json + run: node ../../commit/dist/tstl.js -p tsconfig.53.json working-directory: commit/benchmark - name: Run benchmark Lua 5.3 commit id: benchmark-lua-commit run: lua5.3 -- run.lua ../data/benchmark_master_vs_commit_53.json ../data/benchmark_master_53.json working-directory: commit/benchmark/dist - name: Build benchmark LuaJIT commit - run: node ../dist/tstl.js -p tsconfig.jit.json + run: node ../../commit/dist/tstl.js -p tsconfig.jit.json working-directory: commit/benchmark - name: Run benchmark LuaJIT commit id: benchmark-jit-commit From 51ff390ac0b8407abd318c7836d4738055710708 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Tue, 25 May 2021 22:50:31 +0200 Subject: [PATCH 21/34] Added module resolution test project with lua sources --- test/transpile/module-resolution.spec.ts | 23 +++++++++++++++++++ .../tsconfig.json | 2 -- .../lua_sources/otherluaFile.d.ts | 2 ++ .../lua_sources/otherluaFile.lua | 3 +++ .../project-with-lua-sources/luafile.d.ts | 2 ++ .../project-with-lua-sources/luafile.lua | 3 +++ .../project-with-lua-sources/main.ts | 5 ++++ .../project-with-lua-sources/tsconfig.json | 9 ++++++++ .../project-with-sourceDir/tsconfig.json | 5 +--- 9 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.d.ts create mode 100644 test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.lua create mode 100644 test/transpile/module-resolution/project-with-lua-sources/luafile.d.ts create mode 100644 test/transpile/module-resolution/project-with-lua-sources/luafile.lua create mode 100644 test/transpile/module-resolution/project-with-lua-sources/main.ts create mode 100644 test/transpile/module-resolution/project-with-lua-sources/tsconfig.json diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 52d61d74a..22f414192 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -163,3 +163,26 @@ describe("module resolution with sourceDir", () => { .expectToEqual(expectedResult); }); }); + +describe("module resolution project with lua sources", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-lua-sources"); + const expectedResult = { + funcFromLuaFile: "lua file in subdir", + funcFromSubDirLuaFile: "lua file in subdir", + }; + + test("can resolve lua dependencies", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")) + .setOptions({ outDir: "tstl-out" }) + .expectToEqual(expectedResult); + }); + + test("can resolve dependencies and bundle files with sourceDir", () => { + const mainFile = path.join(projectPath, "main.ts"); + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual(expectedResult); + }); +}); diff --git a/test/transpile/module-resolution/project-with-dependency-chain/tsconfig.json b/test/transpile/module-resolution/project-with-dependency-chain/tsconfig.json index 935b64af6..b76533290 100644 --- a/test/transpile/module-resolution/project-with-dependency-chain/tsconfig.json +++ b/test/transpile/module-resolution/project-with-dependency-chain/tsconfig.json @@ -2,8 +2,6 @@ "compilerOptions": { "strict": true, "moduleResolution": "Node", - "noUnusedLocals": true, - "noUnusedParameters": true, "target": "esnext", "lib": ["esnext"], "types": [], diff --git a/test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.d.ts b/test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.d.ts new file mode 100644 index 000000000..8fa5bf443 --- /dev/null +++ b/test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.d.ts @@ -0,0 +1,2 @@ +/** @noSelfInFile */ +export declare function funcFromSubDir(): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.lua b/test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.lua new file mode 100644 index 000000000..a23007078 --- /dev/null +++ b/test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.lua @@ -0,0 +1,3 @@ +return { + funcFromSubDir = function() return "lua file in subdir" end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-lua-sources/luafile.d.ts b/test/transpile/module-resolution/project-with-lua-sources/luafile.d.ts new file mode 100644 index 000000000..5a16ac949 --- /dev/null +++ b/test/transpile/module-resolution/project-with-lua-sources/luafile.d.ts @@ -0,0 +1,2 @@ +/** @noSelfInFile */ +export declare function funcInLuaFile(): string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-lua-sources/luafile.lua b/test/transpile/module-resolution/project-with-lua-sources/luafile.lua new file mode 100644 index 000000000..56e61090f --- /dev/null +++ b/test/transpile/module-resolution/project-with-lua-sources/luafile.lua @@ -0,0 +1,3 @@ +return { + funcInLuaFile = function() return "lua file in subdir" end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-lua-sources/main.ts b/test/transpile/module-resolution/project-with-lua-sources/main.ts new file mode 100644 index 000000000..946f7016b --- /dev/null +++ b/test/transpile/module-resolution/project-with-lua-sources/main.ts @@ -0,0 +1,5 @@ +import { funcInLuaFile } from "./luafile"; +import { funcFromSubDir } from "./lua_sources/otherluaFile"; + +export const funcFromLuaFile = funcInLuaFile(); +export const funcFromSubDirLuaFile = funcFromSubDir(); \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-lua-sources/tsconfig.json b/test/transpile/module-resolution/project-with-lua-sources/tsconfig.json new file mode 100644 index 000000000..a07455ed7 --- /dev/null +++ b/test/transpile/module-resolution/project-with-lua-sources/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "strict": true, + "target": "esnext", + "lib": ["esnext"], + "types": [], + "outDir": "tstl-out" + } +} diff --git a/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json b/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json index 7fd18481e..200df2468 100644 --- a/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json +++ b/test/transpile/module-resolution/project-with-sourceDir/tsconfig.json @@ -2,12 +2,9 @@ "compilerOptions": { "strict": true, "moduleResolution": "Node", - "noUnusedLocals": true, - "noUnusedParameters": true, "target": "esnext", "lib": ["esnext"], "types": [], - "rootDir": "src", - "outDir": "tstl-out" + "rootDir": "src" } } From ffd8a47b83da6051d5d61ce8da415b8207d10e7d Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 29 May 2021 20:08:19 +0200 Subject: [PATCH 22/34] Add library compilation mode --- src/CompilerOptions.ts | 6 ++ src/cli/parse.ts | 8 ++- src/transpilation/resolve.ts | 47 ++++++++++------ test/cli/parse.spec.ts | 6 ++ test/transpile/module-resolution.spec.ts | 55 +++++++++++++++++++ .../lua_sources/otherluaFile.d.ts | 2 +- .../project-with-lua-sources/luafile.d.ts | 2 +- .../project-with-lua-sources/main.ts | 2 +- 8 files changed, 108 insertions(+), 20 deletions(-) diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index 439b29652..8a4fdf1ae 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -25,6 +25,7 @@ export interface LuaPluginImport { } export type CompilerOptions = OmitIndexSignature & { + compileMode?: CompileMode; noImplicitSelf?: boolean; noHeader?: boolean; luaBundle?: string; @@ -53,6 +54,11 @@ export enum LuaTarget { LuaJIT = "JIT", } +export enum CompileMode { + Application = "application", + Library = "library", +} + export const isBundleEnabled = (options: CompilerOptions) => options.luaBundle !== undefined && options.luaBundleEntry !== undefined; diff --git a/src/cli/parse.ts b/src/cli/parse.ts index fff0ef0d0..e918eac77 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -1,5 +1,5 @@ import * as ts from "typescript"; -import { CompilerOptions, LuaLibImportKind, LuaTarget } from "../CompilerOptions"; +import { CompileMode, CompilerOptions, LuaLibImportKind, LuaTarget } from "../CompilerOptions"; import * as cliDiagnostics from "./diagnostics"; export interface ParsedCommandLine extends ts.ParsedCommandLine { @@ -24,6 +24,12 @@ interface CommandLineOptionOfPrimitive extends CommandLineOptionBase { type CommandLineOption = CommandLineOptionOfEnum | CommandLineOptionOfPrimitive; export const optionDeclarations: CommandLineOption[] = [ + { + name: "compileMode", + description: "Default: application. Compiling as library will not resolve external dependencies.", + type: "enum", + choices: Object.values(CompileMode), + }, { name: "luaBundle", description: "The name of the lua file to bundle output lua to. Requires luaBundleEntry.", diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index c1263a4f3..44b932e6a 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -7,6 +7,7 @@ import { SourceNode } from "source-map"; import { getEmitPathRelativeToOutDir, getProjectRoot, getSourceDir } from "./transpiler"; import { formatPathToLuaPath } from "../utils"; import { couldNotReadDependency, couldNotResolveRequire } from "./diagnostics"; +import { CompileMode } from "../CompilerOptions"; const resolver = resolve.ResolverFactory.createResolver({ extensions: [".lua"], @@ -54,6 +55,7 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH const dependencies: ProcessedFile[] = []; const diagnostics: ts.Diagnostic[] = []; + const options = program.getCompilerOptions(); const projectRootDir = getSourceDir(program); for (const required of findRequiredPaths(file.code)) { @@ -77,11 +79,17 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH // Figure out resolved require path and dependency output path const resolvedRequire = getEmitPathRelativeToOutDir(resolvedDependency, program); - replaceRequireInCode(file, required, resolvedRequire); - replaceRequireInSourceMap(file, required, resolvedRequire); + if (!isExternalDependencyFile(resolvedDependency, program) || options.compileMode !== CompileMode.Library) { + replaceRequireInCode(file, required, resolvedRequire); + replaceRequireInSourceMap(file, required, resolvedRequire); + } // If dependency is not part of project, add dependency to output and resolve its dependencies recursively - if (!isProjectFile(resolvedDependency)) { + if ( + (isExternalDependencyFile(resolvedDependency, program) && + options.compileMode !== CompileMode.Library) || + resolvedDependency.endsWith(".lua") + ) { // If dependency resolved successfully, read its content const dependencyContent = emitHost.readFile(resolvedDependency); if (dependencyContent === undefined) { @@ -109,6 +117,19 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH return { resolvedFiles: dependencies, diagnostics }; } +function findRequiredPaths(code: string): string[] { + // Find all require("") paths in a lua code string + const paths: string[] = []; + const pattern = /require\("(.+)"\)/g; + // eslint-disable-next-line @typescript-eslint/ban-types + let match: RegExpExecArray | null; + while ((match = pattern.exec(code))) { + paths.push(match[1]); + } + + return paths; +} + function replaceRequireInCode(file: ProcessedFile, originalRequire: string, newRequire: string): void { const requirePath = formatPathToLuaPath(newRequire.replace(".lua", "")); file.code = file.code.replace(`require("${originalRequire}")`, `require("${requirePath}")`); @@ -138,19 +159,6 @@ function replaceInSourceMap(node: SourceNode, parent: SourceNode, require: strin return false; // Did not find the require } -function findRequiredPaths(code: string): string[] { - // Find all require("") paths in the code - const paths: string[] = []; - const pattern = /require\("(.+)"\)/g; - // eslint-disable-next-line @typescript-eslint/ban-types - let match: RegExpExecArray | null; - while ((match = pattern.exec(code))) { - paths.push(match[1]); - } - - return paths; -} - function resolveDependency( fileDirectory: string, rootDirectory: string, @@ -194,6 +202,13 @@ function resolveDependency( return undefined; } +function isExternalDependencyFile(filePath: string, program: ts.Program) { + const inSourceRoot = filePath.includes(path.normalize(getSourceDir(program))); + const inNodeModules = filePath.split(path.sep).some(p => p === "node_modules"); + + return !inSourceRoot || inNodeModules; +} + // Transform an import path to a lua require that is probably not correct, but can be used as fallback when regular resolution fails function fallbackResolve(required: string, projectRootDir: string, fileDir: string): string { return formatPathToLuaPath( diff --git a/test/cli/parse.spec.ts b/test/cli/parse.spec.ts index 06ed05f75..1a7baf1de 100644 --- a/test/cli/parse.spec.ts +++ b/test/cli/parse.spec.ts @@ -105,6 +105,9 @@ describe("command line", () => { ["sourceMapTraceback", "false", { sourceMapTraceback: false }], ["sourceMapTraceback", "true", { sourceMapTraceback: true }], + ["compileMode", "application", { compileMode: tstl.CompileMode.Application }], + ["compileMode", "library", { compileMode: tstl.CompileMode.Library }], + ["luaLibImport", "none", { luaLibImport: tstl.LuaLibImportKind.None }], ["luaLibImport", "always", { luaLibImport: tstl.LuaLibImportKind.Always }], ["luaLibImport", "inline", { luaLibImport: tstl.LuaLibImportKind.Inline }], @@ -213,6 +216,9 @@ describe("tsconfig", () => { ["sourceMapTraceback", false, { sourceMapTraceback: false }], ["sourceMapTraceback", true, { sourceMapTraceback: true }], + ["compileMode", "application", { compileMode: tstl.CompileMode.Application }], + ["compileMode", "library", { compileMode: tstl.CompileMode.Library }], + ["luaLibImport", "none", { luaLibImport: tstl.LuaLibImportKind.None }], ["luaLibImport", "always", { luaLibImport: tstl.LuaLibImportKind.Always }], ["luaLibImport", "inline", { luaLibImport: tstl.LuaLibImportKind.Inline }], diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 22f414192..bfd8537d4 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -1,4 +1,5 @@ import * as path from "path"; +import * as tstl from "../../src"; import * as util from "../util"; describe("basic module resolution", () => { @@ -186,3 +187,57 @@ describe("module resolution project with lua sources", () => { .expectToEqual(expectedResult); }); }); + +describe("module resolution in library mode", () => { + test("can resolve dependencies in chain", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); + + const { transpiledFiles } = util + .testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")) + .setOptions({ compileMode: tstl.CompileMode.Library }) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + for (const file of transpiledFiles) { + expect(file.lua).not.toContain('require("lua_modules'); + } + }); + + test("project works in library mode because no external dependencies", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-lua-sources"); + + const { transpiledFiles } = util + .testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")) + .setOptions({ outDir: "tstl-out", compileMode: tstl.CompileMode.Library }) + .expectToEqual({ + funcFromLuaFile: "lua file in subdir", + funcFromSubDirLuaFile: "lua file in subdir", + }) + .getLuaResult(); + + for (const file of transpiledFiles) { + expect(file.lua).not.toContain('require("lua_modules'); + } + }); + + test("bundle works in library mode because no external dependencies", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-lua-sources"); + const mainFile = path.join(projectPath, "main.ts"); + + const { transpiledFiles } = util + .testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")) + .setOptions({ compileMode: tstl.CompileMode.Library, luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual({ + funcFromLuaFile: "lua file in subdir", + funcFromSubDirLuaFile: "lua file in subdir", + }) + .getLuaResult(); + + for (const file of transpiledFiles) { + expect(file.lua).not.toContain('require("lua_modules'); + } + }); +}); diff --git a/test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.d.ts b/test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.d.ts index 8fa5bf443..6166280f2 100644 --- a/test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.d.ts +++ b/test/transpile/module-resolution/project-with-lua-sources/lua_sources/otherluaFile.d.ts @@ -1,2 +1,2 @@ /** @noSelfInFile */ -export declare function funcFromSubDir(): string; \ No newline at end of file +export declare function funcFromSubDir(): string; diff --git a/test/transpile/module-resolution/project-with-lua-sources/luafile.d.ts b/test/transpile/module-resolution/project-with-lua-sources/luafile.d.ts index 5a16ac949..6294675a1 100644 --- a/test/transpile/module-resolution/project-with-lua-sources/luafile.d.ts +++ b/test/transpile/module-resolution/project-with-lua-sources/luafile.d.ts @@ -1,2 +1,2 @@ /** @noSelfInFile */ -export declare function funcInLuaFile(): string; \ No newline at end of file +export declare function funcInLuaFile(): string; diff --git a/test/transpile/module-resolution/project-with-lua-sources/main.ts b/test/transpile/module-resolution/project-with-lua-sources/main.ts index 946f7016b..d4c19b440 100644 --- a/test/transpile/module-resolution/project-with-lua-sources/main.ts +++ b/test/transpile/module-resolution/project-with-lua-sources/main.ts @@ -2,4 +2,4 @@ import { funcInLuaFile } from "./luafile"; import { funcFromSubDir } from "./lua_sources/otherluaFile"; export const funcFromLuaFile = funcInLuaFile(); -export const funcFromSubDirLuaFile = funcFromSubDir(); \ No newline at end of file +export const funcFromSubDirLuaFile = funcFromSubDir(); From 89a046c993daeb0dfe6a3c011cde8ec26c8b973d Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 29 May 2021 21:09:10 +0200 Subject: [PATCH 23/34] renamed compilemode to buildmode --- src/CompilerOptions.ts | 6 +++--- src/cli/parse.ts | 8 ++++---- src/transpilation/resolve.ts | 7 +++---- test/cli/parse.spec.ts | 8 ++++---- test/transpile/module-resolution.spec.ts | 8 ++++---- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index 8a4fdf1ae..e06adc1f3 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -25,7 +25,7 @@ export interface LuaPluginImport { } export type CompilerOptions = OmitIndexSignature & { - compileMode?: CompileMode; + buildMode?: BuildMode; noImplicitSelf?: boolean; noHeader?: boolean; luaBundle?: string; @@ -54,8 +54,8 @@ export enum LuaTarget { LuaJIT = "JIT", } -export enum CompileMode { - Application = "application", +export enum BuildMode { + Default = "default", Library = "library", } diff --git a/src/cli/parse.ts b/src/cli/parse.ts index e918eac77..d29bf4e83 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -1,5 +1,5 @@ import * as ts from "typescript"; -import { CompileMode, CompilerOptions, LuaLibImportKind, LuaTarget } from "../CompilerOptions"; +import { BuildMode, CompilerOptions, LuaLibImportKind, LuaTarget } from "../CompilerOptions"; import * as cliDiagnostics from "./diagnostics"; export interface ParsedCommandLine extends ts.ParsedCommandLine { @@ -25,10 +25,10 @@ type CommandLineOption = CommandLineOptionOfEnum | CommandLineOptionOfPrimitive; export const optionDeclarations: CommandLineOption[] = [ { - name: "compileMode", - description: "Default: application. Compiling as library will not resolve external dependencies.", + name: "buildMode", + description: "'default' or 'library'. Compiling as library will not resolve external dependencies.", type: "enum", - choices: Object.values(CompileMode), + choices: Object.values(BuildMode), }, { name: "luaBundle", diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index 44b932e6a..6cc515d88 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -7,7 +7,7 @@ import { SourceNode } from "source-map"; import { getEmitPathRelativeToOutDir, getProjectRoot, getSourceDir } from "./transpiler"; import { formatPathToLuaPath } from "../utils"; import { couldNotReadDependency, couldNotResolveRequire } from "./diagnostics"; -import { CompileMode } from "../CompilerOptions"; +import { BuildMode } from "../CompilerOptions"; const resolver = resolve.ResolverFactory.createResolver({ extensions: [".lua"], @@ -79,15 +79,14 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH // Figure out resolved require path and dependency output path const resolvedRequire = getEmitPathRelativeToOutDir(resolvedDependency, program); - if (!isExternalDependencyFile(resolvedDependency, program) || options.compileMode !== CompileMode.Library) { + if (!isExternalDependencyFile(resolvedDependency, program) || options.buildMode !== BuildMode.Library) { replaceRequireInCode(file, required, resolvedRequire); replaceRequireInSourceMap(file, required, resolvedRequire); } // If dependency is not part of project, add dependency to output and resolve its dependencies recursively if ( - (isExternalDependencyFile(resolvedDependency, program) && - options.compileMode !== CompileMode.Library) || + (isExternalDependencyFile(resolvedDependency, program) && options.buildMode !== BuildMode.Library) || resolvedDependency.endsWith(".lua") ) { // If dependency resolved successfully, read its content diff --git a/test/cli/parse.spec.ts b/test/cli/parse.spec.ts index 1a7baf1de..f66b97766 100644 --- a/test/cli/parse.spec.ts +++ b/test/cli/parse.spec.ts @@ -105,8 +105,8 @@ describe("command line", () => { ["sourceMapTraceback", "false", { sourceMapTraceback: false }], ["sourceMapTraceback", "true", { sourceMapTraceback: true }], - ["compileMode", "application", { compileMode: tstl.CompileMode.Application }], - ["compileMode", "library", { compileMode: tstl.CompileMode.Library }], + ["buildMode", "default", { buildMode: tstl.BuildMode.Default }], + ["buildMode", "library", { buildMode: tstl.BuildMode.Library }], ["luaLibImport", "none", { luaLibImport: tstl.LuaLibImportKind.None }], ["luaLibImport", "always", { luaLibImport: tstl.LuaLibImportKind.Always }], @@ -216,8 +216,8 @@ describe("tsconfig", () => { ["sourceMapTraceback", false, { sourceMapTraceback: false }], ["sourceMapTraceback", true, { sourceMapTraceback: true }], - ["compileMode", "application", { compileMode: tstl.CompileMode.Application }], - ["compileMode", "library", { compileMode: tstl.CompileMode.Library }], + ["buildMode", "default", { buildMode: tstl.BuildMode.Default }], + ["buildMode", "library", { buildMode: tstl.BuildMode.Library }], ["luaLibImport", "none", { luaLibImport: tstl.LuaLibImportKind.None }], ["luaLibImport", "always", { luaLibImport: tstl.LuaLibImportKind.Always }], diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index bfd8537d4..d5b99c2e1 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -189,13 +189,13 @@ describe("module resolution project with lua sources", () => { }); describe("module resolution in library mode", () => { - test("can resolve dependencies in chain", () => { + test("result does not contain resolved paths", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); const { transpiledFiles } = util .testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "main.ts")) - .setOptions({ compileMode: tstl.CompileMode.Library }) + .setOptions({ buildMode: tstl.BuildMode.Library }) .expectToHaveNoDiagnostics() .getLuaResult(); @@ -210,7 +210,7 @@ describe("module resolution in library mode", () => { const { transpiledFiles } = util .testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "main.ts")) - .setOptions({ outDir: "tstl-out", compileMode: tstl.CompileMode.Library }) + .setOptions({ outDir: "tstl-out", buildMode: tstl.BuildMode.Library }) .expectToEqual({ funcFromLuaFile: "lua file in subdir", funcFromSubDirLuaFile: "lua file in subdir", @@ -229,7 +229,7 @@ describe("module resolution in library mode", () => { const { transpiledFiles } = util .testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "main.ts")) - .setOptions({ compileMode: tstl.CompileMode.Library, luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .setOptions({ buildMode: tstl.BuildMode.Library, luaBundle: "bundle.lua", luaBundleEntry: mainFile }) .expectToEqual({ funcFromLuaFile: "lua file in subdir", funcFromSubDirLuaFile: "lua file in subdir", From f50b945ecee235dfd5a8fd5ba3f1435472362ea1 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Thu, 3 Jun 2021 21:53:19 +0200 Subject: [PATCH 24/34] clean up resolve --- src/transpilation/resolve.ts | 76 ++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index 6cc515d88..d4892e667 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -4,8 +4,8 @@ import * as ts from "typescript"; import * as fs from "fs"; import { EmitHost, ProcessedFile } from "./utils"; import { SourceNode } from "source-map"; -import { getEmitPathRelativeToOutDir, getProjectRoot, getSourceDir } from "./transpiler"; -import { formatPathToLuaPath } from "../utils"; +import { getEmitPathRelativeToOutDir, getSourceDir } from "./transpiler"; +import { formatPathToLuaPath, trimExtension } from "../utils"; import { couldNotReadDependency, couldNotResolveRequire } from "./diagnostics"; import { BuildMode } from "../CompilerOptions"; @@ -21,26 +21,10 @@ interface ResolutionResult { diagnostics: ts.Diagnostic[]; } -// Cache for getting source files from the program -const projectFileCache = new Set(); -function isProjectFile(file: string): boolean { - // Check if file is in the project ts.program - return projectFileCache.has(path.normalize(file)); -} - export function resolveDependencies(program: ts.Program, files: ProcessedFile[], emitHost: EmitHost): ResolutionResult { const outFiles: ProcessedFile[] = [...files]; const diagnostics: ts.Diagnostic[] = []; - // Add files to project cache - const projectRoot = getProjectRoot(program); - for (const sourceFile of program.getSourceFiles()) { - const filePath = path.isAbsolute(sourceFile.fileName) - ? path.normalize(sourceFile.fileName) - : path.resolve(projectRoot, sourceFile.fileName); - projectFileCache.add(filePath); - } - // Resolve dependencies for all processed files for (const file of files) { const resolutionResult = resolveFileDependencies(file, program, emitHost); @@ -55,7 +39,6 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH const dependencies: ProcessedFile[] = []; const diagnostics: ts.Diagnostic[] = []; - const options = program.getCompilerOptions(); const projectRootDir = getSourceDir(program); for (const required of findRequiredPaths(file.code)) { @@ -74,21 +57,18 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH // Try to resolve the import starting from the directory `file` is in const fileDir = path.dirname(file.fileName); - const resolvedDependency = resolveDependency(fileDir, projectRootDir, required, emitHost); + const resolvedDependency = resolveDependency(fileDir, required, program, emitHost); if (resolvedDependency) { // Figure out resolved require path and dependency output path const resolvedRequire = getEmitPathRelativeToOutDir(resolvedDependency, program); - if (!isExternalDependencyFile(resolvedDependency, program) || options.buildMode !== BuildMode.Library) { + if (shouldRewriteRequires(resolvedDependency, program)) { replaceRequireInCode(file, required, resolvedRequire); replaceRequireInSourceMap(file, required, resolvedRequire); } // If dependency is not part of project, add dependency to output and resolve its dependencies recursively - if ( - (isExternalDependencyFile(resolvedDependency, program) && options.buildMode !== BuildMode.Library) || - resolvedDependency.endsWith(".lua") - ) { + if (shouldIncludeDependency(resolvedDependency, program)) { // If dependency resolved successfully, read its content const dependencyContent = emitHost.readFile(resolvedDependency); if (dependencyContent === undefined) { @@ -116,6 +96,28 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH return { resolvedFiles: dependencies, diagnostics }; } +function shouldRewriteRequires(resolvedDependency: string, program: ts.Program) { + return !isNodeModulesFile(resolvedDependency) || !isBuildModeLibrary(program); +} + +function shouldIncludeDependency(resolvedDependency: string, program: ts.Program) { + // Never include lua files (again) that are transpiled from project sources + if (!hasSourceFileInProject(resolvedDependency, program)) { + // Always include lua files not in node_modules (internal lua sources) + if (!isNodeModulesFile(resolvedDependency)) { + return true; + } else { + // Only include node_modules files if not in library mode + return !isBuildModeLibrary(program) + } + } + return false; +} + +function isBuildModeLibrary(program: ts.Program) { + return program.getCompilerOptions().buildMode === BuildMode.Library; +} + function findRequiredPaths(code: string): string[] { // Find all require("") paths in a lua code string const paths: string[] = []; @@ -160,25 +162,25 @@ function replaceInSourceMap(node: SourceNode, parent: SourceNode, require: strin function resolveDependency( fileDirectory: string, - rootDirectory: string, dependency: string, + program: ts.Program, emitHost: EmitHost ): string | undefined { // Check if file is a file in the project const resolvedPath = path.resolve(fileDirectory, dependency); - if (isProjectFile(resolvedPath)) { + if (isProjectFile(resolvedPath, program)) { // JSON files need their extension as part of the import path, caught by this branch return resolvedPath; } const resolvedFile = resolvedPath + ".ts"; - if (isProjectFile(resolvedFile)) { + if (isProjectFile(resolvedFile, program)) { return resolvedFile; } const projectIndexPath = path.resolve(resolvedPath, "index.ts"); - if (isProjectFile(projectIndexPath)) { + if (isProjectFile(projectIndexPath, program)) { return projectIndexPath; } @@ -190,7 +192,7 @@ function resolveDependency( // Not a TS file in our project sources, use resolver to check if we can find dependency try { - const resolveResult = resolver.resolveSync({}, rootDirectory, dependency); + const resolveResult = resolver.resolveSync({}, fileDirectory, dependency); if (resolveResult) { return resolveResult; } @@ -201,11 +203,17 @@ function resolveDependency( return undefined; } -function isExternalDependencyFile(filePath: string, program: ts.Program) { - const inSourceRoot = filePath.includes(path.normalize(getSourceDir(program))); - const inNodeModules = filePath.split(path.sep).some(p => p === "node_modules"); +function isNodeModulesFile(filePath: string): boolean { + return path.normalize(filePath).split(path.sep).some(p => p === "node_modules"); +} + +function isProjectFile(file: string, program: ts.Program): boolean { + return program.getSourceFile(file) !== undefined; +} - return !inSourceRoot || inNodeModules; +function hasSourceFileInProject(filePath: string, program: ts.Program) { + const pathWithoutExtension = trimExtension(filePath); + return isProjectFile(pathWithoutExtension + ".ts", program) || isProjectFile(pathWithoutExtension + ".json", program); } // Transform an import path to a lua require that is probably not correct, but can be used as fallback when regular resolution fails From 5ce4b72e4e28112ee96bbe4ef91f68c975185dd6 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 5 Jun 2021 17:49:26 +0200 Subject: [PATCH 25/34] Fix tests --- src/transpilation/bundle.ts | 2 +- src/transpilation/resolve.ts | 14 ++++++-------- src/transpilation/transpile.ts | 4 +--- test/transpile/bundle.spec.ts | 11 +++++------ test/unit/functions/noImplicitSelfOption.spec.ts | 6 +++++- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/transpilation/bundle.ts b/src/transpilation/bundle.ts index ae17e1b4f..f739b5531 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -44,7 +44,7 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t const resolvedEntryModule = path.resolve(getSourceDir(program), entryModule); const outputPath = normalizeSlashes(path.resolve(getEmitOutDir(program), bundleFile)); - if (!files.some(f => f.fileName === resolvedEntryModule)) { + if (program.getSourceFile(resolvedEntryModule) === undefined && program.getSourceFile(entryModule) === undefined) { diagnostics.push(couldNotFindBundleEntryPoint(entryModule)); return [diagnostics, { outputPath, code: "" }]; } diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index d4892e667..9e4c9ddef 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -4,7 +4,7 @@ import * as ts from "typescript"; import * as fs from "fs"; import { EmitHost, ProcessedFile } from "./utils"; import { SourceNode } from "source-map"; -import { getEmitPathRelativeToOutDir, getSourceDir } from "./transpiler"; +import { getEmitPathRelativeToOutDir, getProjectRoot, getSourceDir } from "./transpiler"; import { formatPathToLuaPath, trimExtension } from "../utils"; import { couldNotReadDependency, couldNotResolveRequire } from "./diagnostics"; import { BuildMode } from "../CompilerOptions"; @@ -39,8 +39,6 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH const dependencies: ProcessedFile[] = []; const diagnostics: ts.Diagnostic[] = []; - const projectRootDir = getSourceDir(program); - for (const required of findRequiredPaths(file.code)) { // Do no resolve lualib if (required === "lualib_bundle") { @@ -86,9 +84,9 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH } } else { // Could not resolve dependency, add a diagnostic and make some fallback path - diagnostics.push(couldNotResolveRequire(required, path.relative(projectRootDir, file.fileName))); + diagnostics.push(couldNotResolveRequire(required, path.relative(getProjectRoot(program), file.fileName))); - const fallbackRequire = fallbackResolve(required, projectRootDir, fileDir); + const fallbackRequire = fallbackResolve(required, getSourceDir(program), fileDir); replaceRequireInCode(file, required, fallbackRequire); replaceRequireInSourceMap(file, required, fallbackRequire); } @@ -167,7 +165,7 @@ function resolveDependency( emitHost: EmitHost ): string | undefined { // Check if file is a file in the project - const resolvedPath = path.resolve(fileDirectory, dependency); + const resolvedPath = path.join(fileDirectory, dependency); if (isProjectFile(resolvedPath, program)) { // JSON files need their extension as part of the import path, caught by this branch @@ -217,10 +215,10 @@ function hasSourceFileInProject(filePath: string, program: ts.Program) { } // Transform an import path to a lua require that is probably not correct, but can be used as fallback when regular resolution fails -function fallbackResolve(required: string, projectRootDir: string, fileDir: string): string { +function fallbackResolve(required: string, sourceRootDir: string, fileDir: string): string { return formatPathToLuaPath( path - .normalize(path.join(path.relative(projectRootDir, fileDir), required)) + .normalize(path.join(path.relative(sourceRootDir, fileDir), required)) .split(path.sep) .filter(s => s !== "." && s !== "..") .join(path.sep) diff --git a/src/transpilation/transpile.ts b/src/transpilation/transpile.ts index cbc77c13c..dfa1fec64 100644 --- a/src/transpilation/transpile.ts +++ b/src/transpilation/transpile.ts @@ -65,9 +65,7 @@ export function getProgramTranspileResult( diagnostics.push(...transformDiagnostics); if (!options.noEmit && !options.emitDeclarationOnly) { const printResult = printer(program, emitHost, sourceFile.fileName, file); - const sourceRootDir = program.getCommonSourceDirectory(); - const fileName = path.resolve(sourceRootDir, sourceFile.fileName); - transpiledFiles.push({ sourceFiles: [sourceFile], fileName, luaAst: file, ...printResult }); + transpiledFiles.push({ sourceFiles: [sourceFile], fileName: path.normalize(sourceFile.fileName), luaAst: file, ...printResult }); } }; diff --git a/test/transpile/bundle.spec.ts b/test/transpile/bundle.spec.ts index 0ccab7c70..69c86406e 100644 --- a/test/transpile/bundle.spec.ts +++ b/test/transpile/bundle.spec.ts @@ -1,20 +1,19 @@ import * as path from "path"; import * as util from "../util"; -import { transpileProjectResult } from "./run"; const projectDir = path.join(__dirname, "bundle"); const inputProject = path.join(projectDir, "tsconfig.json"); test("should transpile into one file", () => { - const { diagnostics, emittedFiles } = transpileProjectResult(inputProject); + const { diagnostics, transpiledFiles } = util.testProject(inputProject).getLuaResult(); expect(diagnostics).not.toHaveDiagnostics(); - expect(emittedFiles).toHaveLength(1); + expect(transpiledFiles).toHaveLength(1); - const { name, text } = emittedFiles[0]; + const { outPath, lua } = transpiledFiles[0]; // Verify the name is as specified in tsconfig - expect(name).toBe("bundle/bundle.lua"); + expect(outPath.endsWith("bundle/bundle.lua")).toBe(true); // Verify exported module by executing // Use an empty TS string because we already transpiled the TS project - util.testModule("").setLuaHeader(text).expectToEqual({ myNumber: 3 }); + util.testModule("").setLuaHeader(lua!).expectToEqual({ myNumber: 3 }); }); diff --git a/test/unit/functions/noImplicitSelfOption.spec.ts b/test/unit/functions/noImplicitSelfOption.spec.ts index f61e37cb2..2da1d9722 100644 --- a/test/unit/functions/noImplicitSelfOption.spec.ts +++ b/test/unit/functions/noImplicitSelfOption.spec.ts @@ -1,3 +1,4 @@ +import { couldNotResolveRequire } from "../../../src/transpilation/diagnostics"; import * as util from "../../util"; test("enables noSelfInFile behavior for functions", () => { @@ -31,10 +32,13 @@ test("generates declaration files with @noSelfInFile", () => { const fooDeclaration = fooBuilder.getLuaResult().transpiledFiles.find(f => f.declaration)?.declaration; util.assert(fooDeclaration !== undefined); + expect(fooDeclaration).toContain("@noSelfInFile"); + util.testModule` - import { bar } from "./foo.d"; + import { bar } from "./foo"; const test: (this: void) => void = bar; ` .addExtraFile("foo.d.ts", fooDeclaration) + .ignoreDiagnostics([couldNotResolveRequire.code]) // no foo implementation in the project to create foo.lua .expectToHaveNoDiagnostics(); }); From c28f3574030774d2f650fff888c2347d820154d8 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 5 Jun 2021 18:09:39 +0200 Subject: [PATCH 26/34] Removed old project test runner --- src/transpilation/resolve.ts | 11 ++++++++--- src/transpilation/transpile.ts | 7 ++++++- test/transpile/__snapshots__/project.spec.ts.snap | 12 ++++-------- test/transpile/project.spec.ts | 15 +++++++++++---- test/transpile/run.ts | 10 ---------- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index 9e4c9ddef..c246bcf90 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -106,7 +106,7 @@ function shouldIncludeDependency(resolvedDependency: string, program: ts.Program return true; } else { // Only include node_modules files if not in library mode - return !isBuildModeLibrary(program) + return !isBuildModeLibrary(program); } } return false; @@ -202,7 +202,10 @@ function resolveDependency( } function isNodeModulesFile(filePath: string): boolean { - return path.normalize(filePath).split(path.sep).some(p => p === "node_modules"); + return path + .normalize(filePath) + .split(path.sep) + .some(p => p === "node_modules"); } function isProjectFile(file: string, program: ts.Program): boolean { @@ -211,7 +214,9 @@ function isProjectFile(file: string, program: ts.Program): boolean { function hasSourceFileInProject(filePath: string, program: ts.Program) { const pathWithoutExtension = trimExtension(filePath); - return isProjectFile(pathWithoutExtension + ".ts", program) || isProjectFile(pathWithoutExtension + ".json", program); + return ( + isProjectFile(pathWithoutExtension + ".ts", program) || isProjectFile(pathWithoutExtension + ".json", program) + ); } // Transform an import path to a lua require that is probably not correct, but can be used as fallback when regular resolution fails diff --git a/src/transpilation/transpile.ts b/src/transpilation/transpile.ts index dfa1fec64..0d3071f73 100644 --- a/src/transpilation/transpile.ts +++ b/src/transpilation/transpile.ts @@ -65,7 +65,12 @@ export function getProgramTranspileResult( diagnostics.push(...transformDiagnostics); if (!options.noEmit && !options.emitDeclarationOnly) { const printResult = printer(program, emitHost, sourceFile.fileName, file); - transpiledFiles.push({ sourceFiles: [sourceFile], fileName: path.normalize(sourceFile.fileName), luaAst: file, ...printResult }); + transpiledFiles.push({ + sourceFiles: [sourceFile], + fileName: path.normalize(sourceFile.fileName), + luaAst: file, + ...printResult, + }); } }; diff --git a/test/transpile/__snapshots__/project.spec.ts.snap b/test/transpile/__snapshots__/project.spec.ts.snap index 4db30085f..623480405 100644 --- a/test/transpile/__snapshots__/project.spec.ts.snap +++ b/test/transpile/__snapshots__/project.spec.ts.snap @@ -3,27 +3,23 @@ exports[`should transpile 1`] = ` Array [ Object { - "name": "project/otherFile.lua", - "text": "--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]] -local ____exports = {} + "filePath": "otherFile.lua", + "lua": "local ____exports = {} function ____exports.getNumber(self) return getAPIValue() end return ____exports ", - "writeByteOrderMark": false, }, Object { - "name": "project/index.lua", - "text": "--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]] -local ____exports = {} + "filePath": "index.lua", + "lua": "local ____exports = {} local ____otherFile = require(\\"otherFile\\") local getNumber = ____otherFile.getNumber local myNumber = getNumber(nil) setAPIValue(myNumber * 5) return ____exports ", - "writeByteOrderMark": false, }, ] `; diff --git a/test/transpile/project.spec.ts b/test/transpile/project.spec.ts index 6f262dada..965c834d7 100644 --- a/test/transpile/project.spec.ts +++ b/test/transpile/project.spec.ts @@ -1,8 +1,15 @@ import * as path from "path"; -import { transpileProjectResult } from "./run"; +import * as util from "../util"; test("should transpile", () => { - const { diagnostics, emittedFiles } = transpileProjectResult(path.join(__dirname, "project", "tsconfig.json")); - expect(diagnostics).not.toHaveDiagnostics(); - expect(emittedFiles).toMatchSnapshot(); + const projectDir = path.join(__dirname, "project"); + const { transpiledFiles } = util + .testProject(path.join(projectDir, "tsconfig.json")) + .setMainFileName(path.join(projectDir, "index.ts")) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + expect( + transpiledFiles.map(f => ({ filePath: path.relative(projectDir, f.outPath), lua: f.lua })) + ).toMatchSnapshot(); }); diff --git a/test/transpile/run.ts b/test/transpile/run.ts index 8890d7a18..a24a76b9f 100644 --- a/test/transpile/run.ts +++ b/test/transpile/run.ts @@ -1,7 +1,6 @@ import * as path from "path"; import * as ts from "typescript"; import * as tstl from "../../src"; -import { parseConfigFileWithSystem } from "../../src/cli/tsconfig"; import { normalizeSlashes } from "../../src/utils"; export function transpileFilesResult(rootNames: string[], options: tstl.CompilerOptions) { @@ -16,12 +15,3 @@ export function transpileFilesResult(rootNames: string[], options: tstl.Compiler return { diagnostics, emittedFiles }; } - -export function transpileProjectResult(configFileName: string) { - const parseResult = parseConfigFileWithSystem(configFileName); - if (parseResult.errors.length > 0) { - return { diagnostics: parseResult.errors, emittedFiles: [] }; - } - - return transpileFilesResult(parseResult.fileNames, parseResult.options); -} From da6d32a1cc77a66e74395bc57a342cbcc39d7763 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 5 Jun 2021 18:41:05 +0200 Subject: [PATCH 27/34] PR comments --- src/transpilation/diagnostics.ts | 4 +- src/transpilation/resolve.ts | 86 +++++++++---------- src/transpilation/transpiler.ts | 12 +-- test/transpile/module-resolution.spec.ts | 31 +++++++ .../SUBDIR/SUBDIRFILE.ts | 3 + .../luafile.d.ts | 2 + .../luafile.lua | 3 + .../project-with-file-casing-mismatch/main.ts | 5 ++ .../tsconfig.json | 9 ++ test/util.ts | 2 +- 10 files changed, 105 insertions(+), 52 deletions(-) create mode 100644 test/transpile/module-resolution/project-with-file-casing-mismatch/SUBDIR/SUBDIRFILE.ts create mode 100644 test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.d.ts create mode 100644 test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.lua create mode 100644 test/transpile/module-resolution/project-with-file-casing-mismatch/main.ts create mode 100644 test/transpile/module-resolution/project-with-file-casing-mismatch/tsconfig.json diff --git a/src/transpilation/diagnostics.ts b/src/transpilation/diagnostics.ts index af971b6e3..c1cf5c5da 100644 --- a/src/transpilation/diagnostics.ts +++ b/src/transpilation/diagnostics.ts @@ -7,8 +7,8 @@ const createDiagnosticFactory = ( ) => createSerialDiagnosticFactory((...args: TArgs) => ({ messageText: getMessage(...args), category })); export const couldNotResolveRequire = createDiagnosticFactory( - (require: string, containingFile: string) => - `Could not resolve require path '${require}' in file ${containingFile}.`, + (requirePath: string, containingFile: string) => + `Could not resolve require path '${requirePath}' in file ${containingFile}.`, ts.DiagnosticCategory.Warning ); diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index c246bcf90..3eca25e2e 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -94,6 +94,49 @@ function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitH return { resolvedFiles: dependencies, diagnostics }; } +function resolveDependency( + fileDirectory: string, + dependency: string, + program: ts.Program, + emitHost: EmitHost +): string | undefined { + // Check if file is a file in the project + const resolvedPath = path.join(fileDirectory, dependency); + + if (isProjectFile(resolvedPath, program)) { + // JSON files need their extension as part of the import path, caught by this branch + return resolvedPath; + } + + const resolvedFile = resolvedPath + ".ts"; + if (isProjectFile(resolvedFile, program)) { + return resolvedFile; + } + + const projectIndexPath = path.resolve(resolvedPath, "index.ts"); + if (isProjectFile(projectIndexPath, program)) { + return projectIndexPath; + } + + // Check if this is a sibling of a required lua file + const luaFilePath = path.resolve(fileDirectory, dependency + ".lua"); + if (emitHost.fileExists(luaFilePath)) { + return luaFilePath; + } + + // Not a TS file in our project sources, use resolver to check if we can find dependency + try { + const resolveResult = resolver.resolveSync({}, fileDirectory, dependency); + if (resolveResult) { + return resolveResult; + } + } catch (e) { + // resolveSync errors if it fails to resolve + } + + return undefined; +} + function shouldRewriteRequires(resolvedDependency: string, program: ts.Program) { return !isNodeModulesFile(resolvedDependency) || !isBuildModeLibrary(program); } @@ -158,49 +201,6 @@ function replaceInSourceMap(node: SourceNode, parent: SourceNode, require: strin return false; // Did not find the require } -function resolveDependency( - fileDirectory: string, - dependency: string, - program: ts.Program, - emitHost: EmitHost -): string | undefined { - // Check if file is a file in the project - const resolvedPath = path.join(fileDirectory, dependency); - - if (isProjectFile(resolvedPath, program)) { - // JSON files need their extension as part of the import path, caught by this branch - return resolvedPath; - } - - const resolvedFile = resolvedPath + ".ts"; - if (isProjectFile(resolvedFile, program)) { - return resolvedFile; - } - - const projectIndexPath = path.resolve(resolvedPath, "index.ts"); - if (isProjectFile(projectIndexPath, program)) { - return projectIndexPath; - } - - // Check if this is a sibling of a required lua file - const luaFilePath = path.resolve(fileDirectory, dependency + ".lua"); - if (emitHost.fileExists(luaFilePath)) { - return luaFilePath; - } - - // Not a TS file in our project sources, use resolver to check if we can find dependency - try { - const resolveResult = resolver.resolveSync({}, fileDirectory, dependency); - if (resolveResult) { - return resolveResult; - } - } catch (e) { - // resolveSync errors if it fails to resolve - } - - return undefined; -} - function isNodeModulesFile(filePath: string): boolean { return path .normalize(filePath) diff --git a/src/transpilation/transpiler.ts b/src/transpilation/transpiler.ts index 9c6a66666..7b5eb0f3f 100644 --- a/src/transpilation/transpiler.ts +++ b/src/transpilation/transpiler.ts @@ -93,20 +93,20 @@ export function getEmitPath(file: string, program: ts.Program): string { export function getEmitPathRelativeToOutDir(fileName: string, program: ts.Program): string { const sourceDir = getSourceDir(program); // Default output path is relative path in source dir - let emitPath = path.relative(sourceDir, fileName).split(path.sep); + let emitPathSplits = path.relative(sourceDir, fileName).split(path.sep); // If source is in a parent directory of source dir, move it into the source dir - emitPath = emitPath.filter(s => s !== ".."); + emitPathSplits = emitPathSplits.filter(s => s !== ".."); // To avoid overwriting lua sources in node_modules, emit into lua_modules - if (emitPath[0] === "node_modules") { - emitPath[0] = "lua_modules"; + if (emitPathSplits[0] === "node_modules") { + emitPathSplits[0] = "lua_modules"; } // Make extension lua - emitPath[emitPath.length - 1] = trimExtension(emitPath[emitPath.length - 1]) + ".lua"; + emitPathSplits[emitPathSplits.length - 1] = trimExtension(emitPathSplits[emitPathSplits.length - 1]) + ".lua"; - return path.join(...emitPath); + return path.join(...emitPathSplits); } export function getSourceDir(program: ts.Program): string { diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index d5b99c2e1..820c361d2 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -1,6 +1,7 @@ import * as path from "path"; import * as tstl from "../../src"; import * as util from "../util"; +import * as ts from "typescript"; describe("basic module resolution", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-node-modules"); @@ -100,6 +101,13 @@ describe("module resolution with chained dependencies", () => { .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) .expectToEqual(expectedResult); }); + + test("works with different module setting", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")) + .setOptions({ module: ts.ModuleKind.ESNext }) + .expectToEqual(expectedResult); + }); }); describe("module resolution with outDir", () => { @@ -188,6 +196,29 @@ describe("module resolution project with lua sources", () => { }); }); +describe("module resolution project with import/file casing mismatch", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-file-casing-mismatch"); + const expectedResult = { + funcFromLuaFile: "lua file in subdir", + funcFromSubDirFile: "ts file in subdir", + }; + + test("can resolve lua dependencies", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")) + .setOptions({ outDir: "tstl-out" }) + .expectToEqual(expectedResult); + }); + + test("can resolve dependencies and bundle files with sourceDir", () => { + const mainFile = path.join(projectPath, "main.ts"); + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual(expectedResult); + }); +}); + describe("module resolution in library mode", () => { test("result does not contain resolved paths", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); diff --git a/test/transpile/module-resolution/project-with-file-casing-mismatch/SUBDIR/SUBDIRFILE.ts b/test/transpile/module-resolution/project-with-file-casing-mismatch/SUBDIR/SUBDIRFILE.ts new file mode 100644 index 000000000..7f9e16049 --- /dev/null +++ b/test/transpile/module-resolution/project-with-file-casing-mismatch/SUBDIR/SUBDIRFILE.ts @@ -0,0 +1,3 @@ +export function funcInSubdir() { + return "ts file in subdir"; +} diff --git a/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.d.ts b/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.d.ts new file mode 100644 index 000000000..6294675a1 --- /dev/null +++ b/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.d.ts @@ -0,0 +1,2 @@ +/** @noSelfInFile */ +export declare function funcInLuaFile(): string; diff --git a/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.lua b/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.lua new file mode 100644 index 000000000..56e61090f --- /dev/null +++ b/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.lua @@ -0,0 +1,3 @@ +return { + funcInLuaFile = function() return "lua file in subdir" end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-file-casing-mismatch/main.ts b/test/transpile/module-resolution/project-with-file-casing-mismatch/main.ts new file mode 100644 index 000000000..673e6f379 --- /dev/null +++ b/test/transpile/module-resolution/project-with-file-casing-mismatch/main.ts @@ -0,0 +1,5 @@ +import { funcInLuaFile } from "./LUAFILE"; +import { funcInSubdir } from "./subdir/subdirfile"; + +export const funcFromLuaFile = funcInLuaFile(); +export const funcFromSubDirFile = funcInSubdir(); diff --git a/test/transpile/module-resolution/project-with-file-casing-mismatch/tsconfig.json b/test/transpile/module-resolution/project-with-file-casing-mismatch/tsconfig.json new file mode 100644 index 000000000..a07455ed7 --- /dev/null +++ b/test/transpile/module-resolution/project-with-file-casing-mismatch/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "strict": true, + "target": "esnext", + "lib": ["esnext"], + "types": [], + "outDir": "tstl-out" + } +} diff --git a/test/util.ts b/test/util.ts index 0f2188527..39259e784 100644 --- a/test/util.ts +++ b/test/util.ts @@ -544,7 +544,7 @@ class ExpressionTestBuilder extends AccessorTestBuilder { class ProjectTestBuilder extends ModuleTestBuilder { constructor(private tsConfig: string) { super(""); - this.setOptions({ configFilePath: this.tsConfig }); + this.setOptions({ configFilePath: this.tsConfig, ...tstl.parseConfigFileWithSystem(this.tsConfig) }); } @memoize From d5e0217a3b568230f83ee9417c437b05250dfa17 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 5 Jun 2021 20:32:26 +0200 Subject: [PATCH 28/34] Remove file casing test --- .vscode/launch.json | 17 ++++++++++++++ test/transpile/module-resolution.spec.ts | 23 ------------------- .../SUBDIR/SUBDIRFILE.ts | 3 --- .../luafile.d.ts | 2 -- .../luafile.lua | 3 --- .../project-with-file-casing-mismatch/main.ts | 5 ---- .../tsconfig.json | 9 -------- 7 files changed, 17 insertions(+), 45 deletions(-) create mode 100644 .vscode/launch.json delete mode 100644 test/transpile/module-resolution/project-with-file-casing-mismatch/SUBDIR/SUBDIRFILE.ts delete mode 100644 test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.d.ts delete mode 100644 test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.lua delete mode 100644 test/transpile/module-resolution/project-with-file-casing-mismatch/main.ts delete mode 100644 test/transpile/module-resolution/project-with-file-casing-mismatch/tsconfig.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..a50a68074 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug current jest test", + "type": "node", + "request": "launch", + "env": { "CI": "true" }, + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/jest", + "args": ["--runInBand", "--no-cache", "--runTestsByPath", "${relativeFile}"], + "cwd": "${workspaceRoot}", + "protocol": "inspector", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + } + ] +} diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 820c361d2..68d434189 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -196,29 +196,6 @@ describe("module resolution project with lua sources", () => { }); }); -describe("module resolution project with import/file casing mismatch", () => { - const projectPath = path.resolve(__dirname, "module-resolution", "project-with-file-casing-mismatch"); - const expectedResult = { - funcFromLuaFile: "lua file in subdir", - funcFromSubDirFile: "ts file in subdir", - }; - - test("can resolve lua dependencies", () => { - util.testProject(path.join(projectPath, "tsconfig.json")) - .setMainFileName(path.join(projectPath, "main.ts")) - .setOptions({ outDir: "tstl-out" }) - .expectToEqual(expectedResult); - }); - - test("can resolve dependencies and bundle files with sourceDir", () => { - const mainFile = path.join(projectPath, "main.ts"); - util.testProject(path.join(projectPath, "tsconfig.json")) - .setMainFileName(mainFile) - .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) - .expectToEqual(expectedResult); - }); -}); - describe("module resolution in library mode", () => { test("result does not contain resolved paths", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-dependency-chain"); diff --git a/test/transpile/module-resolution/project-with-file-casing-mismatch/SUBDIR/SUBDIRFILE.ts b/test/transpile/module-resolution/project-with-file-casing-mismatch/SUBDIR/SUBDIRFILE.ts deleted file mode 100644 index 7f9e16049..000000000 --- a/test/transpile/module-resolution/project-with-file-casing-mismatch/SUBDIR/SUBDIRFILE.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function funcInSubdir() { - return "ts file in subdir"; -} diff --git a/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.d.ts b/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.d.ts deleted file mode 100644 index 6294675a1..000000000 --- a/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/** @noSelfInFile */ -export declare function funcInLuaFile(): string; diff --git a/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.lua b/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.lua deleted file mode 100644 index 56e61090f..000000000 --- a/test/transpile/module-resolution/project-with-file-casing-mismatch/luafile.lua +++ /dev/null @@ -1,3 +0,0 @@ -return { - funcInLuaFile = function() return "lua file in subdir" end -} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-file-casing-mismatch/main.ts b/test/transpile/module-resolution/project-with-file-casing-mismatch/main.ts deleted file mode 100644 index 673e6f379..000000000 --- a/test/transpile/module-resolution/project-with-file-casing-mismatch/main.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { funcInLuaFile } from "./LUAFILE"; -import { funcInSubdir } from "./subdir/subdirfile"; - -export const funcFromLuaFile = funcInLuaFile(); -export const funcFromSubDirFile = funcInSubdir(); diff --git a/test/transpile/module-resolution/project-with-file-casing-mismatch/tsconfig.json b/test/transpile/module-resolution/project-with-file-casing-mismatch/tsconfig.json deleted file mode 100644 index a07455ed7..000000000 --- a/test/transpile/module-resolution/project-with-file-casing-mismatch/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "target": "esnext", - "lib": ["esnext"], - "types": [], - "outDir": "tstl-out" - } -} From 5e68191d15fa40899d2c567ff8199e217ff8a8f2 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 6 Jun 2021 18:30:45 +0200 Subject: [PATCH 29/34] Resolution + library mode combined test --- test/transpile/module-resolution.spec.ts | 31 +++++++++++++++++++ .../dependency1-ts/d1otherfile.ts | 3 ++ .../dependency1-ts/index.ts | 5 +++ .../dependency1-ts/tsconfig.json | 9 ++++++ .../dependency2-ts/d2otherfile.ts | 3 ++ .../dependency2-ts/main.ts | 5 +++ .../dependency2-ts/tsconfig.json | 9 ++++++ .../project-with-tstl-library-modules/main.ts | 8 +++++ .../tsconfig.json | 1 + 9 files changed, 74 insertions(+) create mode 100644 test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/d1otherfile.ts create mode 100644 test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/index.ts create mode 100644 test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/tsconfig.json create mode 100644 test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/d2otherfile.ts create mode 100644 test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/main.ts create mode 100644 test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/tsconfig.json create mode 100644 test/transpile/module-resolution/project-with-tstl-library-modules/main.ts create mode 100644 test/transpile/module-resolution/project-with-tstl-library-modules/tsconfig.json diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index 68d434189..c386e2d33 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -2,6 +2,7 @@ import * as path from "path"; import * as tstl from "../../src"; import * as util from "../util"; import * as ts from "typescript"; +import { transpileProject } from "../../src"; describe("basic module resolution", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-node-modules"); @@ -249,3 +250,33 @@ describe("module resolution in library mode", () => { } }); }); + +describe("module resolution project with dependencies built by tstl library mode", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-tstl-library-modules"); + + // First compile dependencies into node_modules. NOTE: Actually writing to disk, very slow + transpileProject(path.join(projectPath, "dependency1-ts", "tsconfig.json")); + transpileProject(path.join(projectPath, "dependency2-ts", "tsconfig.json")); + + const expectedResult = { + dependency1IndexResult: "function in dependency 1 index: dependency1OtherFileFunc in dependency1/d1otherfile", + dependency1OtherFileFuncResult: "dependency1OtherFileFunc in dependency1/d1otherfile", + dependency2MainResult: "dependency 2 main", + dependency2OtherFileResult: "Dependency 2 func: my string argument", + }; + + test("can resolve lua dependencies", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.ts")) + .setOptions({ outDir: "tstl-out" }) + .expectToEqual(expectedResult); + }); + + test("can resolve dependencies and bundle", () => { + const mainFile = path.join(projectPath, "main.ts"); + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual(expectedResult); + }); +}); diff --git a/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/d1otherfile.ts b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/d1otherfile.ts new file mode 100644 index 000000000..3877a1c52 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/d1otherfile.ts @@ -0,0 +1,3 @@ +export function dependency1OtherFileFunc() { + return "dependency1OtherFileFunc in dependency1/d1otherfile"; +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/index.ts b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/index.ts new file mode 100644 index 000000000..fc1cdddec --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/index.ts @@ -0,0 +1,5 @@ +import { dependency1OtherFileFunc } from "./d1otherfile"; + +export function dependency1IndexFunc() { + return "function in dependency 1 index: " + dependency1OtherFileFunc(); +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/tsconfig.json b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/tsconfig.json new file mode 100644 index 000000000..5fcb76fbe --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "outDir": "../node_modules/dependency1", + "declaration": true + }, + "tstl": { + "buildMode": "library" + } +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/d2otherfile.ts b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/d2otherfile.ts new file mode 100644 index 000000000..63e34f8b6 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/d2otherfile.ts @@ -0,0 +1,3 @@ +export function dependency2OtherFileFunc(this: void, arg: string) { + return `Dependency 2 func: ${arg}`; +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/main.ts b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/main.ts new file mode 100644 index 000000000..03b90c231 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/main.ts @@ -0,0 +1,5 @@ +export function dependency2Main() { + return "dependency 2 main"; +} + +export * from "./d2otherfile"; diff --git a/test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/tsconfig.json b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/tsconfig.json new file mode 100644 index 000000000..f77b336a2 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency2-ts/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "outDir": "../node_modules/dependency2", + "declaration": true + }, + "tstl": { + "buildMode": "library" + } +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-modules/main.ts b/test/transpile/module-resolution/project-with-tstl-library-modules/main.ts new file mode 100644 index 000000000..c1286088a --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-modules/main.ts @@ -0,0 +1,8 @@ +import { dependency1IndexFunc } from "dependency1"; +import { dependency1OtherFileFunc } from "dependency1/d1otherfile"; +import { dependency2Main, dependency2OtherFileFunc } from "dependency2/main"; + +export const dependency1IndexResult = dependency1IndexFunc(); +export const dependency1OtherFileFuncResult = dependency1OtherFileFunc(); +export const dependency2MainResult = dependency2Main(); +export const dependency2OtherFileResult = dependency2OtherFileFunc("my string argument"); diff --git a/test/transpile/module-resolution/project-with-tstl-library-modules/tsconfig.json b/test/transpile/module-resolution/project-with-tstl-library-modules/tsconfig.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-modules/tsconfig.json @@ -0,0 +1 @@ +{} From 258d7ddb4bc2d70512e4c82af147cb68dede1e91 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 6 Jun 2021 18:54:25 +0200 Subject: [PATCH 30/34] remove out path logic from printer --- src/LuaPrinter.ts | 21 ++------------------- test/unit/printer/sourcemaps.spec.ts | 17 +++++------------ 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index 780c1ad89..1abbf77ae 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -1,4 +1,3 @@ -import * as path from "path"; import { Mapping, SourceMapGenerator, SourceNode } from "source-map"; import * as ts from "typescript"; import { CompilerOptions, LuaLibImportKind } from "./CompilerOptions"; @@ -6,7 +5,7 @@ import * as lua from "./LuaAST"; import { loadLuaLibFeatures, LuaLibFeature } from "./LuaLib"; import { isValidLuaIdentifier } from "./transformation/utils/safe-names"; import { EmitHost } from "./transpilation"; -import { intersperse, normalizeSlashes, trimExtension } from "./utils"; +import { intersperse, trimExtension } from "./utils"; // https://www.lua.org/pil/2.4.html // https://www.ecma-international.org/ecma-262/10.0/index.html#table-34 @@ -124,23 +123,7 @@ export class LuaPrinter { constructor(private emitHost: EmitHost, program: ts.Program, fileName: string) { this.options = program.getCompilerOptions(); - - // TODO remove? - if (this.options.outDir) { - const relativeFileName = path.relative(program.getCommonSourceDirectory(), fileName); - if (this.options.sourceRoot) { - // When sourceRoot is specified, just use relative path inside rootDir - this.sourceFile = relativeFileName; - } else { - // Calculate relative path from rootDir to outDir - const outputPath = path.resolve(this.options.outDir, relativeFileName); - this.sourceFile = path.relative(path.dirname(outputPath), fileName); - } - // We want forward slashes, even in windows - this.sourceFile = normalizeSlashes(this.sourceFile); - } else { - this.sourceFile = path.basename(fileName); // File will be in same dir as source - } + this.sourceFile = fileName; } public print(file: lua.File): PrintResult { diff --git a/test/unit/printer/sourcemaps.spec.ts b/test/unit/printer/sourcemaps.spec.ts index 4d4955c03..371c50df9 100644 --- a/test/unit/printer/sourcemaps.spec.ts +++ b/test/unit/printer/sourcemaps.spec.ts @@ -162,32 +162,25 @@ test.each([ }); test.each([ - { fileName: "/proj/foo.ts", config: {}, mapSource: "foo.ts", fullSource: "foo.ts" }, + { fileName: "/proj/foo.ts", config: {} }, { fileName: "/proj/src/foo.ts", config: { outDir: "/proj/dst" }, - mapSource: "../src/foo.ts", - fullSource: "../src/foo.ts", }, { fileName: "/proj/src/foo.ts", config: { rootDir: "/proj/src", outDir: "/proj/dst" }, - mapSource: "../src/foo.ts", - fullSource: "../src/foo.ts", }, { fileName: "/proj/src/sub/foo.ts", config: { rootDir: "/proj/src", outDir: "/proj/dst" }, - mapSource: "../../src/sub/foo.ts", - fullSource: "../../src/sub/foo.ts", }, { fileName: "/proj/src/sub/main.ts", config: { rootDir: "/proj/src", outDir: "/proj/dst", sourceRoot: "bin" }, - mapSource: "sub/main.ts", - fullSource: "bin/sub/main.ts", + fullSource: "bin/proj/src/sub/main.ts", }, -])("Source map has correct sources (%p)", async ({ fileName, config, mapSource, fullSource }) => { +])("Source map has correct sources (%p)", async ({ fileName, config, fullSource }) => { const file = util.testModule` const foo = "foo" ` @@ -197,11 +190,11 @@ test.each([ const sourceMap = JSON.parse(file.luaSourceMap); expect(sourceMap.sources).toHaveLength(1); - expect(sourceMap.sources[0]).toBe(mapSource); + expect(sourceMap.sources[0]).toBe(fileName); const consumer = await new SourceMapConsumer(file.luaSourceMap); expect(consumer.sources).toHaveLength(1); - expect(consumer.sources[0]).toBe(fullSource); + expect(consumer.sources[0]).toBe(fullSource ?? fileName); }); test.each([ From 5487382f2b446e0adc6e3ca079107b1c081cf764 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 12 Jun 2021 16:15:05 +0200 Subject: [PATCH 31/34] Fixed bundle entry point require not being resolved correctly --- src/transpilation/bundle.ts | 3 +-- test/unit/bundle.spec.ts | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/transpilation/bundle.ts b/src/transpilation/bundle.ts index f739b5531..147653d1a 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -46,7 +46,6 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t if (program.getSourceFile(resolvedEntryModule) === undefined && program.getSourceFile(entryModule) === undefined) { diagnostics.push(couldNotFindBundleEntryPoint(entryModule)); - return [diagnostics, { outputPath, code: "" }]; } // For each file: [""] = function() end, @@ -56,7 +55,7 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t const moduleTable = createModuleTableNode(moduleTableEntries); // return require("") - const entryPoint = `return require(${createModulePath(resolvedEntryModule, program)})\n`; + const entryPoint = `return require(${createModulePath(entryModule, program)})\n`; const bundleNode = joinSourceChunks([requireOverride, moduleTable, entryPoint]); const { code, map } = bundleNode.toStringWithSourceMap(); diff --git a/test/unit/bundle.spec.ts b/test/unit/bundle.spec.ts index d28f4bf25..f961dddb0 100644 --- a/test/unit/bundle.spec.ts +++ b/test/unit/bundle.spec.ts @@ -59,6 +59,16 @@ test("entry point in directory", () => { .expectToEqual({ value: true }); }); +test("entry point in rootDir", () => { + util.testModule` + export { value } from "./module"; + ` + .setMainFileName("src/main.ts") + .addExtraFile("src/module.ts", "export const value = true") + .setOptions({ rootDir: "src", luaBundle: "bundle.lua", luaBundleEntry: "src/main.ts" }) + .expectToEqual({ value: true }); +}); + test("LuaLibImportKind.Require", () => { util.testBundle` export const result = [1, 2]; From 2a309ce3a69e1b8d0808636f5f6d6b16dff66eae Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 13 Jun 2021 11:23:49 +0200 Subject: [PATCH 32/34] Add header to bundle --- src/LuaPrinter.ts | 4 +++- src/transpilation/bundle.ts | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index d4c79321e..390b62977 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -24,6 +24,8 @@ const escapeStringMap: Record = { export const escapeString = (value: string) => `"${value.replace(escapeStringRegExp, char => escapeStringMap[char])}"`; +export const tstlHeader = "--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]\n"; + /** * Checks that a name is valid for use in lua function declaration syntax: * @@ -185,7 +187,7 @@ export class LuaPrinter { let header = file.trivia; if (!this.options.noHeader) { - header += "--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]\n"; + header += tstlHeader; } const luaLibImport = this.options.luaLibImport ?? LuaLibImportKind.Require; diff --git a/src/transpilation/bundle.ts b/src/transpilation/bundle.ts index 147653d1a..31d58b27b 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -2,7 +2,7 @@ import * as path from "path"; import { SourceNode } from "source-map"; import * as ts from "typescript"; import { CompilerOptions } from "../CompilerOptions"; -import { escapeString } from "../LuaPrinter"; +import { escapeString, tstlHeader } from "../LuaPrinter"; import { cast, formatPathToLuaPath, isNonNull, normalizeSlashes, trimExtension } from "../utils"; import { couldNotFindBundleEntryPoint } from "./diagnostics"; import { getEmitOutDir, getEmitPathRelativeToOutDir, getSourceDir } from "./transpiler"; @@ -57,7 +57,13 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t // return require("") const entryPoint = `return require(${createModulePath(entryModule, program)})\n`; - const bundleNode = joinSourceChunks([requireOverride, moduleTable, entryPoint]); + const sourceChunks = [requireOverride, moduleTable, entryPoint]; + + if (!options.noHeader) { + sourceChunks.unshift(tstlHeader); + } + + const bundleNode = joinSourceChunks(sourceChunks); const { code, map } = bundleNode.toStringWithSourceMap(); return [ From 9188649ab0b6e3139f507599f0f204fc4466b950 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 13 Jun 2021 16:02:47 +0200 Subject: [PATCH 33/34] Made couldNotResolveRequire an error instead of warning --- src/transpilation/diagnostics.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/transpilation/diagnostics.ts b/src/transpilation/diagnostics.ts index c1cf5c5da..bfae6b140 100644 --- a/src/transpilation/diagnostics.ts +++ b/src/transpilation/diagnostics.ts @@ -8,8 +8,7 @@ const createDiagnosticFactory = ( export const couldNotResolveRequire = createDiagnosticFactory( (requirePath: string, containingFile: string) => - `Could not resolve require path '${requirePath}' in file ${containingFile}.`, - ts.DiagnosticCategory.Warning + `Could not resolve require path '${requirePath}' in file ${containingFile}.` ); export const couldNotReadDependency = createDiagnosticFactory( From ce4abf85cb34a374f950a6d3a74a95945f71cb2b Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 13 Jun 2021 16:25:40 +0200 Subject: [PATCH 34/34] updated couldnotResolveRequire snapshot --- test/unit/modules/__snapshots__/resolution.spec.ts.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/modules/__snapshots__/resolution.spec.ts.snap b/test/unit/modules/__snapshots__/resolution.spec.ts.snap index 37b7e6cd2..4db74c57f 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`] = `"warning 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 require path '../module' in file main.ts."`;