py-terser 0.1.0__py3-none-any.whl
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.
- py_terser-0.1.0.dist-info/METADATA +52 -0
- py_terser-0.1.0.dist-info/RECORD +85 -0
- py_terser-0.1.0.dist-info/WHEEL +4 -0
- py_terser-0.1.0.dist-info/entry_points.txt +5 -0
- py_terser-0.1.0.dist-info/licenses/LICENSE.md +20 -0
- terser/__init__.py +18 -0
- terser/__main__.py +4 -0
- terser/_minify.py +108 -0
- terser/_pipeline/__init__.py +16 -0
- terser/_pipeline/linker.py +91 -0
- terser/_pipeline/mangler/README.md +93 -0
- terser/_pipeline/mangler/__init__.py +9 -0
- terser/_pipeline/mangler/_constants.py +256 -0
- terser/_pipeline/mangler/_globals.py +153 -0
- terser/_pipeline/mangler/_locals.py +287 -0
- terser/_pipeline/mangler/name_generator.py +50 -0
- terser/_pipeline/mangler/util.py +144 -0
- terser/_pipeline/parser/__init__.py +3 -0
- terser/_pipeline/parser/_scope.py +205 -0
- terser/_pipeline/parser/parser.py +41 -0
- terser/_pipeline/parser/parser.pyi +16 -0
- terser/_pipeline/path_provider.py +270 -0
- terser/_pipeline/pipeline.py +9 -0
- terser/_pipeline/preprocessor.py +122 -0
- terser/_pipeline/printer/__init__.py +4 -0
- terser/_pipeline/printer/expression_printer.py +735 -0
- terser/_pipeline/printer/module_printer.py +860 -0
- terser/_pipeline/printer/string/__init__.py +0 -0
- terser/_pipeline/printer/string/f_string.py +553 -0
- terser/_pipeline/printer/string/ministring.py +179 -0
- terser/_pipeline/printer/string/t_string.py +327 -0
- terser/_pipeline/printer/token_printer.py +308 -0
- terser/_pipeline/resolver/__init__.py +6 -0
- terser/_pipeline/resolver/binder/__init__.py +19 -0
- terser/_pipeline/resolver/binder/_all.py +35 -0
- terser/_pipeline/resolver/binder/_bind.py +114 -0
- terser/_pipeline/resolver/binder/_mark_exports.py +26 -0
- terser/_pipeline/resolver/binder/_resolve_imports.py +78 -0
- terser/_pipeline/resolver/binding.py +528 -0
- terser/_pipeline/resolver/resolver.py +244 -0
- terser/_pipeline/resolver/util.py +152 -0
- terser/_pipeline/transforms/README.md +57 -0
- terser/_pipeline/transforms/__init__.py +27 -0
- terser/_pipeline/transforms/__init__.pyi +6 -0
- terser/_pipeline/transforms/_suite.py +243 -0
- terser/_pipeline/transforms/combine_imports.py +80 -0
- terser/_pipeline/transforms/constant_folding.py +157 -0
- terser/_pipeline/transforms/contracts.py +67 -0
- terser/_pipeline/transforms/remove_annotations.py +125 -0
- terser/_pipeline/transforms/remove_asserts.py +27 -0
- terser/_pipeline/transforms/remove_debug.py +71 -0
- terser/_pipeline/transforms/remove_exception_brackets.py +118 -0
- terser/_pipeline/transforms/remove_explicit_return_none.py +35 -0
- terser/_pipeline/transforms/remove_literal_statements.py +63 -0
- terser/_pipeline/transforms/remove_object_base.py +25 -0
- terser/_pipeline/transforms/remove_pass.py +28 -0
- terser/_pipeline/transforms/remove_posargs.py +12 -0
- terser/ast/__init__.py +18 -0
- terser/ast/_compare.py +112 -0
- terser/ast/_printer.py +176 -0
- terser/ast/_visitor.py +47 -0
- terser/ast/ast.py +99 -0
- terser/ast/ast.pyi +209 -0
- terser/ast/ref/__init__.py +25 -0
- terser/ast/ref/_module/__init__.py +12 -0
- terser/ast/ref/_module/_module.py +53 -0
- terser/ast/ref/_module/_spec.py +135 -0
- terser/ast/ref/_node.py +57 -0
- terser/ast/ref/_node.pyi +42 -0
- terser/ast/ref/_scoped.py +43 -0
- terser/cli/__init__.py +4 -0
- terser/cli/_argparse.py +126 -0
- terser/cli/_argv.py +142 -0
- terser/cli/_tqdm.py +206 -0
- terser/cli/main.py +131 -0
- terser/config.py +67 -0
- terser/exceptions.py +28 -0
- terser/hatch.py +91 -0
- terser/hooks.py +8 -0
- terser/project.py +240 -0
- terser/py.typed +0 -0
- terser/terser.py +59 -0
- terser/utils/__init__.py +0 -0
- terser/utils/cli_helper.py +34 -0
- terser/utils/contracts.py +82 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: py-terser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fork of python-minifier: transform Python project into its most compact representation
|
|
5
|
+
Project-URL: source, https://github.com/MadeByAlpha/py-terser.git
|
|
6
|
+
Project-URL: issues, https://github.com/MadeByAlpha/py-terser/issues
|
|
7
|
+
Author-email: Alpha <dev@alpha93.kr>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE.md
|
|
10
|
+
Keywords: compressor,mangler,minifier,minify,uglifier,uglify
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Environment :: Plugins
|
|
14
|
+
Classifier: Framework :: Hatch
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Programming Language :: Python :: Free Threading
|
|
26
|
+
Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable
|
|
27
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
28
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
29
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
30
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
31
|
+
Classifier: Topic :: Software Development :: Pre-processors
|
|
32
|
+
Classifier: Topic :: Text Processing :: General
|
|
33
|
+
Classifier: Topic :: Utilities
|
|
34
|
+
Requires-Python: >=3.10
|
|
35
|
+
Requires-Dist: anyio>=4.14.2
|
|
36
|
+
Requires-Dist: hatchling>=1.17
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# py-terser
|
|
40
|
+
|
|
41
|
+

