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.
- package/.github/workflows/ci.yml +39 -0
- package/.github/workflows/publish.yml +39 -0
- package/.prettierignore +3 -0
- package/.prettierrc.json +1 -0
- package/LICENSE +21 -21
- package/README.md +37 -15
- package/dist/ast2lua.js +188 -85
- package/dist/cli.js +17 -7
- package/dist/index.js +3 -19
- package/dist/linker.js +96 -0
- package/dist/minifier.js +176 -51
- package/dist/output.js +33 -0
- package/dist/renamer.js +87 -0
- package/dist/resolver.js +303 -0
- package/eslint.config.js +29 -0
- package/package.json +33 -27
- package/src/ast2lua.ts +940 -812
- package/src/cli.ts +86 -56
- package/src/linker.ts +108 -0
- package/src/minifier.ts +284 -114
- package/src/output.ts +71 -0
- package/src/renamer.ts +134 -0
- package/src/resolver.ts +378 -0
- package/test/circular-require.test.ts +19 -0
- package/test/fixtures/bare-require/main.lua +2 -0
- package/test/fixtures/bare-require/mod.lua +1 -0
- package/test/fixtures/bitwise-precedence/main.lua +10 -0
- package/test/fixtures/circular-require/a.lua +2 -0
- package/test/fixtures/circular-require/b.lua +2 -0
- package/test/fixtures/circular-require/main.lua +2 -0
- package/test/fixtures/dofile/greet.lua +1 -0
- package/test/fixtures/dofile/main.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_alpha.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_bravo.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_charlie.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_delta.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_echo.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_foxtrot.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_golf.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_hotel.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_india.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_juliet.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/dep_kilo.lua +2 -0
- package/test/fixtures/entry-scope-many-requires/main.lua +25 -0
- package/test/fixtures/multi-require/common.lua +1 -0
- package/test/fixtures/multi-require/main.lua +3 -0
- package/test/fixtures/nested-module/main.lua +2 -0
- package/test/fixtures/nested-module/sub/deep.lua +1 -0
- package/test/fixtures/require-call/main.lua +2 -0
- package/test/fixtures/require-call/mod.lua +5 -0
- package/test/fixtures/require-in-expression/main.lua +1 -0
- package/test/fixtures/require-in-expression/mod.lua +1 -0
- package/test/fixtures/require-string-call/main.lua +2 -0
- package/test/fixtures/require-string-call/mod.lua +5 -0
- package/test/fixtures/single-file/main.lua +15 -0
- package/test/identifier-collision.test.ts +28 -0
- package/test/lib/collision.ts +131 -0
- package/test/lib/helpers.ts +124 -0
- package/test/no-rename.test.ts +19 -0
- package/test/output.test.ts +95 -0
- package/test/precedence.test.ts +28 -0
- package/test/renamer.test.ts +130 -0
- package/test/resolver.test.ts +206 -0
- package/test/roundtrip.test.ts +26 -0
- package/test/snapshot.test.ts +27 -0
- package/test/snapshots/bare-require.sl.lua +1 -0
- package/test/snapshots/bitwise-precedence.sl.lua +2 -0
- package/test/snapshots/dofile.sl.lua +1 -0
- package/test/snapshots/entry-scope-many-requires.m.lua +14 -0
- package/test/snapshots/multi-require.m.lua +4 -0
- package/test/snapshots/multi-require.sl.lua +1 -0
- package/test/snapshots/nested-module.m.lua +4 -0
- package/test/snapshots/require-call.m.lua +5 -0
- package/test/snapshots/require-call.sl.lua +1 -0
- package/test/snapshots/require-in-expression.sl.lua +1 -0
- package/test/snapshots/require-string-call.m.lua +5 -0
- package/test/snapshots/single-file.sl.lua +6 -0
- package/test/sourcemap.test.ts +105 -0
- package/tsconfig.eslint.json +8 -0
- package/tsconfig.json +111 -109
- 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 @@
|
|
|
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,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
|
+
});
|