storm-lua-minify 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/.github/workflows/ci.yml +39 -0
  2. package/.github/workflows/publish.yml +39 -0
  3. package/.prettierignore +3 -0
  4. package/.prettierrc.json +1 -0
  5. package/LICENSE +21 -21
  6. package/README.md +37 -15
  7. package/dist/ast2lua.js +188 -85
  8. package/dist/cli.js +17 -7
  9. package/dist/index.js +3 -19
  10. package/dist/linker.js +96 -0
  11. package/dist/minifier.js +176 -51
  12. package/dist/output.js +33 -0
  13. package/dist/renamer.js +87 -0
  14. package/dist/resolver.js +303 -0
  15. package/eslint.config.js +29 -0
  16. package/package.json +33 -27
  17. package/src/ast2lua.ts +940 -812
  18. package/src/cli.ts +86 -56
  19. package/src/linker.ts +108 -0
  20. package/src/minifier.ts +284 -114
  21. package/src/output.ts +71 -0
  22. package/src/renamer.ts +134 -0
  23. package/src/resolver.ts +378 -0
  24. package/test/circular-require.test.ts +19 -0
  25. package/test/fixtures/bare-require/main.lua +2 -0
  26. package/test/fixtures/bare-require/mod.lua +1 -0
  27. package/test/fixtures/bitwise-precedence/main.lua +10 -0
  28. package/test/fixtures/circular-require/a.lua +2 -0
  29. package/test/fixtures/circular-require/b.lua +2 -0
  30. package/test/fixtures/circular-require/main.lua +2 -0
  31. package/test/fixtures/dofile/greet.lua +1 -0
  32. package/test/fixtures/dofile/main.lua +2 -0
  33. package/test/fixtures/entry-scope-many-requires/dep_alpha.lua +2 -0
  34. package/test/fixtures/entry-scope-many-requires/dep_bravo.lua +2 -0
  35. package/test/fixtures/entry-scope-many-requires/dep_charlie.lua +2 -0
  36. package/test/fixtures/entry-scope-many-requires/dep_delta.lua +2 -0
  37. package/test/fixtures/entry-scope-many-requires/dep_echo.lua +2 -0
  38. package/test/fixtures/entry-scope-many-requires/dep_foxtrot.lua +2 -0
  39. package/test/fixtures/entry-scope-many-requires/dep_golf.lua +2 -0
  40. package/test/fixtures/entry-scope-many-requires/dep_hotel.lua +2 -0
  41. package/test/fixtures/entry-scope-many-requires/dep_india.lua +2 -0
  42. package/test/fixtures/entry-scope-many-requires/dep_juliet.lua +2 -0
  43. package/test/fixtures/entry-scope-many-requires/dep_kilo.lua +2 -0
  44. package/test/fixtures/entry-scope-many-requires/main.lua +25 -0
  45. package/test/fixtures/multi-require/common.lua +1 -0
  46. package/test/fixtures/multi-require/main.lua +3 -0
  47. package/test/fixtures/nested-module/main.lua +2 -0
  48. package/test/fixtures/nested-module/sub/deep.lua +1 -0
  49. package/test/fixtures/require-call/main.lua +2 -0
  50. package/test/fixtures/require-call/mod.lua +5 -0
  51. package/test/fixtures/require-in-expression/main.lua +1 -0
  52. package/test/fixtures/require-in-expression/mod.lua +1 -0
  53. package/test/fixtures/require-string-call/main.lua +2 -0
  54. package/test/fixtures/require-string-call/mod.lua +5 -0
  55. package/test/fixtures/single-file/main.lua +15 -0
  56. package/test/identifier-collision.test.ts +28 -0
  57. package/test/lib/collision.ts +131 -0
  58. package/test/lib/helpers.ts +124 -0
  59. package/test/no-rename.test.ts +19 -0
  60. package/test/output.test.ts +95 -0
  61. package/test/precedence.test.ts +28 -0
  62. package/test/renamer.test.ts +130 -0
  63. package/test/resolver.test.ts +206 -0
  64. package/test/roundtrip.test.ts +26 -0
  65. package/test/snapshot.test.ts +27 -0
  66. package/test/snapshots/bare-require.sl.lua +1 -0
  67. package/test/snapshots/bitwise-precedence.sl.lua +2 -0
  68. package/test/snapshots/dofile.sl.lua +1 -0
  69. package/test/snapshots/entry-scope-many-requires.m.lua +14 -0
  70. package/test/snapshots/multi-require.m.lua +4 -0
  71. package/test/snapshots/multi-require.sl.lua +1 -0
  72. package/test/snapshots/nested-module.m.lua +4 -0
  73. package/test/snapshots/require-call.m.lua +5 -0
  74. package/test/snapshots/require-call.sl.lua +1 -0
  75. package/test/snapshots/require-in-expression.sl.lua +1 -0
  76. package/test/snapshots/require-string-call.m.lua +5 -0
  77. package/test/snapshots/single-file.sl.lua +6 -0
  78. package/test/sourcemap.test.ts +105 -0
  79. package/tsconfig.eslint.json +8 -0
  80. package/tsconfig.json +111 -109
  81. package/.eslintrc.json +0 -20