|
|
43
|
+
|
|
44
|
+
A mangler/minifier toolkit for Python.
|
|
45
|
+
|
|
46
|
+
*Fork of [`dflook/python-minifier`](https://github.com/dflook/python-minifier.git), Name inspired by
|
|
47
|
+
[`terser`](https://github.com/terser/terser.git)*
|
|
48
|
+
|
|
49
|
+
Transforms Python project into its most compact representation.
|
|
50
|
+
|
|
51
|
+
py-terser currently supports Python 3.10 to Python 3.14.
|
|
52
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
terser/__init__.py,sha256=ESv2yBpJa6HAJrFBYS1nz5tqrSuBZbr06_aeizd0Ojw,355
|
|
2
|
+
terser/__main__.py,sha256=5jJg_JaIEERcyB_qxTSjD7cMlR4iBwlkLHBaWJbBd6U,65
|
|
3
|
+
terser/_minify.py,sha256=jN-0tJb76Ajo2DoawhDlQnDg3NJSBBI0P3PHQZgbexA,3354
|
|
4
|
+
terser/config.py,sha256=Gu4scqTpGb7hqJgrxXXL3lZ2CT5AlYIiy3EiKPEPEG8,2018
|
|
5
|
+
terser/exceptions.py,sha256=rAGixGTfHWPni6Yu8SfV0TYfNqUBG83rTxQDwT8yIsA,1012
|
|
6
|
+
terser/hatch.py,sha256=po8-9JuKV8V8CFir405LUn1mWoQpO8lj3iDcN2gYIoo,3708
|
|
7
|
+
terser/hooks.py,sha256=JYK9TTSwTv74QfmUcMOAQPHJ2uEv9z74z_MCRBefZLA,146
|
|
8
|
+
terser/project.py,sha256=vMXPOGyqhgCinfd49JPjiPDcSEy5SeOOQShyLrrC4TU,8966
|
|
9
|
+
terser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
terser/terser.py,sha256=MpB0b8b9tYQr3tbQNVhUMHXS4tau27qS1AylPviweI4,2276
|
|
11
|
+
terser/_pipeline/__init__.py,sha256=MjXOB7aiq5aWrPvw5Fh0UJY-w6w4Xe1zccR4naxOaWo,281
|
|
12
|
+
terser/_pipeline/linker.py,sha256=cvSPpMxbBkK-1zTXCXv45Gzr_SKFSf54SVrj-F5-FSk,3385
|
|
13
|
+
terser/_pipeline/path_provider.py,sha256=VOCH87VAG2pDgbC5_9RMXoFu1Zg6YxcAIUHLNbcGElw,7810
|
|
14
|
+
terser/_pipeline/pipeline.py,sha256=xiqmaC8-9LclXRvHwJ13aKp7p00ipbI_Wg-NJdKLHMU,99
|
|
15
|
+
terser/_pipeline/preprocessor.py,sha256=UdcGWdi_ykh9Az0NT5SO02Z_dr7jlC_a8gCxA5qtluI,4402
|
|
16
|
+
terser/_pipeline/mangler/README.md,sha256=PRNnBxz6Eox0-B_rojO4cqPTZ4jtlLz5HAmnP4NgRIQ,4177
|
|
17
|
+
terser/_pipeline/mangler/__init__.py,sha256=QyXcg5TMZDp4Us_JMGg_hL1mNtCR4W6HLHVGDoHUgVs,191
|
|
18
|
+
terser/_pipeline/mangler/_constants.py,sha256=5jwwSryejk90I-koCshMR6dOBNvnDf_w16-JlkRAY68,7386
|
|
19
|
+
terser/_pipeline/mangler/_globals.py,sha256=3X6Gcbziok22ooLWIt8oMOH8ykQt-OOHLkaWDaqnRm8,6013
|
|
20
|
+
terser/_pipeline/mangler/_locals.py,sha256=h3c76bRyi-4vzQRirR1uLYDvH_q99tzNCxSauG9ADSA,9003
|
|
21
|
+
terser/_pipeline/mangler/name_generator.py,sha256=8HycpsI2--YxvJdwtioW26SdnflszzOMfZZEgzGVOaU,1235
|
|
22
|
+
terser/_pipeline/mangler/util.py,sha256=clA3nczgcJa9Oig8rAyq_2RybCiAkAcraLHDoYXNEug,4185
|
|
23
|
+
terser/_pipeline/parser/__init__.py,sha256=8hIvhuCK2VmxyO0_gOjzEEWLgXA7VLy-nRXHcj0U5Sg,48
|
|
24
|
+
terser/_pipeline/parser/_scope.py,sha256=qxLbnRIJbYKyJcEfdZSAmMhvwUIEDCSwYGsuG59c9T0,7184
|
|
25
|
+
terser/_pipeline/parser/parser.py,sha256=BECSO5NOmtJcKlh_-IirzfGL5_6J57GXeAqaelf0l3Q,1476
|
|
26
|
+
terser/_pipeline/parser/parser.pyi,sha256=CghSYtnRv11b8KvxznPFWp4irsXzpwqS8a9LCzIkASw,354
|
|
27
|
+
terser/_pipeline/printer/__init__.py,sha256=89_DZgKeKQmNgwLHileuYkcDQVtcDFnPFnNW-wa__qM,74
|
|
28
|
+
terser/_pipeline/printer/expression_printer.py,sha256=RPTlrkQCt6mexgZ1GzHA30okXnA6RkvTDVGOsA__xW8,21711
|
|
29
|
+
terser/_pipeline/printer/module_printer.py,sha256=8Bu4DDfir7lFYqjBuuEnxnqMfbRJ_o3ELKNkFHYg3iw,25397
|
|
30
|
+
terser/_pipeline/printer/token_printer.py,sha256=omDTMXwkFxhPMENuDPQJps_1Z5Rubth1ZLWpmTESng4,9596
|
|
31
|
+
terser/_pipeline/printer/string/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
+
terser/_pipeline/printer/string/f_string.py,sha256=Df8nIObBBSc8ZCTt9j3PUJDGxvO4-iB5nt-T9szw_38,18700
|
|
33
|
+
terser/_pipeline/printer/string/ministring.py,sha256=iSDtFEe63YYWR7E_tEblnCZqsF2fZ5xzKTQ_mDzxpBI,4343
|
|
34
|
+
terser/_pipeline/printer/string/t_string.py,sha256=T5JIJ2qm8k5SlPSW8txs947LLxlvQg2dhGakzHMkKiw,13278
|
|
35
|
+
terser/_pipeline/resolver/__init__.py,sha256=x_rsp-T1ML045SaFPvCSU4cRaQ0IVrmtkCWwe0vxG3g,183
|
|
36
|
+
terser/_pipeline/resolver/binding.py,sha256=dwchkFzdtfgsPqVoVPuTcrUHsS0iThVrtqcf0_47G6k,17582
|
|
37
|
+
terser/_pipeline/resolver/resolver.py,sha256=R4AV_SLpu9CZxAeZq4cFVZX_JWXALWRWbRMBxldcBjI,8797
|
|
38
|
+
terser/_pipeline/resolver/util.py,sha256=WMy3bGorwGc_hnXGY-Cnk1EWxYF7chAlpP-C5mDKy8A,4618
|
|
39
|
+
terser/_pipeline/resolver/binder/__init__.py,sha256=H14hGDx46XfDDfM5PyqEBQGQvDSxhDszrKPqDx8BAH0,488
|
|
40
|
+
terser/_pipeline/resolver/binder/_all.py,sha256=fDtHtQYhpvLac6IvNeTIinM2SGt8b1L4ElNCeoBKkXQ,945
|
|
41
|
+
terser/_pipeline/resolver/binder/_bind.py,sha256=NKSoH4vLCA83Q26VhEA19DRGJ8kunl95yBrmL7jDOBU,4300
|
|
42
|
+
terser/_pipeline/resolver/binder/_mark_exports.py,sha256=RuhSN_ICwDIiz81i5NjCBLQDEiEbdzbdeaTrQMR1wh0,825
|
|
43
|
+
terser/_pipeline/resolver/binder/_resolve_imports.py,sha256=mcSkc1ag1QCyna4-OCy65mbmh6MTrU5nOu8EQQKPmg0,3140
|
|
44
|
+
terser/_pipeline/transforms/README.md,sha256=tC-e3AvQTRa_Z2BNlTFUrbByBHOAGNvzVSAhKGwFI64,3263
|
|
45
|
+
terser/_pipeline/transforms/__init__.py,sha256=9pDGpxnXgWyY3Rl4sdZ31oYFdSaFvQ6gUTIVO6W-UwU,773
|
|
46
|
+
terser/_pipeline/transforms/__init__.pyi,sha256=AmekzSs7umkPKLIOwJWsepsOMRR_PyvrfQts3nCSTD4,189
|
|
47
|
+
terser/_pipeline/transforms/_suite.py,sha256=wwUor1RE9QjddkzD8kqJ-Gwq4OHIod9k7INaIrT5MvU,7778
|
|
48
|
+
terser/_pipeline/transforms/combine_imports.py,sha256=K-ZqHNFUTn0e_Zkjhzp3ZEtOFW39etifKE9-d59N0uQ,2386
|
|
49
|
+
terser/_pipeline/transforms/constant_folding.py,sha256=St40B2JIJ8YtOLjU3rBq5p5PxW8B8YxHUSgjrMSTYT8,5368
|
|
50
|
+
terser/_pipeline/transforms/contracts.py,sha256=uHvyrhRFtMheDWiRo6kPj6-odCjRh-xDBfJHK52iKk4,2074
|
|
51
|
+
terser/_pipeline/transforms/remove_annotations.py,sha256=aqV5KxcfpDdF4QqC5TaWtkyWJbKIj5D9Q4ZxBqzv9Zw,4831
|
|
52
|
+
terser/_pipeline/transforms/remove_asserts.py,sha256=aAPmRsqm8FKbe0DFBYBc3CkTwBoNKwsy27mU_FGwufE,790
|
|
53
|
+
terser/_pipeline/transforms/remove_debug.py,sha256=tQ7IcDFoogZ-Bqj8xHScf2fcjxETU5K4NroAIoydieA,2290
|
|
54
|
+
terser/_pipeline/transforms/remove_exception_brackets.py,sha256=VXu0UhTZKzQOSYRct-xG2Y4VyvMo3J0yr7JdTpaAWiU,3968
|
|
55
|
+
terser/_pipeline/transforms/remove_explicit_return_none.py,sha256=VF_DL4RJKZXRlyA-23Ym5MYM3Gh0Lz6Bss_3BjjXW7Y,1102
|
|
56
|
+
terser/_pipeline/transforms/remove_literal_statements.py,sha256=fbMNg4EB6dwpPcljpJMtfL3_4mOjXUJL1aeVj4LgC8c,1671
|
|
57
|
+
terser/_pipeline/transforms/remove_object_base.py,sha256=oCq1Ra9UKA15R52Seao3YY2BQl-Bo_-wy2skhgAPM2g,675
|
|
58
|
+
terser/_pipeline/transforms/remove_pass.py,sha256=sTM80fkBQyuYWGYrt6oOv3jz5GH-5aE7ihsYwomaqgU,762
|
|
59
|
+
terser/_pipeline/transforms/remove_posargs.py,sha256=DEbtA2gEz4_G-DBUva9KSlrdYuJTx8eM89ZQB1okXzY,302
|
|
60
|
+
terser/ast/__init__.py,sha256=j3FAISfn1xwUzPnwO8uRiSddFJhgjhgTyGeaoZi6hdw,393
|
|
61
|
+
terser/ast/_compare.py,sha256=tnZGoA293QA94f69tCKvS3yb-V0zDr9mZ9XvdGlHzkg,3379
|
|
62
|
+
terser/ast/_printer.py,sha256=3LsUyLtPCDXPoaXMbEG4J9boBLmngyv4VuSyNq2iv4I,4486
|
|
63
|
+
terser/ast/_visitor.py,sha256=WQcAr_UJ6BWQWoiuBCUvKE57aWJuErhWTyib7bt8gww,1520
|
|
64
|
+
terser/ast/ast.py,sha256=3ZGOVdXKZE5ccZ7fMsdU1GjAjdsbs4eL2W_CPCoTFKo,2516
|
|
65
|
+
terser/ast/ast.pyi,sha256=xk5mGkxHHwCNS7OpH8R7Iy5Xyt8t3EBjq9AzMoV_QN4,6072
|
|
66
|
+
terser/ast/ref/__init__.py,sha256=bhkyuhr68hhL5BSk4pXUXzMiQCmoXTqS9s355b0JMoo,464
|
|
67
|
+
terser/ast/ref/_node.py,sha256=Q0eAx5R7EFCr7Yvvd7A-X1fKQyawdXdgPLAVSTsFt4Y,1419
|
|
68
|
+
terser/ast/ref/_node.pyi,sha256=OpB7DlC9uQhLTGUKPS8jYTFXX9zrYgp6INHCBS83X7c,1080
|
|
69
|
+
terser/ast/ref/_scoped.py,sha256=4pW9WBBez5e-nW60ENFSaLTdQDXgHv8mu3hBJlYWw7o,1089
|
|
70
|
+
terser/ast/ref/_module/__init__.py,sha256=qq5CJsgN0pfKU4ZmvRbsfK8VUtCPd99MOU1__3dlwOg,266
|
|
71
|
+
terser/ast/ref/_module/_module.py,sha256=T8t41eQ8HfVEglcb7uOj4JaBnsgyelXw-YRfbIE3tZg,1518
|
|
72
|
+
terser/ast/ref/_module/_spec.py,sha256=Kfl8LohwgLEAIyi7iAIRqzRxb3PlpPyjByqUnzjFeA8,3337
|
|
73
|
+
terser/cli/__init__.py,sha256=A9NbzGjvlukYjOePG7927i2RHsm0XhOy4DUFUhXbqtU,45
|
|
74
|
+
terser/cli/_argparse.py,sha256=9GR1i6utPnQ-e0N673jeuBPYa0n5oCG3073Uhbf7fXI,4968
|
|
75
|
+
terser/cli/_argv.py,sha256=ZGoMbDDS1FDK_5uW6w4cr0s81A59Z2_YHlkgzJgPFnU,5182
|
|
76
|
+
terser/cli/_tqdm.py,sha256=RMIGoMW5VRRG64BsLwfdS7Z2iuaI6WeeLW4yLFIRV_c,5742
|
|
77
|
+
terser/cli/main.py,sha256=nfTvipgHkx9orYV2BRNqtEl1Njbd1r5A476920bHC18,5020
|
|
78
|
+
terser/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
79
|
+
terser/utils/cli_helper.py,sha256=kw2oxmZsB2X8j0sGElVyxRZ5n4luzH1lfjCDU66hDJ8,803
|
|
80
|
+
terser/utils/contracts.py,sha256=y0ese169-juWOQkrLZNo4lJg2Iy7xse9329nIQxO-Eg,2416
|
|
81
|
+
py_terser-0.1.0.dist-info/METADATA,sha256=JTUvFZqzqZgteB23pAuaIFgHHgVx-kvrm7RVqDnJY6w,2187
|
|
82
|
+
py_terser-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
83
|
+
py_terser-0.1.0.dist-info/entry_points.txt,sha256=pgnHQx3tn7ZVI3UVUbFcDE2ISSTK03xt40OHyu_v-o4,74
|
|
84
|
+
py_terser-0.1.0.dist-info/licenses/LICENSE.md,sha256=0Rxr0B1-rQIrqL-mJvHDMmibkjZulSKpajGtNgxg8o0,1043
|
|
85
|
+
py_terser-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
======
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
terser/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This package transforms python source code strings or ast.Module Nodes into
|
|
3
|
+
a 'minified' representation of the same source code.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from ._minify import unparse
|
|
8
|
+
from .config import TransformConfig
|
|
9
|
+
from .terser import minify, minify_project
|
|
10
|
+
|
|
11
|
+
version = "0.1.0"
|
|
12
|
+
|
|
13
|
+
__all__ = (
|
|
14
|
+
"TransformConfig",
|
|
15
|
+
"minify",
|
|
16
|
+
"minify_project",
|
|
17
|
+
"unparse",
|
|
18
|
+
)
|
terser/__main__.py
ADDED
terser/_minify.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from ._pipeline import preprocessor, parser, resolver, transforms, mangler
|
|
4
|
+
from ._pipeline.printer import ModulePrinter
|
|
5
|
+
from .ast import CompareError, ast, compare_ast
|
|
6
|
+
from .exceptions import InvalidTransformError, UnbeneficialMinificationError
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from alpha93.progression import Task
|
|
10
|
+
from .ast.ref import ModuleSpec
|
|
11
|
+
from .config import TransformConfig
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def unparse(
|
|
15
|
+
path: str,
|
|
16
|
+
source: str | None,
|
|
17
|
+
module: ast.Module,
|
|
18
|
+
prefer_single_line: bool = False
|
|
19
|
+
) -> str:
|
|
20
|
+
"""
|
|
21
|
+
Turn a module AST into python code
|
|
22
|
+
|
|
23
|
+
This returns an exact representation of the given module,
|
|
24
|
+
such that it can be parsed back into the same AST.
|
|
25
|
+
|
|
26
|
+
:param ast.Module module: The module to turn into python code
|
|
27
|
+
:param bool prefer_single_line: If semi-colons should be preferred over newlines where there is no difference in output size
|
|
28
|
+
:rtype: str
|
|
29
|
+
"""
|
|
30
|
+
printer = ModulePrinter(prefer_single_line=prefer_single_line)
|
|
31
|
+
printer(module)
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
minified_module = ast.parse(printer.code, "<terser.unparse output>")
|
|
35
|
+
except SyntaxError as syntax_error:
|
|
36
|
+
raise InvalidTransformError(syntax_error, path, source, module)
|
|
37
|
+
|
|
38
|
+
if source and len(printer.code) >= len(source):
|
|
39
|
+
raise UnbeneficialMinificationError()
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
compare_ast(module, minified_module)
|
|
43
|
+
except CompareError as compare_error:
|
|
44
|
+
raise InvalidTransformError(compare_error, path, source, minified_module)
|
|
45
|
+
|
|
46
|
+
return printer.code
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def minify(
|
|
50
|
+
task: Task,
|
|
51
|
+
source: str,
|
|
52
|
+
spec: ModuleSpec | str,
|
|
53
|
+
/,
|
|
54
|
+
config: TransformConfig,
|
|
55
|
+
*,
|
|
56
|
+
strict: bool = False,
|
|
57
|
+
defines: dict[str, bool] | None = None,
|
|
58
|
+
rename: bool = True,
|
|
59
|
+
preserved_names: list[str] | None = None,
|
|
60
|
+
hoist_literals: bool = True,
|
|
61
|
+
) -> tuple[ast.Module, str | None]:
|
|
62
|
+
with task("Preprocessing sources"):
|
|
63
|
+
source, shebang = preprocessor.preprocess(source, defines, strict)
|
|
64
|
+
|
|
65
|
+
with task("Parsing AST"):
|
|
66
|
+
module = parser.parse(source, spec, optimize=config.optimize)
|
|
67
|
+
|
|
68
|
+
for transform in transforms.__transforms__:
|
|
69
|
+
if not transform.is_enabled(config) or transform.FLAGS > 0:
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
module: ast.Module = transform(config)(module)
|
|
73
|
+
|
|
74
|
+
with task("Resolving names"):
|
|
75
|
+
resolver.resolve(module)
|
|
76
|
+
resolver.bind(module)
|
|
77
|
+
|
|
78
|
+
cache = transforms.TransformCache(config)
|
|
79
|
+
for _ in task("Applying transforms", range(config.passes)):
|
|
80
|
+
for transform in transforms.__transforms__:
|
|
81
|
+
if not transform.is_enabled(config) or transform.FLAGS > 1:
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
module: ast.Module = transform(cache)(module)
|
|
85
|
+
|
|
86
|
+
if not any(cache.passes.values()):
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
with task("Mangling"):
|
|
90
|
+
if hoist_literals:
|
|
91
|
+
mangler.hoist_literals(module)
|
|
92
|
+
|
|
93
|
+
if rename:
|
|
94
|
+
mangler.mangle_locals(module, rename, preserved_names)
|
|
95
|
+
|
|
96
|
+
for transform in transforms.__transforms__:
|
|
97
|
+
if not transform.is_enabled(config) or transform.FLAGS > 2:
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
module: ast.Module = transform(config)(module)
|
|
101
|
+
|
|
102
|
+
# FIXME: lineno problem
|
|
103
|
+
# try:
|
|
104
|
+
# module = ast.parse(module)
|
|
105
|
+
# except SyntaxError as exc:
|
|
106
|
+
# raise InvalidTransformError(exc, spec, source, module) from exc
|
|
107
|
+
|
|
108
|
+
return module, shebang
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .path_provider import PathProvider
|
|
2
|
+
from .pipeline import Pipeline, SingleFilePipeline
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
__all__ = (
|
|
6
|
+
"PathProvider",
|
|
7
|
+
"Pipeline",
|
|
8
|
+
"SingleFilePipeline",
|
|
9
|
+
"linker",
|
|
10
|
+
"mangler",
|
|
11
|
+
"parser",
|
|
12
|
+
"preprocessor",
|
|
13
|
+
"printer",
|
|
14
|
+
"resolver",
|
|
15
|
+
"transforms",
|
|
16
|
+
)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from terser.ast import ast, ref
|
|
4
|
+
from .resolver.binding import ImportBinding
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from terser.ast import ModuleRef
|
|
8
|
+
from .resolver.binder import UnresolvedModuleRef
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _link_alias(
|
|
12
|
+
binding: ImportBinding,
|
|
13
|
+
unresolved: UnresolvedModuleRef,
|
|
14
|
+
project: dict[str, ModuleRef]
|
|
15
|
+
) -> None:
|
|
16
|
+
package_target = project.get(unresolved.path) if unresolved.path is not None else None
|
|
17
|
+
|
|
18
|
+
if unresolved.submodule_path is None:
|
|
19
|
+
# plain `import x[.y]` - unambiguous, the binding always refers to the module itself
|
|
20
|
+
binding.target = package_target
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
name = binding.node.name # the imported attribute/submodule name
|
|
24
|
+
if package_target is not None and any(binding.name == name for binding in package_target.bindings):
|
|
25
|
+
binding.target = package_target
|
|
26
|
+
binding.target_name = name
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
submodule_target = project.get(unresolved.submodule_path) if unresolved.submodule_path is not None else None
|
|
30
|
+
if submodule_target is not None:
|
|
31
|
+
binding.target = submodule_target
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
if package_target is not None:
|
|
35
|
+
# x resolves within the project, but y is neither a binding nor a submodule of it,
|
|
36
|
+
# e.g. provided dynamically through x's __getattr__ (PEP 562)
|
|
37
|
+
binding.target = package_target
|
|
38
|
+
binding.disallow_rename()
|
|
39
|
+
|
|
40
|
+
# else: x itself is stdlib / third-party - leave binding.target as None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _link_wildcard(
|
|
44
|
+
module_ref: ModuleRef,
|
|
45
|
+
stmt: ast.ImportFrom,
|
|
46
|
+
unresolved: UnresolvedModuleRef,
|
|
47
|
+
project: dict[str, ModuleRef]
|
|
48
|
+
) -> None:
|
|
49
|
+
target = project.get(unresolved.path) if unresolved.path is not None else None
|
|
50
|
+
|
|
51
|
+
if target is None:
|
|
52
|
+
# Can't enumerate an external module's exports statically
|
|
53
|
+
module_ref.tainted = True
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
exported = {binding.name for binding in target.bindings if binding._exported}
|
|
57
|
+
|
|
58
|
+
for index, binding in enumerate(module_ref.bindings):
|
|
59
|
+
if binding.name not in exported or not all(
|
|
60
|
+
isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load) for node in binding.references
|
|
61
|
+
):
|
|
62
|
+
continue # unused, or shadowed by a real local definition - the wildcard doesn't apply
|
|
63
|
+
|
|
64
|
+
upgraded = ImportBinding(binding.name, stmt, module_ref)
|
|
65
|
+
for node in binding.references:
|
|
66
|
+
upgraded.add_reference(node)
|
|
67
|
+
|
|
68
|
+
upgraded.target = target
|
|
69
|
+
upgraded.target_name = binding.name # exported implies target actually has this binding
|
|
70
|
+
|
|
71
|
+
module_ref.bindings[index] = upgraded
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def link(module: ast.Module, project: dict[str, ModuleRef]) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Resolve every import's target and expand wildcard imports, using the other modules in the
|
|
77
|
+
project. Must run after every module in the project has run `resolve_imports`, `mark_exports`
|
|
78
|
+
and `binder.resolve` (the latter so undefined-but-used names have their fallback binding, for
|
|
79
|
+
`_link_wildcard` to upgrade).
|
|
80
|
+
|
|
81
|
+
:param module: The module to link imports for
|
|
82
|
+
:param project: Every module in the project, keyed by resolved module path
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
module_ref = ref(module)
|
|
86
|
+
|
|
87
|
+
for binding, unresolved in module_ref.import_targets.items():
|
|
88
|
+
_link_alias(binding, unresolved, project)
|
|
89
|
+
|
|
90
|
+
for stmt, unresolved in module_ref.wildcard_targets.items():
|
|
91
|
+
_link_wildcard(module_ref, stmt, unresolved, project)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Renaming
|
|
2
|
+
|
|
3
|
+
We can save bytes by shortening the names used in a python program.
|
|
4
|
+
|
|
5
|
+
One simple way to do this is to replace each unique name in a module with a shorter one.
|
|
6
|
+
This will probably exhaust the available single character names, so is not as efficient as it could be.
|
|
7
|
+
Also, not all names can be safely changed this way.
|
|
8
|
+
|
|
9
|
+
By determining the scope of each name, we can assign the same short name to multiple non-overlapping scopes.
|
|
10
|
+
This means sibling namespaces may have the same names, and names will be shadowed in inner namespaces where possible.
|
|
11
|
+
|
|
12
|
+
This file is a guide for how the python_minifier package shortens names.
|
|
13
|
+
There are multiple steps to the renaming process.
|
|
14
|
+
|
|
15
|
+
## Binding Names
|
|
16
|
+
|
|
17
|
+
Names are bound to the local namespace it is defined in.
|
|
18
|
+
|
|
19
|
+
### Create namespace nodes
|
|
20
|
+
|
|
21
|
+
Namespaces in python are introduced by Modules, Functions, Comprehensions, Generators and Classes.
|
|
22
|
+
The AST node that introduces a new namespace is called a 'namespace node'.
|
|
23
|
+
|
|
24
|
+
These attributes are added to namespace nodes:
|
|
25
|
+
|
|
26
|
+
- Bindings - A list of Bindings local to this namespace, populated by the Bind names step
|
|
27
|
+
- Globals - A list of global names in this namespace
|
|
28
|
+
- Nonlocals - A list of nonlocal names in this namespace
|
|
29
|
+
|
|
30
|
+
### Determine parent node
|
|
31
|
+
|
|
32
|
+
Add a parent attribute to each node with the value of the node of which this is a child.
|
|
33
|
+
|
|
34
|
+
### Determine namespace
|
|
35
|
+
|
|
36
|
+
Add a namespace attribute to each node with the value of the namespace node that will be used for name binding and resolution.
|
|
37
|
+
This is usually the closest parent namespace node. The exceptions are:
|
|
38
|
+
|
|
39
|
+
- Function argument default values are in the same namespace as their function.
|
|
40
|
+
- Function decorators are in the same namespace as their function.
|
|
41
|
+
- Function annotations are in the same namespace as their function.
|
|
42
|
+
- Class decorator are in the same namespace as their class.
|
|
43
|
+
- Class bases, keywords, starargs and kwargs are in the same namespace as their class.
|
|
44
|
+
- The first iteration expression of a comprehension is in the same namespace as it's parent ListComp/SetComp/DictComp or GeneratorExp.
|
|
45
|
+
|
|
46
|
+
### Bind names
|
|
47
|
+
|
|
48
|
+
Every node that binds a name creates a NameBinding for that name in its namespace.
|
|
49
|
+
The node is added to the NameBinding as a reference.
|
|
50
|
+
|
|
51
|
+
If the name is nonlocal in its namespace it does not create a binding.
|
|
52
|
+
|
|
53
|
+
Nodes that create a binding:
|
|
54
|
+
|
|
55
|
+
- FunctionDef nodes bind their name
|
|
56
|
+
- ClassDef nodes bind their name
|
|
57
|
+
- arg nodes bind their arg
|
|
58
|
+
- Name nodes in Store or Del context bind their id
|
|
59
|
+
- MatchAs nodes bind their name
|
|
60
|
+
- MatchStar nodes bind their name
|
|
61
|
+
- MatchMapping nodes bind their rest
|
|
62
|
+
|
|
63
|
+
### Resolve names
|
|
64
|
+
|
|
65
|
+
For the remaining unbound name nodes and nodes that normally create a binding but are for a nonlocal name, we find their binding.
|
|
66
|
+
|
|
67
|
+
Bindings for name references are found by searching their namespace, then parent namespaces.
|
|
68
|
+
If a name is global in a searched namespace, skip straight to the module node.
|
|
69
|
+
If a name is nonlocal in a searched namespace, skip to the next parent namespace.
|
|
70
|
+
When traversing parent namespaces, Class namespaces are skipped.
|
|
71
|
+
|
|
72
|
+
If a NameBinding is found, add the node as a reference.
|
|
73
|
+
If no NameBinding is found, check if the name would resolve to a builtin.
|
|
74
|
+
If so, create a BuiltinBinding in the module namespace and add this node as a reference.
|
|
75
|
+
|
|
76
|
+
Otherwise we failed to find a binding for this name - Create a NameBinding in the module namespace and add this node
|
|
77
|
+
as a reference.
|
|
78
|
+
|
|
79
|
+
## Hoist Literals
|
|
80
|
+
|
|
81
|
+
At this point we do the HoistLiterals transform, which adds new HoistedLiteral bindings to the namespaces where it wants
|
|
82
|
+
to introduce new names.
|
|
83
|
+
|
|
84
|
+
## Name Assignment
|
|
85
|
+
|
|
86
|
+
Collect all bindings in the module and sort by estimated byte savings
|
|
87
|
+
|
|
88
|
+
For each binding:
|
|
89
|
+
|
|
90
|
+
- Determine it's 'reservation scope', which is the set of namespaces that name is referenced in (and all namespaces between them)
|
|
91
|
+
- Get the next available name that is unassigned and unreserved in all namespaces in the reservation scope.
|
|
92
|
+
- Check if we should proceed with the rename - is it space efficient to do this rename, or has the original name been assigned somewhere else?
|
|
93
|
+
- Rename the binding, rename all referenced nodes to the new name, and record this name as assigned in every namespace of the reservation scope.
|