storm-lua-minify 0.1.3 → 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 +16 -6
- package/dist/index.js +3 -19
- package/dist/linker.js +96 -0
- package/dist/minifier.js +174 -53
- 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 -118
- 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,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- develop
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
- develop
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build-lint-test:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Setup Node.js
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: "22.x"
|
|
24
|
+
cache: "npm"
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: npm ci
|
|
28
|
+
|
|
29
|
+
- name: Build
|
|
30
|
+
run: npm run build
|
|
31
|
+
|
|
32
|
+
- name: Lint
|
|
33
|
+
run: npm run lint
|
|
34
|
+
|
|
35
|
+
- name: Check formatting
|
|
36
|
+
run: npm run format:check
|
|
37
|
+
|
|
38
|
+
- name: Test
|
|
39
|
+
run: npm test
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
# NOTE: npm publish is disabled for now (see issue #10).
|
|
4
|
+
# Once the repository is ready to publish (NPM_TOKEN secret configured,
|
|
5
|
+
# owner sign-off), uncomment the "Publish" step below.
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [published]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Setup Node.js
|
|
18
|
+
uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: "22.x"
|
|
21
|
+
cache: "npm"
|
|
22
|
+
registry-url: "https://registry.npmjs.org"
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: npm ci
|
|
26
|
+
|
|
27
|
+
- name: Build
|
|
28
|
+
run: npm run build
|
|
29
|
+
|
|
30
|
+
- name: Lint
|
|
31
|
+
run: npm run lint
|
|
32
|
+
|
|
33
|
+
- name: Test
|
|
34
|
+
run: npm test
|
|
35
|
+
|
|
36
|
+
# - name: Publish
|
|
37
|
+
# run: npm publish
|
|
38
|
+
# env:
|
|
39
|
+
# NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/.prettierignore
ADDED
package/.prettierrc.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 Nona Takahara, Mathias Bynens
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Nona Takahara, Mathias Bynens
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
|
-
# storm-lua-minify
|
|
2
|
-
|
|
3
|
-
このプログラムは [mathiasbynens/luamin](https://github.com/mathiasbynens/luamin) をベースにした Stormworks: Build and Rescue 向けのLua minifierです。
|
|
4
|
-
|
|
5
|
-
Stormworks: Build and Rescue 以外の用途にも使用可能です。
|
|
6
|
-
|
|
7
|
-
# 使い方
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
npm i storm-lua-minify
|
|
11
|
-
|
|
12
|
-
npx storm-lua-minify script.lua
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
- `-m`オプションを付加すると、モジュールの挙動をLuaの実際の挙動に近づけます
|
|
1
|
+
# storm-lua-minify
|
|
2
|
+
|
|
3
|
+
このプログラムは [mathiasbynens/luamin](https://github.com/mathiasbynens/luamin) をベースにした Stormworks: Build and Rescue 向けのLua minifierです。
|
|
4
|
+
|
|
5
|
+
Stormworks: Build and Rescue 以外の用途にも使用可能です。
|
|
6
|
+
|
|
7
|
+
# 使い方
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm i storm-lua-minify
|
|
11
|
+
|
|
12
|
+
npx storm-lua-minify script.lua
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- `-m`オプションを付加すると、モジュールの挙動をLuaの実際の挙動に近づけます
|
|
16
|
+
|
|
17
|
+
# テスト
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
npm ci
|
|
21
|
+
npm run build
|
|
22
|
+
npm test
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
# Lint / Format
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
npm run lint # ESLint
|
|
29
|
+
npm run format:check # Prettierのフォーマットチェック
|
|
30
|
+
npm run format # Prettierでフォーマット
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`test/` 以下にスナップショット・ラウンドトリップパース・識別子衝突検知のテストがあります。
|
|
34
|
+
既知バグ(#11, #12 など)の再現ケースは `test.todo` として登録されており、`npm test` は成功しますが、
|
|
35
|
+
修正が入るまではそのテスト自体は失敗した状態のまま todo 扱いになります。
|
|
36
|
+
|
|
37
|
+
スナップショットを更新する場合は `UPDATE_SNAPSHOTS=1 npm test` を実行してください。
|
package/dist/ast2lua.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
// based on "luamin": Copyright Mathias Bynens <https://mathiasbynens.be/>
|
|
3
3
|
// SPDX-License-Identifier: MIT
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.MinifyFile = void 0;
|
|
5
|
+
exports.MinifyFile = exports.IDENTIFIER_PARTS = void 0;
|
|
6
|
+
exports.isKeyword = isKeyword;
|
|
6
7
|
const source_map_1 = require("source-map");
|
|
8
|
+
const linker_1 = require("./linker");
|
|
7
9
|
const PRECEDENCE = {
|
|
8
10
|
or: 1,
|
|
9
11
|
and: 2,
|
|
@@ -13,28 +15,25 @@ const PRECEDENCE = {
|
|
|
13
15
|
">=": 3,
|
|
14
16
|
"~=": 3,
|
|
15
17
|
"==": 3,
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
18
|
+
"|": 4,
|
|
19
|
+
"~": 5, // binary ~ (bxor)
|
|
20
|
+
"&": 6,
|
|
21
|
+
"<<": 7,
|
|
22
|
+
">>": 7,
|
|
23
|
+
"..": 9,
|
|
24
|
+
"+": 10,
|
|
25
|
+
"-": 10, // binary -
|
|
26
|
+
"*": 11,
|
|
27
|
+
"/": 11,
|
|
28
|
+
"//": 11,
|
|
29
|
+
"%": 11,
|
|
30
|
+
unarynot: 12,
|
|
31
|
+
"unary#": 12,
|
|
32
|
+
"unary-": 12, // unary -
|
|
33
|
+
"unary~": 12, // unary ~ (bnot)
|
|
34
|
+
"^": 14,
|
|
26
35
|
};
|
|
27
|
-
|
|
28
|
-
"0",
|
|
29
|
-
"1",
|
|
30
|
-
"2",
|
|
31
|
-
"3",
|
|
32
|
-
"4",
|
|
33
|
-
"5",
|
|
34
|
-
"6",
|
|
35
|
-
"7",
|
|
36
|
-
"8",
|
|
37
|
-
"9",
|
|
36
|
+
exports.IDENTIFIER_PARTS = [
|
|
38
37
|
"a",
|
|
39
38
|
"b",
|
|
40
39
|
"c",
|
|
@@ -95,6 +94,7 @@ function wrapArray(obj) {
|
|
|
95
94
|
}
|
|
96
95
|
return [obj];
|
|
97
96
|
}
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- 現時点では未参照だが、オリジナル(luamin)由来のコードのため残置
|
|
98
98
|
function generateZeroes(length) {
|
|
99
99
|
let zero = "0";
|
|
100
100
|
let result = "";
|
|
@@ -108,7 +108,6 @@ function generateZeroes(length) {
|
|
|
108
108
|
if (length & 1) {
|
|
109
109
|
result += zero;
|
|
110
110
|
}
|
|
111
|
-
// eslint-disable-next-line no-cond-assign
|
|
112
111
|
if ((length >>= 1)) {
|
|
113
112
|
zero += zero;
|
|
114
113
|
}
|
|
@@ -206,11 +205,13 @@ function insertSeparator(a, b, separator = " ") {
|
|
|
206
205
|
}
|
|
207
206
|
class MinifyFile {
|
|
208
207
|
fileName;
|
|
208
|
+
moduleName;
|
|
209
209
|
ast;
|
|
210
210
|
minifier;
|
|
211
211
|
mode;
|
|
212
|
-
constructor(fileName, ast, minifier, mode) {
|
|
212
|
+
constructor(fileName, moduleName, ast, minifier, mode) {
|
|
213
213
|
this.fileName = fileName;
|
|
214
|
+
this.moduleName = moduleName;
|
|
214
215
|
this.ast = ast;
|
|
215
216
|
this.minifier = minifier;
|
|
216
217
|
this.mode = mode;
|
|
@@ -219,7 +220,10 @@ class MinifyFile {
|
|
|
219
220
|
const body = this.formatStatementList(this.ast.body);
|
|
220
221
|
if (!noComment && this.ast.comments) {
|
|
221
222
|
const comments = this.ast.comments;
|
|
222
|
-
comments
|
|
223
|
+
comments
|
|
224
|
+
.reverse()
|
|
225
|
+
.filter((v) => v.raw.includes("--#") || v.raw.includes("[[#"))
|
|
226
|
+
.forEach((comment) => {
|
|
223
227
|
body.prepend([this.sourceNodeHelper(comment, comment.raw), "\n"]);
|
|
224
228
|
});
|
|
225
229
|
return body;
|
|
@@ -228,11 +232,42 @@ class MinifyFile {
|
|
|
228
232
|
return body;
|
|
229
233
|
}
|
|
230
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* モジュール本体の最後の文が「単一の式を返すreturn文」であるときに限り、
|
|
237
|
+
* それ以外の文と最後の式を分けて返す。requireを式(IIFE)ではなく文として
|
|
238
|
+
* 展開したい呼び出し側(#29のレビュー対応)が利用する。
|
|
239
|
+
* 該当しない場合はundefinedを返し、呼び出し側はIIFE方式へフォールバックする。
|
|
240
|
+
*/
|
|
241
|
+
parseAsStatementsAndFinalExpression(noComment) {
|
|
242
|
+
const body = this.ast.body;
|
|
243
|
+
const last = body[body.length - 1];
|
|
244
|
+
if (!last ||
|
|
245
|
+
last.type !== "ReturnStatement" ||
|
|
246
|
+
last.arguments.length !== 1) {
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
const statements = this.formatStatementList(body.slice(0, -1));
|
|
250
|
+
if (!noComment && this.ast.comments) {
|
|
251
|
+
this.ast.comments
|
|
252
|
+
.slice()
|
|
253
|
+
.reverse()
|
|
254
|
+
.filter((v) => v.raw.includes("--#") || v.raw.includes("[[#"))
|
|
255
|
+
.forEach((comment) => {
|
|
256
|
+
statements.prepend([
|
|
257
|
+
this.sourceNodeHelper(comment, comment.raw),
|
|
258
|
+
"\n",
|
|
259
|
+
]);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
const finalExpression = this.formatExpression(last.arguments[0]);
|
|
263
|
+
return { statements, finalExpression };
|
|
264
|
+
}
|
|
231
265
|
sourceNodeHelper(node, chuncks, name) {
|
|
232
266
|
const line = node?.loc?.start.line;
|
|
233
267
|
const column = node?.loc?.start.column;
|
|
234
|
-
|
|
235
|
-
|
|
268
|
+
// this.fileNameは常にこのMinifyFileインスタンスが担当するモジュール自身の
|
|
269
|
+
// ファイル名(Linkパスで解決済み)なので、ここで出力するノードの由来ファイルとして正しい。
|
|
270
|
+
return new source_map_1.SourceNode(line == undefined ? null : line, column == undefined ? null : column, this.fileName, chuncks, name);
|
|
236
271
|
}
|
|
237
272
|
formatStatementList(body) {
|
|
238
273
|
const result = this.sourceNodeHelper(undefined, []);
|
|
@@ -241,7 +276,66 @@ class MinifyFile {
|
|
|
241
276
|
});
|
|
242
277
|
return result;
|
|
243
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* SLモード限定: `local x = require("m")` / `x = require("m")` の形(変数1個・
|
|
281
|
+
* 初期化式1個)を、requireを式(IIFE)として埋め込むのではなく、モジュール本体の
|
|
282
|
+
* 文をそのまま展開し最後にターゲットへ代入する「文」の並びとして出力する
|
|
283
|
+
* (#29のレビュー対応。無オプション実行時の挙動互換性のため)。
|
|
284
|
+
*
|
|
285
|
+
* モジュール本体が「単一の式を返すreturn文」で終わっていない場合や、変数・
|
|
286
|
+
* 初期化式が複数ある場合、-mモードの場合は対象外とし、undefinedを返す
|
|
287
|
+
* (呼び出し側は従来のIIFE方式にフォールバックする)。
|
|
288
|
+
*/
|
|
289
|
+
trySpliceRequireStatement(statement) {
|
|
290
|
+
if (this.mode.moduleLikeLua) {
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
if (statement.variables.length !== 1 || statement.init.length !== 1) {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
const moduleRef = this.matchModuleCallExpression(statement.init[0]);
|
|
297
|
+
if (!moduleRef || moduleRef.kind !== "require") {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
const spliced = this.minifier.splitModuleForStatementSplice(moduleRef.moduleName);
|
|
301
|
+
if (!spliced) {
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
const isLocal = statement.type === "LocalStatement";
|
|
305
|
+
const target = statement.variables[0];
|
|
306
|
+
const targetNode = isLocal
|
|
307
|
+
? this.generateIdentifier(target)
|
|
308
|
+
: this.formatExpression(target);
|
|
309
|
+
const result = this.sourceNodeHelper(statement, []);
|
|
310
|
+
addWithSeparator(result, spliced.statements);
|
|
311
|
+
addWithSeparator(result, isLocal ? ["local ", targetNode] : [targetNode]);
|
|
312
|
+
addWithSeparator(result, "=");
|
|
313
|
+
addWithSeparator(result, spliced.finalExpression);
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* SLモード限定: `require("m")` を戻り値を使わない単独の文として書いた場合、
|
|
318
|
+
* dofileと同じくfunctionで包まずそのまま展開する(Cプリプロセッサのincludeに
|
|
319
|
+
* 近い、LifeBoat Modeでの一般的な使い方に合わせるための対応。#29のレビュー対応)。
|
|
320
|
+
*/
|
|
321
|
+
tryInlineBareRequireStatement(expr) {
|
|
322
|
+
if (this.mode.moduleLikeLua) {
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
const moduleRef = this.matchModuleCallExpression(expr);
|
|
326
|
+
if (!moduleRef || moduleRef.kind !== "require") {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
return this.minifier.printModuleInline(moduleRef.moduleName);
|
|
330
|
+
}
|
|
244
331
|
formatStatement(statement) {
|
|
332
|
+
if (statement.type == "AssignmentStatement" ||
|
|
333
|
+
statement.type == "LocalStatement") {
|
|
334
|
+
const spliced = this.trySpliceRequireStatement(statement);
|
|
335
|
+
if (spliced) {
|
|
336
|
+
return spliced;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
245
339
|
if (statement.type == "AssignmentStatement") {
|
|
246
340
|
// left-hand side
|
|
247
341
|
const variables = statement.variables
|
|
@@ -273,6 +367,10 @@ class MinifyFile {
|
|
|
273
367
|
return result;
|
|
274
368
|
}
|
|
275
369
|
else if (statement.type == "CallStatement") {
|
|
370
|
+
const bareRequire = this.tryInlineBareRequireStatement(statement.expression);
|
|
371
|
+
if (bareRequire) {
|
|
372
|
+
return bareRequire;
|
|
373
|
+
}
|
|
276
374
|
return this.formatExpression(statement.expression); // NOTE: もう一度囲んでもいい
|
|
277
375
|
}
|
|
278
376
|
else if (statement.type == "IfStatement") {
|
|
@@ -414,13 +512,7 @@ class MinifyFile {
|
|
|
414
512
|
}*/
|
|
415
513
|
formatExpression(expression, argOptions) {
|
|
416
514
|
if (expression.type == "Identifier") {
|
|
417
|
-
return this.
|
|
418
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error
|
|
419
|
-
//@ts-ignore
|
|
420
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
421
|
-
expression.isLocal
|
|
422
|
-
? this.generateIdentifier(expression, true)
|
|
423
|
-
: expression.name, expression.name);
|
|
515
|
+
return this.generateIdentifier(expression);
|
|
424
516
|
}
|
|
425
517
|
else if (expression.type == "StringLiteral" ||
|
|
426
518
|
expression.type == "NumericLiteral" ||
|
|
@@ -500,22 +592,11 @@ class MinifyFile {
|
|
|
500
592
|
return result;
|
|
501
593
|
}
|
|
502
594
|
else if (expression.type == "CallExpression") {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
.replaceAll('"', "")
|
|
509
|
-
.replaceAll("'", "");
|
|
510
|
-
const res = this.minifier.parseModule(moduleName);
|
|
511
|
-
if (this.mode.moduleLikeLua) {
|
|
512
|
-
if (callExpr === "dofile") {
|
|
513
|
-
return (res || this.sourceNodeHelper(undefined, ""));
|
|
514
|
-
}
|
|
515
|
-
// requireならスキップ
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
518
|
-
return (res || this.sourceNodeHelper(undefined, ""));
|
|
595
|
+
const moduleRef = this.matchModuleCallExpression(expression);
|
|
596
|
+
if (moduleRef) {
|
|
597
|
+
const replacement = this.formatModuleReference(expression, moduleRef);
|
|
598
|
+
if (replacement) {
|
|
599
|
+
return replacement;
|
|
519
600
|
}
|
|
520
601
|
}
|
|
521
602
|
const args = expression.arguments
|
|
@@ -535,6 +616,13 @@ class MinifyFile {
|
|
|
535
616
|
]);
|
|
536
617
|
}
|
|
537
618
|
else if (expression.type == "StringCallExpression") {
|
|
619
|
+
const moduleRef = this.matchModuleCallExpression(expression);
|
|
620
|
+
if (moduleRef) {
|
|
621
|
+
const replacement = this.formatModuleReference(expression, moduleRef);
|
|
622
|
+
if (replacement) {
|
|
623
|
+
return replacement;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
538
626
|
return this.sourceNodeHelper(expression, [
|
|
539
627
|
this.formatExpression(expression.base),
|
|
540
628
|
this.formatExpression(expression.argument),
|
|
@@ -637,43 +725,58 @@ class MinifyFile {
|
|
|
637
725
|
}
|
|
638
726
|
return result;
|
|
639
727
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
return this.generateIdentifier(nameItem, nested);
|
|
664
|
-
}
|
|
665
|
-
this.minifier.identifierMap.set(nameItem.name, this.currentIdentifier);
|
|
666
|
-
return this.generateIdentifier(nameItem, nested);
|
|
667
|
-
}
|
|
668
|
-
--position;
|
|
728
|
+
// require/dofileへの静的な呼び出しを検出する。実際のモジュール解決はLinkパス
|
|
729
|
+
// (Minifier.link)が事前に済ませているため、ここでは呼び出し形が
|
|
730
|
+
// require/dofile への静的文字列呼び出しかどうかを判定するだけでよい(#18)。
|
|
731
|
+
matchModuleCall(base, argument) {
|
|
732
|
+
if (base.type !== "Identifier") {
|
|
733
|
+
return undefined;
|
|
734
|
+
}
|
|
735
|
+
if (base.name !== "require" && base.name !== "dofile") {
|
|
736
|
+
return undefined;
|
|
737
|
+
}
|
|
738
|
+
const moduleName = (0, linker_1.staticStringArgument)(argument);
|
|
739
|
+
if (moduleName === undefined) {
|
|
740
|
+
return undefined;
|
|
741
|
+
}
|
|
742
|
+
return { kind: base.name, moduleName };
|
|
743
|
+
}
|
|
744
|
+
// CallExpression / StringCallExpression のどちらの構文でも呼べるmatchModuleCall
|
|
745
|
+
matchModuleCallExpression(expr) {
|
|
746
|
+
if (expr.type === "CallExpression") {
|
|
747
|
+
return this.matchModuleCall(expr.base, expr.arguments[0]);
|
|
748
|
+
}
|
|
749
|
+
if (expr.type === "StringCallExpression") {
|
|
750
|
+
return this.matchModuleCall(expr.base, expr.argument);
|
|
669
751
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
752
|
+
return undefined;
|
|
753
|
+
}
|
|
754
|
+
formatModuleReference(expression, ref) {
|
|
755
|
+
if (ref.kind === "dofile") {
|
|
756
|
+
// dofileは呼び出しごとに毎回展開しなおす(キャッシュしない)
|
|
757
|
+
return this.minifier.printModuleInline(ref.moduleName);
|
|
758
|
+
}
|
|
759
|
+
if (!this.mode.moduleLikeLua) {
|
|
760
|
+
// SLモード(無オプション): requireもキャッシュせずその場展開する
|
|
761
|
+
// (挙動互換性のため、ホイストした共有ローカルへの参照にはしない)。
|
|
762
|
+
// 式の位置に置けるようにIIFEで包む。
|
|
763
|
+
const body = this.minifier.printModuleInline(ref.moduleName);
|
|
764
|
+
return this.sourceNodeHelper(expression, [
|
|
765
|
+
"(function() ",
|
|
766
|
+
body,
|
|
767
|
+
" end)()",
|
|
768
|
+
]);
|
|
673
769
|
}
|
|
674
|
-
|
|
675
|
-
return
|
|
676
|
-
|
|
770
|
+
// -mモードのrequireはそのまま呼び出しとして出力し、実行時のrequire関数に委ねる
|
|
771
|
+
return undefined;
|
|
772
|
+
}
|
|
773
|
+
// Renameパス(#20)が解決済みシンボルテーブルをもとに割り当てた短縮名を参照する。
|
|
774
|
+
// 対応するローカルシンボルが無い場合(グローバル参照や"self")は元の名前のまま出力する。
|
|
775
|
+
generateIdentifier(nameItem) {
|
|
776
|
+
const renamed = this.minifier
|
|
777
|
+
.getRenameResult(this.moduleName)
|
|
778
|
+
.nameOf(nameItem);
|
|
779
|
+
return this.sourceNodeHelper(nameItem, renamed ?? nameItem.name, nameItem.name);
|
|
677
780
|
}
|
|
678
781
|
}
|
|
679
782
|
exports.MinifyFile = MinifyFile;
|
package/dist/cli.js
CHANGED
|
@@ -8,11 +8,15 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const commander_1 = require("commander");
|
|
10
10
|
const minifier_1 = require("./minifier");
|
|
11
|
+
const output_1 = require("./output");
|
|
11
12
|
const program = new commander_1.Command();
|
|
12
13
|
program
|
|
13
14
|
.version("0.1.3")
|
|
14
15
|
.description("A Lua minifier also outputs source map")
|
|
15
|
-
.option("-m, --module-like-lua", "require・dofileの動作を実際のLuaに近づけます")
|
|
16
|
+
.option("-m, --module-like-lua", "require・dofileの動作を実際のLuaに近づけます")
|
|
17
|
+
.option("--no-rename", "識別子の短縮(リネーム)を無効にします(デバッグ用途)")
|
|
18
|
+
.option("--single-line-source-mapping-url", "sourceMappingURLアノテーションを単一行の--コメントで出力します(Source Map仕様の「最終行」ルールに従いますが、既定の複数行ブロックコメント形式を前提とするツールとは組み合わせられません)")
|
|
19
|
+
.option("--strict-source-mapping-url", "sourceMappingURLアノテーションをLuaコメントで一切包まず、Source Map仕様のマーカー文字列(//# sourceMappingURL=...)そのままを出力します。Luaの文法上この形式と有効なLuaコードは両立できないため、出力ファイルの最終行は有効なLua文ではなくなります");
|
|
16
20
|
program.parse(process.argv);
|
|
17
21
|
const luaFiles = program.args;
|
|
18
22
|
const luaparseSetting = {
|
|
@@ -21,7 +25,14 @@ const luaparseSetting = {
|
|
|
21
25
|
ranges: true,
|
|
22
26
|
scope: true,
|
|
23
27
|
};
|
|
24
|
-
const mode = program.opts();
|
|
28
|
+
const { singleLineSourceMappingUrl, strictSourceMappingUrl, ...mode } = program.opts();
|
|
29
|
+
// 既定は旧バージョンと互換の複数行ブロックコメント("legacy")。
|
|
30
|
+
// --strict-source-mapping-url > --single-line-source-mapping-url の優先順で上書きする。
|
|
31
|
+
const sourceMappingUrlStyle = strictSourceMappingUrl
|
|
32
|
+
? "strict"
|
|
33
|
+
: singleLineSourceMappingUrl
|
|
34
|
+
? "line"
|
|
35
|
+
: "legacy";
|
|
25
36
|
luaFiles.forEach((fileName) => {
|
|
26
37
|
const parsedFileName = path_1.default.parse(fileName);
|
|
27
38
|
if (fs_1.default.existsSync(fileName)) {
|
|
@@ -36,10 +47,9 @@ luaFiles.forEach((fileName) => {
|
|
|
36
47
|
name: parsedFileName.name,
|
|
37
48
|
ext: parsedFileName.ext + ".map",
|
|
38
49
|
});
|
|
39
|
-
map
|
|
40
|
-
|
|
41
|
-
fs_1.default.writeFileSync(
|
|
42
|
-
fs_1.default.writeFileSync(mapFileName, JSON.stringify(sourceAndMap.map));
|
|
50
|
+
const { code, map: mapJson } = (0, output_1.buildMinifiedOutput)(map, minFileName, mapFileName, { sourceMappingUrlStyle });
|
|
51
|
+
fs_1.default.writeFileSync(minFileName, code);
|
|
52
|
+
fs_1.default.writeFileSync(mapFileName, mapJson);
|
|
43
53
|
}
|
|
44
54
|
else {
|
|
45
55
|
console.error("No such file: " + fileName);
|
package/dist/index.js
CHANGED
|
@@ -4,38 +4,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const fs_1 = __importDefault(require("fs"));
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
7
|
const luaparse_1 = __importDefault(require("luaparse"));
|
|
9
8
|
const node_process_1 = require("node:process");
|
|
10
9
|
const ast2lua_1 = require("./ast2lua");
|
|
11
10
|
const luaparseSetting = {
|
|
12
11
|
locations: true,
|
|
13
|
-
luaVersion:
|
|
12
|
+
luaVersion: '5.3',
|
|
14
13
|
ranges: true,
|
|
15
14
|
scope: true,
|
|
16
15
|
};
|
|
17
16
|
const argPath = node_process_1.argv[2];
|
|
18
|
-
const includes = new Set();
|
|
19
|
-
function requireHelper(fileName) {
|
|
20
|
-
const resolvePath = path_1.default.join(path_1.default.dirname(argPath), fileName.replaceAll(".", path_1.default.sep) + ".lua");
|
|
21
|
-
if (!includes.has(resolvePath) && fs_1.default.existsSync(resolvePath)) {
|
|
22
|
-
const code = fs_1.default.readFileSync(resolvePath).toString();
|
|
23
|
-
const ast = luaparse_1.default.parse(code, luaparseSetting);
|
|
24
|
-
if ("globals" in ast) {
|
|
25
|
-
return new ast2lua_1.Minifier(resolvePath, ast, requireHelper).parse();
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
includes.add(resolvePath);
|
|
32
|
-
}
|
|
33
17
|
if (fs_1.default.existsSync(argPath)) {
|
|
34
18
|
const code = fs_1.default.readFileSync(argPath).toString();
|
|
35
19
|
const ast = luaparse_1.default.parse(code, luaparseSetting);
|
|
36
20
|
if ("globals" in ast) {
|
|
37
|
-
const map =
|
|
38
|
-
map.add("\n--[[\n//# sourceMappingURL=
|
|
21
|
+
const map = (0, ast2lua_1.minify)(ast);
|
|
22
|
+
map.add("\n--[[\n//# sourceMappingURL=test.lua.map\n]]");
|
|
39
23
|
console.log(map.toStringWithSourceMap().code);
|
|
40
24
|
// toStringWithSourceMap().map の file に書き出したのちのファイル名を入れないとVSCode Extでは検索失敗する
|
|
41
25
|
//console.log(JSON.stringify(map.toStringWithSourceMap().map));
|