@@ -0,0 +1,206 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import Parser from "luaparse";
4
+ import { resolveScopes } from "../src/resolver";
5
+
6
+ // Resolveパス(#19)の単体テスト。宣言と参照の対応、シャドーイング、グローバル判定を検証する。
7
+ // このパスは出力を変更しないため、ここでは解析結果のみを検証する。
8
+
9
+ function parse(code: string): Parser.Chunk {
10
+ return Parser.parse(code, { luaVersion: "5.3" });
11
+ }
12
+
13
+ void test("resolves references to their declaring local symbol", () => {
14
+ const chunk = parse(`
15
+ local x = 1
16
+ print(x)
17
+ x = 2
18
+ `);
19
+ const result = resolveScopes(chunk);
20
+
21
+ const declaration = (chunk.body[0] as Parser.LocalStatement).variables[0];
22
+ const declSymbol = result.symbolOf(declaration);
23
+ assert.ok(declSymbol);
24
+ assert.equal(declSymbol.kind, "local");
25
+
26
+ const printArg = (
27
+ (chunk.body[1] as Parser.CallStatement).expression as Parser.CallExpression
28
+ ).arguments[0] as Parser.Identifier;
29
+ assert.equal(result.symbolOf(printArg), declSymbol);
30
+
31
+ const assignTarget = (chunk.body[2] as Parser.AssignmentStatement)
32
+ .variables[0] as Parser.Identifier;
33
+ assert.equal(result.symbolOf(assignTarget), declSymbol);
34
+
35
+ assert.deepEqual(
36
+ declSymbol.references.map((r) => r.name),
37
+ ["x", "x"],
38
+ );
39
+ });
40
+
41
+ void test("shadowed locals in a nested block resolve to a distinct symbol", () => {
42
+ const chunk = parse(`
43
+ local x = 1
44
+ do
45
+ local x = 2
46
+ print(x)
47
+ end
48
+ print(x)
49
+ `);
50
+ const result = resolveScopes(chunk);
51
+
52
+ const outerDecl = (chunk.body[0] as Parser.LocalStatement).variables[0];
53
+ const outerSymbol = result.symbolOf(outerDecl);
54
+
55
+ const doStatement = chunk.body[1] as Parser.DoStatement;
56
+ const innerDecl = (doStatement.body[0] as Parser.LocalStatement).variables[0];
57
+ const innerSymbol = result.symbolOf(innerDecl);
58
+
59
+ assert.ok(outerSymbol);
60
+ assert.ok(innerSymbol);
61
+ assert.notEqual(innerSymbol, outerSymbol);
62
+
63
+ const innerPrintArg = (
64
+ (doStatement.body[1] as Parser.CallStatement)
65
+ .expression as Parser.CallExpression
66
+ ).arguments[0] as Parser.Identifier;
67
+ assert.equal(result.symbolOf(innerPrintArg), innerSymbol);
68
+
69
+ const outerPrintArg = (
70
+ (chunk.body[2] as Parser.CallStatement).expression as Parser.CallExpression
71
+ ).arguments[0] as Parser.Identifier;
72
+ assert.equal(result.symbolOf(outerPrintArg), outerSymbol);
73
+ });
74
+
75
+ void test("re-declaring a local in the same block shadows only subsequent references", () => {
76
+ const chunk = parse(`
77
+ local x = 1
78
+ print(x)
79
+ local x = 2
80
+ print(x)
81
+ `);
82
+ const result = resolveScopes(chunk);
83
+
84
+ const firstSymbol = result.symbolOf(
85
+ (chunk.body[0] as Parser.LocalStatement).variables[0],
86
+ );
87
+ const secondSymbol = result.symbolOf(
88
+ (chunk.body[2] as Parser.LocalStatement).variables[0],
89
+ );
90
+ assert.ok(firstSymbol);
91
+ assert.ok(secondSymbol);
92
+ assert.notEqual(firstSymbol, secondSymbol);
93
+
94
+ const firstPrintArg = (
95
+ (chunk.body[1] as Parser.CallStatement).expression as Parser.CallExpression
96
+ ).arguments[0] as Parser.Identifier;
97
+ assert.equal(result.symbolOf(firstPrintArg), firstSymbol);
98
+
99
+ const secondPrintArg = (
100
+ (chunk.body[3] as Parser.CallStatement).expression as Parser.CallExpression
101
+ ).arguments[0] as Parser.Identifier;
102
+ assert.equal(result.symbolOf(secondPrintArg), secondSymbol);
103
+ });
104
+
105
+ void test("a function parameter shadows an outer local of the same name", () => {
106
+ const chunk = parse(`
107
+ local x = 1
108
+ local function f(x)
109
+ return x
110
+ end
111
+ `);
112
+ const result = resolveScopes(chunk);
113
+
114
+ const outerSymbol = result.symbolOf(
115
+ (chunk.body[0] as Parser.LocalStatement).variables[0],
116
+ );
117
+ const fnDecl = chunk.body[1] as Parser.FunctionDeclaration;
118
+ const paramSymbol = result.symbolOf(
119
+ fnDecl.parameters[0] as Parser.Identifier,
120
+ );
121
+ assert.ok(outerSymbol);
122
+ assert.ok(paramSymbol);
123
+ assert.notEqual(paramSymbol, outerSymbol);
124
+ assert.equal(paramSymbol.kind, "param");
125
+
126
+ const returnArg = (fnDecl.body[0] as Parser.ReturnStatement)
127
+ .arguments[0] as Parser.Identifier;
128
+ assert.equal(result.symbolOf(returnArg), paramSymbol);
129
+ });
130
+
131
+ void test("a local function can refer to itself recursively", () => {
132
+ const chunk = parse(`
133
+ local function fact(n)
134
+ if n <= 1 then return 1 end
135
+ return n * fact(n - 1)
136
+ end
137
+ `);
138
+ const result = resolveScopes(chunk);
139
+
140
+ const fnDecl = chunk.body[0] as Parser.FunctionDeclaration;
141
+ const declSymbol = result.symbolOf(fnDecl.identifier as Parser.Identifier);
142
+ assert.ok(declSymbol);
143
+ assert.equal(declSymbol.kind, "local");
144
+
145
+ const returnStatement = fnDecl.body[1] as Parser.ReturnStatement;
146
+ const multiplyExpr = returnStatement.arguments[0] as Parser.BinaryExpression;
147
+ const callExpr = multiplyExpr.right as Parser.CallExpression;
148
+ const callee = callExpr.base as Parser.Identifier;
149
+ assert.equal(result.symbolOf(callee), declSymbol);
150
+ });
151
+
152
+ void test("a numeric for-loop variable is scoped to the loop body only", () => {
153
+ const chunk = parse(`
154
+ for i = 1, 10 do
155
+ print(i)
156
+ end
157
+ print(i)
158
+ `);
159
+ const result = resolveScopes(chunk);
160
+
161
+ const forStatement = chunk.body[0] as Parser.ForNumericStatement;
162
+ const loopVarSymbol = result.symbolOf(forStatement.variable);
163
+ assert.ok(loopVarSymbol);
164
+ assert.equal(loopVarSymbol.kind, "for");
165
+
166
+ const insideArg = (
167
+ (forStatement.body[0] as Parser.CallStatement)
168
+ .expression as Parser.CallExpression
169
+ ).arguments[0] as Parser.Identifier;
170
+ assert.equal(result.symbolOf(insideArg), loopVarSymbol);
171
+
172
+ // ループの外側にある同名の参照は、ループ変数のシンボルとは無関係でグローバル扱いになる
173
+ const afterArg = (
174
+ (chunk.body[1] as Parser.CallStatement).expression as Parser.CallExpression
175
+ ).arguments[0] as Parser.Identifier;
176
+ assert.equal(result.symbolOf(afterArg), undefined);
177
+ assert.ok(result.globals.has("i"));
178
+ });
179
+
180
+ void test("unresolved identifiers are collected as globals, not symbols, and field/key names are ignored", () => {
181
+ const chunk = parse(`
182
+ screen.setColor(1, 2, 3)
183
+ local w, h = screen.getWidth(), screen.getHeight()
184
+ local t = { x = 1 }
185
+ `);
186
+ const result = resolveScopes(chunk);
187
+
188
+ // "screen" はどこにも宣言されていないためグローバル参照が3回集計される
189
+ const screenBinding = result.globals.get("screen");
190
+ assert.ok(screenBinding);
191
+ assert.equal(screenBinding.references.length, 3);
192
+
193
+ // フィールド名(setColor/getWidth/getHeight)やテーブルキー名(x)は
194
+ // 変数参照ではないため、グローバルにもシンボルにも現れない
195
+ assert.equal(result.globals.size, 1);
196
+ assert.ok(!result.symbols.some((s) => s.name === "screen"));
197
+ assert.ok(!result.symbols.some((s) => s.name === "x"));
198
+
199
+ assert.deepEqual(
200
+ result.symbols
201
+ .filter((s) => s.kind === "local")
202
+ .map((s) => s.name)
203
+ .sort(),
204
+ ["h", "t", "w"],
205
+ );
206
+ });
@@ -0,0 +1,26 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import Parser from "luaparse";
4
+ import { WORKING_CASES, KNOWN_BUG_CASES, runMinifier } from "./lib/helpers";
5
+
6
+ // minifyされたコードが再度luaparseでパース可能であることを検証する
7
+ // (luaparseが例外を投げないこと = 少なくとも構文として壊れていないこと)。
8
+
9
+ for (const c of WORKING_CASES) {
10
+ void test(`round-trip parse: ${c.label}`, () => {
11
+ const { code } = runMinifier(c);
12
+ assert.doesNotThrow(() => Parser.parse(code, { luaVersion: "5.3" }));
13
+ });
14
+ }
15
+
16
+ for (const c of KNOWN_BUG_CASES) {
17
+ const issue = String(c.issue);
18
+ void test(
19
+ `round-trip parse (known bug, issue #${issue}): ${c.label}`,
20
+ { todo: `#${issue} の本修正待ち` },
21
+ () => {
22
+ const { code } = runMinifier(c);
23
+ assert.doesNotThrow(() => Parser.parse(code, { luaVersion: "5.3" }));
24
+ },
25
+ );
26
+ }
@@ -0,0 +1,27 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { WORKING_CASES, runMinifier, slug, SNAPSHOTS_DIR } from "./lib/helpers";
6
+
7
+ // UPDATE_SNAPSHOTS=1 npm test でゴールデンファイルを更新できる。
8
+ const UPDATE = process.env.UPDATE_SNAPSHOTS === "1";
9
+
10
+ for (const c of WORKING_CASES) {
11
+ void test(`snapshot: ${c.label}`, () => {
12
+ const { code } = runMinifier(c);
13
+ const snapshotPath = path.join(SNAPSHOTS_DIR, `${slug(c)}.lua`);
14
+
15
+ if (UPDATE) {
16
+ fs.writeFileSync(snapshotPath, code);
17
+ return;
18
+ }
19
+
20
+ assert.ok(
21
+ fs.existsSync(snapshotPath),
22
+ `スナップショットが存在しません: ${snapshotPath}\nUPDATE_SNAPSHOTS=1 npm test で生成してください。`,
23
+ );
24
+ const expected = fs.readFileSync(snapshotPath, "utf8");
25
+ assert.equal(code, expected);
26
+ });
27
+ }
@@ -0,0 +1 @@
1
+ print("hello from mod")print("done")
@@ -0,0 +1,2 @@
1
+ local a,b,c=1,2,3
2
+ print(a|b&c)print((a|b)&c)print(a&b|c)print(a~b&c)print(a<<b+c)print((a<<b)+c)print(a//b//c)print(~a&b)
@@ -0,0 +1 @@
1
+ print("hello from greet")print("done")
@@ -0,0 +1,14 @@
1
+ function require(m,r)package=package or{loaded={}};if package.loaded[m]then return package.loaded[m]end
2
+ if m=="dep_alpha"then r=(function() local a="dep_alpha"return{id=a} end)()end
3
+ if m=="dep_bravo"then r=(function() local b="dep_bravo"return{id=b} end)()end
4
+ if m=="dep_charlie"then r=(function() local c="dep_charlie"return{id=c} end)()end
5
+ if m=="dep_delta"then r=(function() local d="dep_delta"return{id=d} end)()end
6
+ if m=="dep_echo"then r=(function() local e="dep_echo"return{id=e} end)()end
7
+ if m=="dep_foxtrot"then r=(function() local f="dep_foxtrot"return{id=f} end)()end
8
+ if m=="dep_golf"then r=(function() local g="dep_golf"return{id=g} end)()end
9
+ if m=="dep_hotel"then r=(function() local h="dep_hotel"return{id=h} end)()end
10
+ if m=="dep_india"then r=(function() local i="dep_india"return{id=i} end)()end
11
+ if m=="dep_juliet"then r=(function() local j="dep_juliet"return{id=j} end)()end
12
+ if m=="dep_kilo"then r=(function() local k="dep_kilo"return{id=k} end)()end
13
+ package.loaded[m]=package.loaded[m]or r or true;return package.loaded[m]end
14
+ local l=require("dep_alpha")local m=require("dep_bravo")local n=require("dep_charlie")local o=require("dep_delta")local p=require("dep_echo")local q=require("dep_foxtrot")local r=require("dep_golf")local s=require("dep_hotel")local t=require("dep_india")local u=require("dep_juliet")local v=require("dep_kilo")print(l,m,n,o,p,q,r,s,t,u,v)
@@ -0,0 +1,4 @@
1
+ function require(m,r)package=package or{loaded={}};if package.loaded[m]then return package.loaded[m]end
2
+ if m=="common"then r=(function() return{value=42} end)()end
3
+ package.loaded[m]=package.loaded[m]or r or true;return package.loaded[m]end
4
+ local a=require("common")local b=require("common")print(a.value,b.value)
@@ -0,0 +1 @@
1
+ local a={value=42}local b={value=42}print(a.value,b.value)
@@ -0,0 +1,4 @@
1
+ function require(m,r)package=package or{loaded={}};if package.loaded[m]then return package.loaded[m]end
2
+ if m=="sub.deep"then r=(function() return{value=1} end)()end
3
+ package.loaded[m]=package.loaded[m]or r or true;return package.loaded[m]end
4
+ local a=require("sub.deep")print(a.value)
@@ -0,0 +1,5 @@
1
+ function require(m,r)package=package or{loaded={}};if package.loaded[m]then return package.loaded[m]end
2
+ if m=="mod"then r=(function() local function a()return"hello"end
3
+ return{hello=a} end)()end
4
+ package.loaded[m]=package.loaded[m]or r or true;return package.loaded[m]end
5
+ local b=require("mod")print(b.hello())
@@ -0,0 +1 @@
1
+ local function a()return"hello"end local b={hello=a}print(b.hello())
@@ -0,0 +1 @@
1
+ print(((function() return{hello=function()return"hello"end} end)()).hello())
@@ -0,0 +1,5 @@
1
+ function require(m,r)package=package or{loaded={}};if package.loaded[m]then return package.loaded[m]end
2
+ if m=="mod"then r=(function() local function a()return"hello"end
3
+ return{hello=a} end)()end
4
+ package.loaded[m]=package.loaded[m]or r or true;return package.loaded[m]end
5
+ local b=require"mod"print(b.hello())
@@ -0,0 +1,6 @@
1
+ local function d(b,e)return b+e end
2
+ local c=0
3
+ for b=1,10 do c=d(c,b)end
4
+ local a=0
5
+ while a<3 do a=a+1 end
6
+ print(c,a)
@@ -0,0 +1,105 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import fs from "fs";
4
+ import { SourceMapConsumer } from "source-map";
5
+ import { fixtureEntryPath, runMinifier } from "./lib/helpers";
6
+
7
+ // 生成コード中の`needle`が何回目に出現する行・列(source-map準拠: 行は1始まり、
8
+ // 列は0始まり)かを求める。複数モジュールが1つの出力にまとめられるケースで、
9
+ // 由来モジュールごとに異なる出現箇所を指定できるようにするため。
10
+ function locateInGenerated(
11
+ code: string,
12
+ needle: string,
13
+ occurrence = 0,
14
+ ): { line: number; column: number } {
15
+ const lines = code.split("\n");
16
+ let seen = 0;
17
+ for (let i = 0; i < lines.length; i++) {
18
+ let from = 0;
19
+ for (;;) {
20
+ const column = lines[i].indexOf(needle, from);
21
+ if (column === -1) {
22
+ break;
23
+ }
24
+ if (seen === occurrence) {
25
+ return { line: i + 1, column };
26
+ }
27
+ seen++;
28
+ from = column + 1;
29
+ }
30
+ }
31
+ throw new Error(
32
+ `"${needle}" (occurrence ${String(occurrence)}) not found in generated code:\n${code}`,
33
+ );
34
+ }
35
+
36
+ void test("sourcemap: mapファイルにfileフィールドが設定される", () => {
37
+ const { map } = runMinifier({
38
+ fixture: "multi-require",
39
+ mode: { moduleLikeLua: true },
40
+ });
41
+ assert.equal(map.file, "multi-require.min.lua");
42
+ });
43
+
44
+ void test("sourcemap: sourcesContentに各モジュールの元テキストがそのまま埋め込まれる", async () => {
45
+ const { map } = runMinifier({
46
+ fixture: "multi-require",
47
+ mode: { moduleLikeLua: true },
48
+ });
49
+
50
+ await SourceMapConsumer.with(map, null, (consumer) => {
51
+ assert.ok(consumer.hasContentsOfAllSources());
52
+ const mainContent = consumer.sourceContentFor("main.lua");
53
+ const commonContent = consumer.sourceContentFor("common.lua");
54
+ assert.equal(
55
+ mainContent,
56
+ fs.readFileSync(fixtureEntryPath("multi-require", "main.lua"), "utf8"),
57
+ );
58
+ assert.equal(
59
+ commonContent,
60
+ fs.readFileSync(fixtureEntryPath("multi-require", "common.lua"), "utf8"),
61
+ );
62
+ });
63
+ });
64
+
65
+ void test("sourcemap: 別モジュール由来のトークンがそれぞれ正しい元ファイル・位置にマップされる", async () => {
66
+ const { code, map } = runMinifier({
67
+ fixture: "multi-require",
68
+ mode: { moduleLikeLua: false },
69
+ });
70
+ // SLモードでは同一モジュールへの多重requireがそれぞれ独立して展開されるため、
71
+ // "local a={value=42}local b={value=42}print(a.value,b.value)" のような形になる
72
+ // (test/snapshots/multi-require.sl.lua 参照)。
73
+
74
+ await SourceMapConsumer.with(map, null, (consumer) => {
75
+ // common.lua由来: 1回目のインライン展開の `42`
76
+ const firstValue = locateInGenerated(code, "42", 0);
77
+ const firstValuePos = consumer.originalPositionFor(firstValue);
78
+ assert.equal(firstValuePos.source, "common.lua");
79
+ assert.equal(firstValuePos.line, 1);
80
+
81
+ // common.lua由来: 2回目のインライン展開の `42`(1回目とは別のSourceNodeインスタンス)
82
+ const secondValue = locateInGenerated(code, "42", 1);
83
+ const secondValuePos = consumer.originalPositionFor(secondValue);
84
+ assert.equal(secondValuePos.source, "common.lua");
85
+ assert.equal(secondValuePos.line, 1);
86
+
87
+ // main.lua由来: print呼び出し
88
+ const printCall = locateInGenerated(code, "print");
89
+ const printPos = consumer.originalPositionFor(printCall);
90
+ assert.equal(printPos.source, "main.lua");
91
+ assert.equal(printPos.line, 3);
92
+ assert.equal(printPos.name, "print");
93
+ });
94
+ });
95
+
96
+ void test("sourcemap: ドット区切りモジュール名のsourcesはOSに依存せず'/'区切りになる", () => {
97
+ const { map } = runMinifier({
98
+ fixture: "nested-module",
99
+ mode: { moduleLikeLua: true },
100
+ });
101
+ assert.ok(
102
+ map.sources.includes("sub/deep.lua"),
103
+ `sources に "sub/deep.lua" が含まれていること。実際: ${JSON.stringify(map.sources)}`,
104
+ );
105
+ });
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./",
5
+ "noEmit": true
6
+ },
7
+ "include": ["src/**/*.ts", "test/**/*.ts"]
8
+ }