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.
Files changed (85) hide show
  1. py_terser-0.1.0.dist-info/METADATA +52 -0
  2. py_terser-0.1.0.dist-info/RECORD +85 -0
  3. py_terser-0.1.0.dist-info/WHEEL +4 -0
  4. py_terser-0.1.0.dist-info/entry_points.txt +5 -0
  5. py_terser-0.1.0.dist-info/licenses/LICENSE.md +20 -0
  6. terser/__init__.py +18 -0
  7. terser/__main__.py +4 -0
  8. terser/_minify.py +108 -0
  9. terser/_pipeline/__init__.py +16 -0
  10. terser/_pipeline/linker.py +91 -0
  11. terser/_pipeline/mangler/README.md +93 -0
  12. terser/_pipeline/mangler/__init__.py +9 -0
  13. terser/_pipeline/mangler/_constants.py +256 -0
  14. terser/_pipeline/mangler/_globals.py +153 -0
  15. terser/_pipeline/mangler/_locals.py +287 -0
  16. terser/_pipeline/mangler/name_generator.py +50 -0
  17. terser/_pipeline/mangler/util.py +144 -0
  18. terser/_pipeline/parser/__init__.py +3 -0
  19. terser/_pipeline/parser/_scope.py +205 -0
  20. terser/_pipeline/parser/parser.py +41 -0
  21. terser/_pipeline/parser/parser.pyi +16 -0
  22. terser/_pipeline/path_provider.py +270 -0
  23. terser/_pipeline/pipeline.py +9 -0
  24. terser/_pipeline/preprocessor.py +122 -0
  25. terser/_pipeline/printer/__init__.py +4 -0
  26. terser/_pipeline/printer/expression_printer.py +735 -0
  27. terser/_pipeline/printer/module_printer.py +860 -0
  28. terser/_pipeline/printer/string/__init__.py +0 -0
  29. terser/_pipeline/printer/string/f_string.py +553 -0
  30. terser/_pipeline/printer/string/ministring.py +179 -0
  31. terser/_pipeline/printer/string/t_string.py +327 -0
  32. terser/_pipeline/printer/token_printer.py +308 -0
  33. terser/_pipeline/resolver/__init__.py +6 -0
  34. terser/_pipeline/resolver/binder/__init__.py +19 -0
  35. terser/_pipeline/resolver/binder/_all.py +35 -0
  36. terser/_pipeline/resolver/binder/_bind.py +114 -0
  37. terser/_pipeline/resolver/binder/_mark_exports.py +26 -0
  38. terser/_pipeline/resolver/binder/_resolve_imports.py +78 -0
  39. terser/_pipeline/resolver/binding.py +528 -0
  40. terser/_pipeline/resolver/resolver.py +244 -0
  41. terser/_pipeline/resolver/util.py +152 -0
  42. terser/_pipeline/transforms/README.md +57 -0
  43. terser/_pipeline/transforms/__init__.py +27 -0
  44. terser/_pipeline/transforms/__init__.pyi +6 -0
  45. terser/_pipeline/transforms/_suite.py +243 -0
  46. terser/_pipeline/transforms/combine_imports.py +80 -0
  47. terser/_pipeline/transforms/constant_folding.py +157 -0
  48. terser/_pipeline/transforms/contracts.py +67 -0
  49. terser/_pipeline/transforms/remove_annotations.py +125 -0
  50. terser/_pipeline/transforms/remove_asserts.py +27 -0
  51. terser/_pipeline/transforms/remove_debug.py +71 -0
  52. terser/_pipeline/transforms/remove_exception_brackets.py +118 -0
  53. terser/_pipeline/transforms/remove_explicit_return_none.py +35 -0
  54. terser/_pipeline/transforms/remove_literal_statements.py +63 -0
  55. terser/_pipeline/transforms/remove_object_base.py +25 -0
  56. terser/_pipeline/transforms/remove_pass.py +28 -0
  57. terser/_pipeline/transforms/remove_posargs.py +12 -0
  58. terser/ast/__init__.py +18 -0
  59. terser/ast/_compare.py +112 -0
  60. terser/ast/_printer.py +176 -0
  61. terser/ast/_visitor.py +47 -0
  62. terser/ast/ast.py +99 -0
  63. terser/ast/ast.pyi +209 -0
  64. terser/ast/ref/__init__.py +25 -0
  65. terser/ast/ref/_module/__init__.py +12 -0
  66. terser/ast/ref/_module/_module.py +53 -0
  67. terser/ast/ref/_module/_spec.py +135 -0
  68. terser/ast/ref/_node.py +57 -0
  69. terser/ast/ref/_node.pyi +42 -0
  70. terser/ast/ref/_scoped.py +43 -0
  71. terser/cli/__init__.py +4 -0
  72. terser/cli/_argparse.py +126 -0
  73. terser/cli/_argv.py +142 -0
  74. terser/cli/_tqdm.py +206 -0
  75. terser/cli/main.py +131 -0
  76. terser/config.py +67 -0
  77. terser/exceptions.py +28 -0
  78. terser/hatch.py +91 -0
  79. terser/hooks.py +8 -0
  80. terser/project.py +240 -0
  81. terser/py.typed +0 -0
  82. terser/terser.py +59 -0
  83. terser/utils/__init__.py +0 -0
  84. terser/utils/cli_helper.py +34 -0
  85. 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
+ ![`Development Status :: 3 -
42
+ Alpha`](https://img.shields.io/badge/-3%20--%20Alpha-orange?style=flat&label=Development%20Status)
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,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.31.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ terser = terser.cli:main
3
+
4
+ [hatch]
5
+ terser = terser.hooks
@@ -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
@@ -0,0 +1,4 @@
1
+ if __name__ == "__main__":
2
+ from .cli import main
3
+
4
+ main()
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.
@@ -0,0 +1,9 @@
1
+ from ._constants import hoist_literals
2
+ from ._globals import mangle_globals
3
+ from ._locals import mangle_locals
4
+
5
+ __all__ = (
6
+ "hoist_literals",
7
+ "mangle_locals",
8
+ "mangle_globals",
9
+ )