storm-lua-minify 0.2.0 → 0.3.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/LICENSE +21 -21
- package/README.md +79 -37
- package/dist/ast2lua.js +67 -14
- package/dist/cli.js +28 -2
- package/dist/globalRename.js +35 -0
- package/dist/keywordLocator.js +68 -0
- package/dist/linker.js +13 -0
- package/dist/minifier.js +75 -1
- package/dist/renamer.js +16 -2
- package/dist/resolver.js +10 -4
- package/dist/transform.js +401 -0
- package/package.json +41 -33
- package/.github/workflows/ci.yml +0 -39
- package/.github/workflows/publish.yml +0 -39
- package/.prettierignore +0 -3
- package/.prettierrc.json +0 -1
- package/eslint.config.js +0 -29
- package/src/ast2lua.ts +0 -940
- package/src/cli.ts +0 -86
- package/src/linker.ts +0 -108
- package/src/minifier.ts +0 -284
- package/src/output.ts +0 -71
- package/src/renamer.ts +0 -134
- package/src/resolver.ts +0 -378
- package/test/circular-require.test.ts +0 -19
- package/test/fixtures/bare-require/main.lua +0 -2
- package/test/fixtures/bare-require/mod.lua +0 -1
- package/test/fixtures/bitwise-precedence/main.lua +0 -10
- package/test/fixtures/circular-require/a.lua +0 -2
- package/test/fixtures/circular-require/b.lua +0 -2
- package/test/fixtures/circular-require/main.lua +0 -2
- package/test/fixtures/dofile/greet.lua +0 -1
- package/test/fixtures/dofile/main.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_alpha.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_bravo.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_charlie.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_delta.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_echo.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_foxtrot.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_golf.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_hotel.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_india.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_juliet.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/dep_kilo.lua +0 -2
- package/test/fixtures/entry-scope-many-requires/main.lua +0 -25
- package/test/fixtures/multi-require/common.lua +0 -1
- package/test/fixtures/multi-require/main.lua +0 -3
- package/test/fixtures/nested-module/main.lua +0 -2
- package/test/fixtures/nested-module/sub/deep.lua +0 -1
- package/test/fixtures/require-call/main.lua +0 -2
- package/test/fixtures/require-call/mod.lua +0 -5
- package/test/fixtures/require-in-expression/main.lua +0 -1
- package/test/fixtures/require-in-expression/mod.lua +0 -1
- package/test/fixtures/require-string-call/main.lua +0 -2
- package/test/fixtures/require-string-call/mod.lua +0 -5
- package/test/fixtures/single-file/main.lua +0 -15
- package/test/identifier-collision.test.ts +0 -28
- package/test/lib/collision.ts +0 -131
- package/test/lib/helpers.ts +0 -124
- package/test/no-rename.test.ts +0 -19
- package/test/output.test.ts +0 -95
- package/test/precedence.test.ts +0 -28
- package/test/renamer.test.ts +0 -130
- package/test/resolver.test.ts +0 -206
- package/test/roundtrip.test.ts +0 -26
- package/test/snapshot.test.ts +0 -27
- package/test/snapshots/bare-require.sl.lua +0 -1
- package/test/snapshots/bitwise-precedence.sl.lua +0 -2
- package/test/snapshots/dofile.sl.lua +0 -1
- package/test/snapshots/entry-scope-many-requires.m.lua +0 -14
- package/test/snapshots/multi-require.m.lua +0 -4
- package/test/snapshots/multi-require.sl.lua +0 -1
- package/test/snapshots/nested-module.m.lua +0 -4
- package/test/snapshots/require-call.m.lua +0 -5
- package/test/snapshots/require-call.sl.lua +0 -1
- package/test/snapshots/require-in-expression.sl.lua +0 -1
- package/test/snapshots/require-string-call.m.lua +0 -5
- package/test/snapshots/single-file.sl.lua +0 -6
- package/test/sourcemap.test.ts +0 -105
- package/tsconfig.eslint.json +0 -8
- package/tsconfig.json +0 -111
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,37 +1,79 @@
|
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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の実際の挙動に近づけます
|
|
16
|
+
|
|
17
|
+
# Source Map
|
|
18
|
+
|
|
19
|
+
このツールは、ミニファイ後のコードと合わせて [Source Map](https://tc39.es/source-map/) (`.lua.map`) を出力します。
|
|
20
|
+
|
|
21
|
+
## sourceMappingURLアノテーションの出力形式
|
|
22
|
+
|
|
23
|
+
出力される `.min.lua` の末尾に付く `sourceMappingURL` アノテーションの書式は、3種類から選べます。
|
|
24
|
+
|
|
25
|
+
| オプション | 書式 | 特徴 |
|
|
26
|
+
| ---------------------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| (既定) | 複数行の `--[[`〜`]]` ブロックコメント | 過去のバージョンと同じ出力。既存ツールとの互換性を優先する場合はこのまま使う |
|
|
28
|
+
| `--single-line-source-mapping-url` | 単一行の `--` ラインコメント | 有効なLuaのまま、Source Map仕様が定める「アノテーションは生成コードの最終行に置く」という規則を満たす |
|
|
29
|
+
| `--strict-source-mapping-url` | Luaコメントで包まないマーカー文字列そのまま | 規則を厳密に満たすが、出力ファイルの最終行は有効なLua文ではなくなる(Lua文法上、`//`から始まる行をコメント化なしに構文解析可能にする方法は存在しないため) |
|
|
30
|
+
|
|
31
|
+
「最終行に置く」規則を厳密な前提とするツール(生成コードの末尾だけを見て `sourceMappingURL` を解決する実装など)と組み合わせる場合は、`--single-line-source-mapping-url` を試してください。
|
|
32
|
+
|
|
33
|
+
## sourcesContent
|
|
34
|
+
|
|
35
|
+
`.lua.map` の `sourcesContent` には、エントリファイルおよび `require`/`dofile` で参照される全モジュールの元テキストがそのまま埋め込まれます。ビューア側で元の `.lua` ファイルに個別にアクセスできなくても、Source Mapファイル単体で元コードを参照できます。
|
|
36
|
+
|
|
37
|
+
## キーワードトークンのマッピング
|
|
38
|
+
|
|
39
|
+
識別子・リテラル・式などASTノードに対応する出力は、それぞれ元ソース上の対応する位置に正確にマッピングされます。加えて、`then`/`elseif`/`else`/`end`/`do`/`until` といった、AST上では文・式の境界としてしか位置を持たないキーワードについても、それぞれが実際に出現する位置に個別にマッピングされます(例: `if`〜`then`〜`end` の `then` と `end` は、`if` とは別の、それぞれ自身の出現位置を持ちます)。トークン単位で対応関係を表示するビューアと組み合わせる際は、この粒度でのマッピングが利用されます。
|
|
40
|
+
|
|
41
|
+
## 識別子短縮関連のオプション
|
|
42
|
+
|
|
43
|
+
既定では以下がすべて有効です。デバッグ用途や意図しない出力になった場合は個別に無効化できます。
|
|
44
|
+
|
|
45
|
+
- `--no-rename`: すべての識別子短縮を無効にします
|
|
46
|
+
- `--no-merge-locals`: 連続する`local`変数宣言のまとめ上げを無効にします
|
|
47
|
+
- `--no-global-rename`: 代入されている(=スクリプト内部でのみ使われている)グローバル識別子の短縮を無効にします
|
|
48
|
+
- `--no-global-alias`: 短縮できない外部グローバル識別子(`screen`など、代入されず参照のみされるもの)を、頻出する場合にローカル変数へ代入して短縮する最適化を無効にします
|
|
49
|
+
- `--reserved-globals-config <path>`: `{"neverRenameGlobals": ["name", ...]}`形式のJSONファイルを指定し、**代入されていても常にリネームしないグローバル名**を列挙します
|
|
50
|
+
|
|
51
|
+
### ⚠️ `--reserved-globals-config` について(重要)
|
|
52
|
+
|
|
53
|
+
`--no-global-rename`が有効な場合、このツールは「プログラム中のどこかで代入されているグローバル名」を自動的に内部専用とみなしてリネームします。これは通常のスクリプト内部の状態変数には安全ですが、**実行環境(エンジンなど)が特定の名前のグローバル関数・変数を探して呼び出す規約がある場合、その名前を誤ってリネームしてしまうと、静かに(エラーなく)呼び出されなくなるバグになります**。
|
|
54
|
+
|
|
55
|
+
そのような規約(例: Stormworksのマイクロコントローラーでエンジン側が呼び出す特定名のコールバック関数など)が対象のスクリプトにある場合は、必ず`--reserved-globals-config`でその名前を保護対象として指定してください。規約を把握していない場合は、安全のため`--no-global-rename`で本機能自体を無効にすることを推奨します。
|
|
56
|
+
|
|
57
|
+
# 開発・テスト
|
|
58
|
+
|
|
59
|
+
開発はpnpmに変更になっています(v0.3.0リリース時点より)
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
pnpm ci
|
|
63
|
+
pnpm run build
|
|
64
|
+
pnpm test
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
# Lint / Format
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
pnpm run lint # ESLint
|
|
71
|
+
pnpm run format:check # Prettierのフォーマットチェック
|
|
72
|
+
pnpm run format # Prettierでフォーマット
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`test/` 以下にスナップショット・ラウンドトリップパース・識別子衝突検知のテストがあります。
|
|
76
|
+
既知バグ(#11, #12 など)の再現ケースは `test.todo` として登録されており、`npm test` は成功しますが、
|
|
77
|
+
修正が入るまではそのテスト自体は失敗した状態のまま todo 扱いになります。
|
|
78
|
+
|
|
79
|
+
スナップショットを更新する場合は `UPDATE_SNAPSHOTS=1 npm test` を実行してください。
|
package/dist/ast2lua.js
CHANGED
|
@@ -6,6 +6,8 @@ exports.MinifyFile = exports.IDENTIFIER_PARTS = void 0;
|
|
|
6
6
|
exports.isKeyword = isKeyword;
|
|
7
7
|
const source_map_1 = require("source-map");
|
|
8
8
|
const linker_1 = require("./linker");
|
|
9
|
+
const keywordLocator_1 = require("./keywordLocator");
|
|
10
|
+
const transform_1 = require("./transform");
|
|
9
11
|
const PRECEDENCE = {
|
|
10
12
|
or: 1,
|
|
11
13
|
and: 2,
|
|
@@ -269,6 +271,47 @@ class MinifyFile {
|
|
|
269
271
|
// ファイル名(Linkパスで解決済み)なので、ここで出力するノードの由来ファイルとして正しい。
|
|
270
272
|
return new source_map_1.SourceNode(line == undefined ? null : line, column == undefined ? null : column, this.fileName, chuncks, name);
|
|
271
273
|
}
|
|
274
|
+
_keywordLocator;
|
|
275
|
+
/**
|
|
276
|
+
* luaparseのASTはキーワード単体の位置を持たないため、`then`/`do`/`until`の
|
|
277
|
+
* ような「文・式の間に挟まるキーワード」の正確な位置は、モジュールの
|
|
278
|
+
* ソースを再トークン化して求める(#14)。初回アクセス時に1回だけ構築する。
|
|
279
|
+
*/
|
|
280
|
+
keywordLocator() {
|
|
281
|
+
if (!this._keywordLocator) {
|
|
282
|
+
const sourceText = this.minifier.moduleSourceText.get(this.moduleName);
|
|
283
|
+
if (sourceText == undefined) {
|
|
284
|
+
throw new Error(this.moduleName + " is not found");
|
|
285
|
+
}
|
|
286
|
+
this._keywordLocator = new keywordLocator_1.KeywordLocator(sourceText, this.minifier.luaParseSettings);
|
|
287
|
+
}
|
|
288
|
+
return this._keywordLocator;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* `anchor`ノードの直後(`anchor.loc.end`以降)で最初に現れる`value`という
|
|
292
|
+
* 値のキーワードトークンを探し、その位置を持つ`SourceNode`を返す。
|
|
293
|
+
* `anchor`は「探しているキーワードの直前に必ず存在する、既に位置が分かって
|
|
294
|
+
* いるノード」(例: `then`の場合は条件式)を渡す。ASTの入れ子構造上、
|
|
295
|
+
* その直前ノードの`loc.end`以降を走査すれば、ネストした同名キーワード
|
|
296
|
+
* (入れ子の`if...then`等)を誤って拾うことはない。
|
|
297
|
+
*/
|
|
298
|
+
keywordAfter(anchor, value) {
|
|
299
|
+
return this.keywordFrom(anchor.loc?.end, value);
|
|
300
|
+
}
|
|
301
|
+
keywordFrom(from, value) {
|
|
302
|
+
const pos = from ? this.keywordLocator().findFrom(from, value) : undefined;
|
|
303
|
+
return new source_map_1.SourceNode(pos?.line ?? null, pos?.column ?? null, this.fileName, value);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* `if`/`while`/`do`/`for`/`function`を締める`end`キーワードの位置を返す。
|
|
307
|
+
* これらの文は必ず`end`で終わり、末尾に余計な内容が続かないため、
|
|
308
|
+
* 文全体の`loc.end`(luaparseが既に計算済み)から`end`の文字数(3)を
|
|
309
|
+
* 引くだけで、再トークン化なしに正確な位置を求められる。
|
|
310
|
+
*/
|
|
311
|
+
endKeyword(node) {
|
|
312
|
+
const end = node.loc?.end;
|
|
313
|
+
return new source_map_1.SourceNode(end?.line ?? null, end == undefined ? null : end.column - 3, this.fileName, "end");
|
|
314
|
+
}
|
|
272
315
|
formatStatementList(body) {
|
|
273
316
|
const result = this.sourceNodeHelper(undefined, []);
|
|
274
317
|
wrapArray(body).forEach((statement) => {
|
|
@@ -380,12 +423,12 @@ class MinifyFile {
|
|
|
380
423
|
if (clause.type == "IfClause") {
|
|
381
424
|
addWithSeparator(clauseMap, "if");
|
|
382
425
|
addWithSeparator(clauseMap, this.formatExpression(clause.condition));
|
|
383
|
-
addWithSeparator(clauseMap, "then");
|
|
426
|
+
addWithSeparator(clauseMap, this.keywordAfter(clause.condition, "then"));
|
|
384
427
|
}
|
|
385
428
|
else if (clause.type == "ElseifClause") {
|
|
386
429
|
addWithSeparator(clauseMap, "elseif");
|
|
387
430
|
addWithSeparator(clauseMap, this.formatExpression(clause.condition));
|
|
388
|
-
addWithSeparator(clauseMap, "then");
|
|
431
|
+
addWithSeparator(clauseMap, this.keywordAfter(clause.condition, "then"));
|
|
389
432
|
}
|
|
390
433
|
else {
|
|
391
434
|
addWithSeparator(clauseMap, "else");
|
|
@@ -393,21 +436,21 @@ class MinifyFile {
|
|
|
393
436
|
addWithSeparator(clauseMap, this.formatStatementList(clause.body));
|
|
394
437
|
addWithSeparator(result, clauseMap);
|
|
395
438
|
});
|
|
396
|
-
addWithSeparator(result,
|
|
439
|
+
addWithSeparator(result, this.endKeyword(statement));
|
|
397
440
|
return result;
|
|
398
441
|
}
|
|
399
442
|
else if (statement.type == "WhileStatement") {
|
|
400
443
|
const result = this.sourceNodeHelper(statement, "while");
|
|
401
444
|
addWithSeparator(result, this.formatExpression(statement.condition));
|
|
402
|
-
addWithSeparator(result, "do");
|
|
445
|
+
addWithSeparator(result, this.keywordAfter(statement.condition, "do"));
|
|
403
446
|
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
404
|
-
addWithSeparator(result,
|
|
447
|
+
addWithSeparator(result, this.endKeyword(statement));
|
|
405
448
|
return result;
|
|
406
449
|
}
|
|
407
450
|
else if (statement.type == "DoStatement") {
|
|
408
451
|
const result = this.sourceNodeHelper(statement, "do");
|
|
409
452
|
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
410
|
-
addWithSeparator(result,
|
|
453
|
+
addWithSeparator(result, this.endKeyword(statement));
|
|
411
454
|
return result;
|
|
412
455
|
}
|
|
413
456
|
else if (statement.type == "ReturnStatement") {
|
|
@@ -426,7 +469,13 @@ class MinifyFile {
|
|
|
426
469
|
else if (statement.type == "RepeatStatement") {
|
|
427
470
|
const result = this.sourceNodeHelper(statement, "repeat");
|
|
428
471
|
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
429
|
-
|
|
472
|
+
// body内の最後の文があればその直後、無ければ`repeat`キーワード自身の
|
|
473
|
+
// 開始位置から`until`を探す(bodyが空でも、その手前には`repeat`しか
|
|
474
|
+
// 存在しないため安全に検索できる)。
|
|
475
|
+
const untilAnchor = statement.body.length
|
|
476
|
+
? statement.body[statement.body.length - 1].loc?.end
|
|
477
|
+
: statement.loc?.start;
|
|
478
|
+
addWithSeparator(result, this.keywordFrom(untilAnchor, "until"));
|
|
430
479
|
addWithSeparator(result, this.formatExpression(statement.condition));
|
|
431
480
|
return result;
|
|
432
481
|
}
|
|
@@ -451,7 +500,7 @@ class MinifyFile {
|
|
|
451
500
|
}
|
|
452
501
|
addWithSeparator(result, ")");
|
|
453
502
|
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
454
|
-
addWithSeparator(result,
|
|
503
|
+
addWithSeparator(result, this.endKeyword(statement));
|
|
455
504
|
return result;
|
|
456
505
|
}
|
|
457
506
|
else if (statement.type == "ForGenericStatement") {
|
|
@@ -466,9 +515,9 @@ class MinifyFile {
|
|
|
466
515
|
addWithSeparator(result, variables.slice(0, -1));
|
|
467
516
|
addWithSeparator(result, "in");
|
|
468
517
|
addWithSeparator(result, iterators.slice(0, -1));
|
|
469
|
-
addWithSeparator(result, "do");
|
|
518
|
+
addWithSeparator(result, this.keywordAfter(statement.iterators[statement.iterators.length - 1], "do"));
|
|
470
519
|
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
471
|
-
addWithSeparator(result,
|
|
520
|
+
addWithSeparator(result, this.endKeyword(statement));
|
|
472
521
|
return result;
|
|
473
522
|
}
|
|
474
523
|
else if (statement.type == "ForNumericStatement") {
|
|
@@ -483,9 +532,9 @@ class MinifyFile {
|
|
|
483
532
|
addWithSeparator(result, ",");
|
|
484
533
|
addWithSeparator(result, this.formatExpression(statement.step));
|
|
485
534
|
}
|
|
486
|
-
addWithSeparator(result, "do");
|
|
535
|
+
addWithSeparator(result, this.keywordAfter(statement.step ?? statement.end, "do"));
|
|
487
536
|
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
488
|
-
addWithSeparator(result,
|
|
537
|
+
addWithSeparator(result, this.endKeyword(statement));
|
|
489
538
|
return result;
|
|
490
539
|
}
|
|
491
540
|
else if (statement.type == "LabelStatement") {
|
|
@@ -663,7 +712,7 @@ class MinifyFile {
|
|
|
663
712
|
result.add(")");
|
|
664
713
|
const body = this.formatStatementList(expression.body);
|
|
665
714
|
addWithSeparator(result, body);
|
|
666
|
-
addWithSeparator(result,
|
|
715
|
+
addWithSeparator(result, this.endKeyword(expression));
|
|
667
716
|
return result;
|
|
668
717
|
}
|
|
669
718
|
else if (expression.type == "TableConstructorExpression") {
|
|
@@ -776,7 +825,11 @@ class MinifyFile {
|
|
|
776
825
|
const renamed = this.minifier
|
|
777
826
|
.getRenameResult(this.moduleName)
|
|
778
827
|
.nameOf(nameItem);
|
|
779
|
-
|
|
828
|
+
// #8bのエイリアス化はノードの`.name`自体を書き換えるため、loc(元のソース上の
|
|
829
|
+
// 位置)はそのままでもnameItem.nameはもう元の識別子名ではない。Source Mapの
|
|
830
|
+
// namesフィールドには常に「元のソースで書かれていた名前」を載せる必要がある。
|
|
831
|
+
const originalName = (0, transform_1.originalNameOf)(nameItem) ?? nameItem.name;
|
|
832
|
+
return this.sourceNodeHelper(nameItem, renamed ?? nameItem.name, originalName);
|
|
780
833
|
}
|
|
781
834
|
}
|
|
782
835
|
exports.MinifyFile = MinifyFile;
|
package/dist/cli.js
CHANGED
|
@@ -11,10 +11,14 @@ const minifier_1 = require("./minifier");
|
|
|
11
11
|
const output_1 = require("./output");
|
|
12
12
|
const program = new commander_1.Command();
|
|
13
13
|
program
|
|
14
|
-
.version("0.
|
|
14
|
+
.version("0.3.0")
|
|
15
15
|
.description("A Lua minifier also outputs source map")
|
|
16
16
|
.option("-m, --module-like-lua", "require・dofileの動作を実際のLuaに近づけます")
|
|
17
17
|
.option("--no-rename", "識別子の短縮(リネーム)を無効にします(デバッグ用途)")
|
|
18
|
+
.option("--no-global-rename", "内部でのみ使用するグローバル識別子の短縮を無効にします(デバッグ用途)")
|
|
19
|
+
.option("--no-merge-locals", "連続するローカル変数宣言のまとめ上げを無効にします(デバッグ用途)")
|
|
20
|
+
.option("--no-global-alias", "外部グローバル識別子(リネームできないもの)のローカル代入短縮を無効にします(デバッグ用途)")
|
|
21
|
+
.option("--reserved-globals-config <path>", '代入されていても短縮しないグローバル名を列挙したJSON設定ファイルのパス({"neverRenameGlobals":["onTick",...]}形式)。エンジン側のコールバック規約名など、常に元の名前のまま残す必要がある識別子を指定します')
|
|
18
22
|
.option("--single-line-source-mapping-url", "sourceMappingURLアノテーションを単一行の--コメントで出力します(Source Map仕様の「最終行」ルールに従いますが、既定の複数行ブロックコメント形式を前提とするツールとは組み合わせられません)")
|
|
19
23
|
.option("--strict-source-mapping-url", "sourceMappingURLアノテーションをLuaコメントで一切包まず、Source Map仕様のマーカー文字列(//# sourceMappingURL=...)そのままを出力します。Luaの文法上この形式と有効なLuaコードは両立できないため、出力ファイルの最終行は有効なLua文ではなくなります");
|
|
20
24
|
program.parse(process.argv);
|
|
@@ -25,7 +29,29 @@ const luaparseSetting = {
|
|
|
25
29
|
ranges: true,
|
|
26
30
|
scope: true,
|
|
27
31
|
};
|
|
28
|
-
|
|
32
|
+
function isReservedGlobalsConfig(value) {
|
|
33
|
+
if (typeof value !== "object" || value === null) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const candidate = value;
|
|
37
|
+
return (Array.isArray(candidate.neverRenameGlobals) &&
|
|
38
|
+
candidate.neverRenameGlobals.every((name) => typeof name === "string"));
|
|
39
|
+
}
|
|
40
|
+
function loadNeverRenameGlobals(configPath) {
|
|
41
|
+
if (!fs_1.default.existsSync(configPath)) {
|
|
42
|
+
throw new Error("Reserved globals config not found: " + configPath);
|
|
43
|
+
}
|
|
44
|
+
const parsed = JSON.parse(fs_1.default.readFileSync(configPath).toString());
|
|
45
|
+
if (!isReservedGlobalsConfig(parsed)) {
|
|
46
|
+
throw new Error(configPath +
|
|
47
|
+
' must be a JSON object of the form {"neverRenameGlobals": ["name", ...]}');
|
|
48
|
+
}
|
|
49
|
+
return new Set(parsed.neverRenameGlobals);
|
|
50
|
+
}
|
|
51
|
+
const { singleLineSourceMappingUrl, strictSourceMappingUrl, reservedGlobalsConfig, ...mode } = program.opts();
|
|
52
|
+
if (reservedGlobalsConfig) {
|
|
53
|
+
mode.neverRenameGlobals = loadNeverRenameGlobals(reservedGlobalsConfig);
|
|
54
|
+
}
|
|
29
55
|
// 既定は旧バージョンと互換の複数行ブロックコメント("legacy")。
|
|
30
56
|
// --strict-source-mapping-url > --single-line-source-mapping-url の優先順で上書きする。
|
|
31
57
|
const sourceMappingUrlStyle = strictSourceMappingUrl
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.classifyAndRenameGlobals = classifyAndRenameGlobals;
|
|
4
|
+
const renamer_1 = require("./renamer");
|
|
5
|
+
const linker_1 = require("./linker");
|
|
6
|
+
function classifyAndRenameGlobals(moduleResolve, neverRename, reserved) {
|
|
7
|
+
const everWritten = new Set();
|
|
8
|
+
const totalReferenceCount = new Map();
|
|
9
|
+
moduleResolve.forEach((resolved) => {
|
|
10
|
+
resolved.globals.forEach((binding) => {
|
|
11
|
+
if (binding.writes.length > 0) {
|
|
12
|
+
everWritten.add(binding.name);
|
|
13
|
+
}
|
|
14
|
+
totalReferenceCount.set(binding.name, (totalReferenceCount.get(binding.name) ?? 0) +
|
|
15
|
+
binding.references.length);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
// require/dofileはPrintパスが名前文字列で再検出するため、通常は代入対象に
|
|
19
|
+
// ならないが(そのため既にeverWrittenに入らない)、ユーザーコードが
|
|
20
|
+
// `require = ...`のように再代入する非現実的なケースへの防御として明示的にも除外する。
|
|
21
|
+
const qualifying = [...everWritten].filter((name) => !neverRename.has(name) && !linker_1.RESERVED_MODULE_FUNCTION_NAMES.has(name));
|
|
22
|
+
const orderedNames = qualifying.sort((a, b) => (totalReferenceCount.get(b) ?? 0) - (totalReferenceCount.get(a) ?? 0));
|
|
23
|
+
const usedShortNames = new Set(reserved);
|
|
24
|
+
const globalRenames = new Map();
|
|
25
|
+
let counter = 0;
|
|
26
|
+
orderedNames.forEach((name) => {
|
|
27
|
+
let candidate;
|
|
28
|
+
do {
|
|
29
|
+
candidate = (0, renamer_1.generateCandidate)(counter++);
|
|
30
|
+
} while (!(0, renamer_1.isAvailable)(candidate, usedShortNames));
|
|
31
|
+
usedShortNames.add(candidate);
|
|
32
|
+
globalRenames.set(name, candidate);
|
|
33
|
+
});
|
|
34
|
+
return globalRenames;
|
|
35
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.KeywordLocator = void 0;
|
|
7
|
+
const luaparse_1 = __importDefault(require("luaparse"));
|
|
8
|
+
// luaparseの型定義(@types/luaparse)には`tokenTypes`が含まれないため、
|
|
9
|
+
// 実行時の値を直接参照する。luaparse 0.3.1のソース上の定義は
|
|
10
|
+
// `var EOF = 1, StringLiteral = 2, Keyword = 4, ...`。
|
|
11
|
+
const tokenTypes = luaparse_1.default.tokenTypes;
|
|
12
|
+
function comparePosition(a, b) {
|
|
13
|
+
return a.line !== b.line ? a.line - b.line : a.column - b.column;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* luaparseのASTは文・式の境界にしか`loc`を持たず、`then`/`do`/`until`のような
|
|
17
|
+
* キーワード単体の出現位置は保持しない。このクラスはluaparseの低レベル
|
|
18
|
+
* 再トークン化API(`parse(code, {wait: true, ...})` + `lex()`)でモジュールの
|
|
19
|
+
* ソースを再走査し、キーワードトークンの正確な位置を検索できるようにする(#14)。
|
|
20
|
+
*/
|
|
21
|
+
class KeywordLocator {
|
|
22
|
+
tokens;
|
|
23
|
+
constructor(sourceText, luaParseSettings) {
|
|
24
|
+
const parser = luaparse_1.default.parse(sourceText, {
|
|
25
|
+
...luaParseSettings,
|
|
26
|
+
wait: true,
|
|
27
|
+
});
|
|
28
|
+
const tokens = [];
|
|
29
|
+
for (;;) {
|
|
30
|
+
const token = parser.lex();
|
|
31
|
+
if (token.type === tokenTypes.EOF) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
if (token.type === tokenTypes.Keyword) {
|
|
35
|
+
tokens.push({
|
|
36
|
+
value: token.value,
|
|
37
|
+
line: token.line,
|
|
38
|
+
column: token.range[0] - token.lineStart,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
this.tokens = tokens;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* `from`(含む)以降で最初に現れる、値が`value`と一致するキーワードトークンの
|
|
46
|
+
* 開始位置を返す。見つからない場合はundefined。
|
|
47
|
+
*/
|
|
48
|
+
findFrom(from, value) {
|
|
49
|
+
let lo = 0;
|
|
50
|
+
let hi = this.tokens.length;
|
|
51
|
+
while (lo < hi) {
|
|
52
|
+
const mid = (lo + hi) >>> 1;
|
|
53
|
+
if (comparePosition(this.tokens[mid], from) < 0) {
|
|
54
|
+
lo = mid + 1;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
hi = mid;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (let i = lo; i < this.tokens.length; i++) {
|
|
61
|
+
if (this.tokens[i].value === value) {
|
|
62
|
+
return { line: this.tokens[i].line, column: this.tokens[i].column };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.KeywordLocator = KeywordLocator;
|
package/dist/linker.js
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RESERVED_MODULE_FUNCTION_NAMES = void 0;
|
|
3
4
|
exports.staticStringArgument = staticStringArgument;
|
|
4
5
|
exports.findModuleReferences = findModuleReferences;
|
|
6
|
+
// require/dofileは、Linkパス(このファイルのfindModuleReferences)だけでなく
|
|
7
|
+
// Printパス(ast2lua.tsのmatchModuleCallExpression、minifier.tsの
|
|
8
|
+
// buildRequireWrapper/collectRequireTargets)でも、この2つの名前文字列を
|
|
9
|
+
// 手がかりに呼び出しを再検出している。そのため、識別子リネーム系のパス
|
|
10
|
+
// (#8a: globalRename.ts、#8b: transform.tsのinsertGlobalAliases)は、
|
|
11
|
+
// これらの名前を書き換え候補から常に除外しなければならない。書き換えると
|
|
12
|
+
// Printパスがrequire/dofile呼び出しを検出できなくなり、モジュール解決が
|
|
13
|
+
// 静かに壊れる(ディスパッチテーブルの生成漏れ等)。
|
|
14
|
+
exports.RESERVED_MODULE_FUNCTION_NAMES = new Set([
|
|
15
|
+
"require",
|
|
16
|
+
"dofile",
|
|
17
|
+
]);
|
|
5
18
|
/**
|
|
6
19
|
* Chunk配下を型を問わず再帰的に走査するジェネリックウォーカー。
|
|
7
20
|
* printerとは独立に、AST全体からrequire/dofile呼び出しを見つけ出すために使う(#18)。
|
package/dist/minifier.js
CHANGED
|
@@ -12,6 +12,8 @@ const ast2lua_1 = require("./ast2lua");
|
|
|
12
12
|
const linker_1 = require("./linker");
|
|
13
13
|
const resolver_1 = require("./resolver");
|
|
14
14
|
const renamer_1 = require("./renamer");
|
|
15
|
+
const globalRename_1 = require("./globalRename");
|
|
16
|
+
const transform_1 = require("./transform");
|
|
15
17
|
const NO_RENAME = {
|
|
16
18
|
nameOf: () => undefined,
|
|
17
19
|
usedNames: new Set(),
|
|
@@ -31,6 +33,8 @@ class Minifier {
|
|
|
31
33
|
moduleResolve = new Map();
|
|
32
34
|
// モジュールごとのRenameパスの結果(初回アクセス時に計算しキャッシュする)
|
|
33
35
|
renameCache = new Map();
|
|
36
|
+
// #8a: プログラム全体を横断して決定された「内部グローバル名 -> 短縮名」の対応
|
|
37
|
+
globalRenames = new Map();
|
|
34
38
|
constructor(entryFilePath, luaParseSettings, mode) {
|
|
35
39
|
this.identifiersInUse = new Set();
|
|
36
40
|
this.moduleSourceText = new Map();
|
|
@@ -44,6 +48,8 @@ class Minifier {
|
|
|
44
48
|
}
|
|
45
49
|
parse() {
|
|
46
50
|
this.link();
|
|
51
|
+
this.computeGlobalRenames();
|
|
52
|
+
this.transformAll();
|
|
47
53
|
this.renameAll();
|
|
48
54
|
const parts = [];
|
|
49
55
|
const entryComments = this.moduleAST.get(this.entryModule)?.comments;
|
|
@@ -122,11 +128,79 @@ class Minifier {
|
|
|
122
128
|
if (!resolved) {
|
|
123
129
|
throw new Error(moduleName + " is not found");
|
|
124
130
|
}
|
|
125
|
-
const result = (0, renamer_1.assignRenames)(resolved, this.identifiersInUse);
|
|
131
|
+
const result = (0, renamer_1.assignRenames)(resolved, this.identifiersInUse, this.globalRenames);
|
|
126
132
|
this.renameCache.set(moduleName, result);
|
|
127
133
|
result.usedNames.forEach((name) => this.identifiersInUse.add(name));
|
|
128
134
|
});
|
|
129
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Transformパス(#8b, #9): モジュールごとにASTレベルの最適化を適用する。
|
|
138
|
+
* computeGlobalRenamesの後・renameAllの前に実行する必要がある(renameは
|
|
139
|
+
* このパスが確定させた最終的な文構造を前提に短縮名を割り当てるため)。
|
|
140
|
+
*
|
|
141
|
+
* 実行順序は 8b(エイリアス挿入)→ #9(local宣言のまとめ上げ)。
|
|
142
|
+
* 8bが挿入する複数の1変数1式local文は、#9のまとめ上げ対象としてそのまま
|
|
143
|
+
* 束ねられるため、8bを先に行うことで両者の効果が重なる。
|
|
144
|
+
*
|
|
145
|
+
* 8bは新しい識別子ノード(エイリアスの宣言と、書き換えられた参照)を生成する。
|
|
146
|
+
* これらのノードは元のResolveパス結果には存在しないため、#9のハザード1判定
|
|
147
|
+
* (「候補文がグループ内で宣言済みの変数を参照していないか」)が正しく働くには、
|
|
148
|
+
* 8bの直後・#9の直前でResolveパスを再実行しておく必要がある。この順序を
|
|
149
|
+
* 誤ると、8bがrequire等の頻出グローバルをエイリアス化した際に、そのエイリアス
|
|
150
|
+
* 宣言自体と「エイリアス経由で呼び出す側」の文を#9が誤って1つのlocal文に
|
|
151
|
+
* まとめてしまい(エイリアス変数がまだ束縛される前のスコープで参照される形に
|
|
152
|
+
* なり)、意味が壊れる(要修正が発覚した実例)。
|
|
153
|
+
*/
|
|
154
|
+
transformAll() {
|
|
155
|
+
// globalRenames.keys()は8aが実際にリネームした(=代入もされていた)名前のみ。
|
|
156
|
+
// neverRenameGlobalsは代入されていない名前にも及ぶ保護指定なので、8bのエイリアス化
|
|
157
|
+
// が誤ってそれらを書き換えてしまわないよう、必ず両方をあわせてexcludeNamesに渡す。
|
|
158
|
+
const excludeGlobalNames = new Set([
|
|
159
|
+
...this.globalRenames.keys(),
|
|
160
|
+
...(this.mode.neverRenameGlobals ?? []),
|
|
161
|
+
]);
|
|
162
|
+
this.linkOrder.forEach((moduleName) => {
|
|
163
|
+
const ast = this.moduleAST.get(moduleName);
|
|
164
|
+
let resolved = this.moduleResolve.get(moduleName);
|
|
165
|
+
if (!ast || !resolved) {
|
|
166
|
+
throw new Error(moduleName + " is not found");
|
|
167
|
+
}
|
|
168
|
+
if (this.mode.rename !== false && this.mode.globalAlias !== false) {
|
|
169
|
+
(0, transform_1.insertGlobalAliases)(ast, resolved, {
|
|
170
|
+
excludeNames: excludeGlobalNames,
|
|
171
|
+
});
|
|
172
|
+
resolved = (0, resolver_1.resolveScopes)(ast);
|
|
173
|
+
}
|
|
174
|
+
if (this.mode.mergeLocals !== false) {
|
|
175
|
+
(0, transform_1.mergeLocalDeclarations)(ast, resolved, {
|
|
176
|
+
preserveRequireSplice: !this.mode.moduleLikeLua,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
this.moduleResolve.set(moduleName, resolved);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* #8aのGlobal Renameパス: リンクされた全モジュールを横断して、代入されている
|
|
184
|
+
* グローバル(neverRenameGlobalsに含まれるものを除く)に短縮名を割り当てる。
|
|
185
|
+
* グローバルは1つのランタイム束縛をモジュール間で共有するため、この判定・採番は
|
|
186
|
+
* renameAll(モジュールごとに独立して行うローカルのリネーム)より前に、
|
|
187
|
+
* 一度だけ行う必要がある。
|
|
188
|
+
*
|
|
189
|
+
* 選ばれた短縮名はrenameAllの前にidentifiersInUseへ予約し、元の長い名前の予約は
|
|
190
|
+
* 解除する(もう出力に現れないため)。順序を誤ると、
|
|
191
|
+
* ローカルの短縮名がグローバルの新しい短縮名と衝突しうる。
|
|
192
|
+
*/
|
|
193
|
+
computeGlobalRenames() {
|
|
194
|
+
if (this.mode.rename === false || this.mode.globalRename === false) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const neverRename = this.mode.neverRenameGlobals ?? new Set();
|
|
198
|
+
this.globalRenames = (0, globalRename_1.classifyAndRenameGlobals)(this.moduleResolve, neverRename, this.identifiersInUse);
|
|
199
|
+
this.globalRenames.forEach((shortName, originalName) => {
|
|
200
|
+
this.identifiersInUse.add(shortName);
|
|
201
|
+
this.identifiersInUse.delete(originalName);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
130
204
|
printModule(moduleName) {
|
|
131
205
|
const ast = this.moduleAST.get(moduleName);
|
|
132
206
|
const fileName = this.moduleNameAndFileName.get(moduleName);
|
package/dist/renamer.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isAvailable = isAvailable;
|
|
4
|
+
exports.generateCandidate = generateCandidate;
|
|
3
5
|
exports.assignRenames = assignRenames;
|
|
4
6
|
const ast2lua_1 = require("./ast2lua");
|
|
5
7
|
function isAvailable(id, reserved) {
|
|
@@ -42,7 +44,11 @@ function assignSlots(scope, active) {
|
|
|
42
44
|
});
|
|
43
45
|
return slotOf;
|
|
44
46
|
}
|
|
45
|
-
function assignRenames(resolveResult, reserved
|
|
47
|
+
function assignRenames(resolveResult, reserved,
|
|
48
|
+
// #8a: プログラム全体を横断して決定されたグローバル識別子の短縮名。
|
|
49
|
+
// 全モジュールに対して同じマップを渡すことで、モジュールをまたいで
|
|
50
|
+
// 共有される1つのランタイム束縛に一貫した短縮名を割り当てられる。
|
|
51
|
+
globalRenames) {
|
|
46
52
|
const slotOf = assignSlots(resolveResult.chunkScope, new Set());
|
|
47
53
|
// スロットの通算参照回数(宣言自体も1回として数える)を集計する。
|
|
48
54
|
const weightOfSlot = new Map();
|
|
@@ -80,7 +86,15 @@ function assignRenames(resolveResult, reserved) {
|
|
|
80
86
|
return undefined;
|
|
81
87
|
}
|
|
82
88
|
const symbol = resolveResult.symbolOf(identifier);
|
|
83
|
-
|
|
89
|
+
if (symbol) {
|
|
90
|
+
return nameOfSymbol.get(symbol);
|
|
91
|
+
}
|
|
92
|
+
// フィールド名(`.foo`)はここに来るが、これらはisGlobalReferenceが
|
|
93
|
+
// falseになるため名前文字列がたまたま一致しても誤ってリネームしない。
|
|
94
|
+
if (globalRenames && resolveResult.isGlobalReference(identifier)) {
|
|
95
|
+
return globalRenames.get(identifier.name);
|
|
96
|
+
}
|
|
97
|
+
return undefined;
|
|
84
98
|
},
|
|
85
99
|
usedNames: new Set(nameOfSlot.values()),
|
|
86
100
|
};
|