diff --git a/third_party/python/jsmin/CHANGELOG.txt b/third_party/python/jsmin/CHANGELOG.txt index d9da338a7f8e8..1adfa9b9a7db7 100644 --- a/third_party/python/jsmin/CHANGELOG.txt +++ b/third_party/python/jsmin/CHANGELOG.txt @@ -1,11 +1,79 @@ Changelog ========= +v3.0.0 (2021-09-08) Ben Bradshaw +-------------------------------- + +- Breaking Change: Removed support for Python 2 + +- Removed usage of use_2to3 in setup.py + +v2.2.2 (2017-05-01) Tikitu de Jager +----------------------------------- + +- Add license headers to code files (fixes i#17) + +- Remove mercurial files (fixes #20) + +v2.2.1 (2016-03-06) Tikitu de Jager +----------------------------------- + +- Fix #14: Infinite loop on `return x / 1;` + +v2.2.0 (2015-12-19) Tikitu de Jager +----------------------------------- + +- Merge #13: Preserve "loud comments" starting with `/*!` + + These are commonly used for copyright notices, and are preserved by various + other minifiers (e.g. YUI Compressor). + +v2.1.6 (2015-10-14) Tikitu de Jager +----------------------------------- + +- Fix #12: Newline following a regex literal should not be elided. + +v2.1.5 (2015-10-11) Tikitu de Jager +----------------------------------- + +- Fix #9: Premature end of statement caused by multi-line comment not + adding newline. + +- Fix #10: Removing multiline comment separating tokens must leave a space. + +- Refactor comment handling for maintainability. + +v2.1.4 (2015-08-23) Tikitu de Jager +----------------------------------- + +- Fix #6: regex literal matching comment was not correctly matched. + +- Refactor regex literal handling for robustness. + +v2.1.3 (2015-08-09) Tikitu de Jager +----------------------------------- + +- Reset issue numbering: issues live in github from now on. + +- Fix #1: regex literal was not recognised when occurring directly after `{`. + +v2.1.2 (2015-07-12) Tikitu de Jager +----------------------------------- + +- Issue numbers here and below refer to the bitbucket repository. + +- Fix #17: bug when JS starts with comment then literal regex. + +v2.1.1 (2015-02-14) Tikitu de Jager +----------------------------------- + +- Fix #16: bug returning a literal regex containing escaped forward-slashes. + v2.1.0 (2014-12-24) Tikitu de Jager ----------------------------------- - * First changelog entries; see README.rst for prior contributors. +- First changelog entries; see README.rst for prior contributors. - * Expose quote_chars parameter to provide just enough unofficial Harmony - support to be useful. +- Expose quote_chars parameter to provide just enough unofficial Harmony + support to be useful. diff --git a/third_party/python/jsmin/PKG-INFO b/third_party/python/jsmin/PKG-INFO index 27f88212e5755..c4bbd566aad82 100644 --- a/third_party/python/jsmin/PKG-INFO +++ b/third_party/python/jsmin/PKG-INFO @@ -1,11 +1,12 @@ -Metadata-Version: 1.1 +Metadata-Version: 1.2 Name: jsmin -Version: 2.1.0 +Version: 3.0.0 Summary: JavaScript minifier. -PLEASE UPDATE TO VERSION >= 2.0.6. Older versions have a serious bug related to comments. -Home-page: https://bitbucket.org/dcs/jsmin/ -Author: Tikitu de Jager -Author-email: tikitu+jsmin@logophile.org +Home-page: https://github.com/tikitu/jsmin/ +Author: Dave St.Germain +Author-email: dave@st.germa.in +Maintainer: Tikitu de Jager +Maintainer-email: tikitu+jsmin@logophile.org License: MIT License Description: ===== jsmin @@ -26,9 +27,14 @@ Description: ===== python -m jsmin myfile.js - As yet, ``jsmin`` makes no attempt to be compatible with + NB: ``jsmin`` makes no attempt to be compatible with `ECMAScript 6 / ES.next / Harmony `_. - If you're using it on Harmony code, though, you might find the ``quote_chars`` + The current maintainer does not intend to add ES6-compatibility. If you would + like to take over maintenance and update ``jsmin`` for ES6, please contact + `Tikitu de Jager `_. Pull requests are also + welcome, of course, but my time to review them is somewhat limited these days. + + If you're using ``jsmin`` on ES6 code, though, you might find the ``quote_chars`` parameter useful: .. code:: python @@ -42,42 +48,48 @@ Description: ===== =============== * install the package `from pypi `_ - * get the latest release `from the stable branch on bitbucket `_ - * get the development version `from the default branch on bitbucket `_ + * get the latest release `from latest-release on github `_ + * get the development version `from master on github `_ + + + Python 2 support removed + ======================== + + Python 2 support was removed in version 3.0.0. If you need to support Python 2, please use version 2.2.2 with setuptools<58. Contributing ============ - `Issues `_ and `Pull requests `_ - will be gratefully received on Bitbucket. Pull requests on github are great too, but the issue tracker lives on - bitbucket. + `Issues `_ and `Pull requests `_ + will be gratefully received on Github. The project used to be hosted + `on bitbucket `_ and old issues can still be + found there. - If possible, please make separate pull requests for tests and for code: tests will be committed on the stable branch - (which tracks the latest released version) while code will go to default by, erm, default. + If possible, please make separate pull requests for tests and for code: tests will be added to the `latest-release` branch while code will go to `master`. - Unless you request otherwise, your Bitbucket identity will be added to the contributor's list below; if you prefer a + Unless you request otherwise, your Github identity will be added to the contributor's list below; if you prefer a different name feel free to add it in your pull request instead. (If you prefer not to be mentioned you'll have to let the maintainer know somehow.) Build/test status ================= - Both default and stable branches are tested with Travis: https://travis-ci.org/tikitu/jsmin + Both branches are tested with Travis: https://travis-ci.org/tikitu/jsmin - Stable (latest released version plus any new tests) is tested against CPython 2.6, 2.7, 3.2, and 3.3. + The `latest-release` branch (the version on PyPI plus any new tests) is tested against CPython 3. Currently: - .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=ghstable + .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=latest-release - If stable is failing that means there's a new test that fails on *the latest released version on pypi*, with no fix yet + If that branch is failing that means there's a new test that fails on *the latest released version on pypi*, with no fix yet released. - Default (development version, might be ahead of latest released version) is tested against CPython 2.6, 2.7, 3.2, and - 3.3. Currently: + The `master` branch (development version, might be ahead of latest released version) is tested against CPython 3. + Currently: .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=master - If default is failing don't use it, but as long as stable is passing the pypi release should be ok. + If `master` is failing don't use it, but as long as `latest-release` is passing the pypi release should be ok. Contributors (chronological commit order) ========================================= @@ -87,17 +99,89 @@ Description: ===== * `Tikitu de Jager `_ (current maintainer) * https://bitbucket.org/rennat * `Nick Alexander `_ + * `Gennady Kovshenin `_ + * `Matt Molyneaux `_ + * `Albert Wang `_ + * `Ben Bradshaw `_ Changelog ========= + v3.0.0 (2021-09-08) Ben Bradshaw + -------------------------------- + + - Breaking Change: Removed support for Python 2 + + - Removed usage of use_2to3 in setup.py + + v2.2.2 (2017-05-01) Tikitu de Jager + ----------------------------------- + + - Add license headers to code files (fixes i#17) + + - Remove mercurial files (fixes #20) + + v2.2.1 (2016-03-06) Tikitu de Jager + ----------------------------------- + + - Fix #14: Infinite loop on `return x / 1;` + + v2.2.0 (2015-12-19) Tikitu de Jager + ----------------------------------- + + - Merge #13: Preserve "loud comments" starting with `/*!` + + These are commonly used for copyright notices, and are preserved by various + other minifiers (e.g. YUI Compressor). + + v2.1.6 (2015-10-14) Tikitu de Jager + ----------------------------------- + + - Fix #12: Newline following a regex literal should not be elided. + + v2.1.5 (2015-10-11) Tikitu de Jager + ----------------------------------- + + - Fix #9: Premature end of statement caused by multi-line comment not + adding newline. + + - Fix #10: Removing multiline comment separating tokens must leave a space. + + - Refactor comment handling for maintainability. + + v2.1.4 (2015-08-23) Tikitu de Jager + ----------------------------------- + + - Fix #6: regex literal matching comment was not correctly matched. + + - Refactor regex literal handling for robustness. + + v2.1.3 (2015-08-09) Tikitu de Jager + ----------------------------------- + + - Reset issue numbering: issues live in github from now on. + + - Fix #1: regex literal was not recognised when occurring directly after `{`. + + v2.1.2 (2015-07-12) Tikitu de Jager + ----------------------------------- + + - Issue numbers here and below refer to the bitbucket repository. + + - Fix #17: bug when JS starts with comment then literal regex. + + v2.1.1 (2015-02-14) Tikitu de Jager + ----------------------------------- + + - Fix #16: bug returning a literal regex containing escaped forward-slashes. + v2.1.0 (2014-12-24) Tikitu de Jager ----------------------------------- - * First changelog entries; see README.rst for prior contributors. + - First changelog entries; see README.rst for prior contributors. - * Expose quote_chars parameter to provide just enough unofficial Harmony - support to be useful. + - Expose quote_chars parameter to provide just enough unofficial Harmony + support to be useful. Platform: UNKNOWN @@ -106,12 +190,7 @@ Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Pre-processors Classifier: Topic :: Text Processing :: Filters diff --git a/third_party/python/jsmin/README.rst b/third_party/python/jsmin/README.rst index 6e7f35cde9fbe..8f6a9bdec51ce 100644 --- a/third_party/python/jsmin/README.rst +++ b/third_party/python/jsmin/README.rst @@ -17,9 +17,14 @@ You can run it as a commandline tool also:: python -m jsmin myfile.js -As yet, ``jsmin`` makes no attempt to be compatible with +NB: ``jsmin`` makes no attempt to be compatible with `ECMAScript 6 / ES.next / Harmony `_. -If you're using it on Harmony code, though, you might find the ``quote_chars`` +The current maintainer does not intend to add ES6-compatibility. If you would +like to take over maintenance and update ``jsmin`` for ES6, please contact +`Tikitu de Jager `_. Pull requests are also +welcome, of course, but my time to review them is somewhat limited these days. + +If you're using ``jsmin`` on ES6 code, though, you might find the ``quote_chars`` parameter useful: .. code:: python @@ -33,42 +38,48 @@ Where to get it =============== * install the package `from pypi `_ -* get the latest release `from the stable branch on bitbucket `_ -* get the development version `from the default branch on bitbucket `_ +* get the latest release `from latest-release on github `_ +* get the development version `from master on github `_ + + +Python 2 support removed +======================== + +Python 2 support was removed in version 3.0.0. If you need to support Python 2, please use version 2.2.2 with setuptools<58. Contributing ============ -`Issues `_ and `Pull requests `_ -will be gratefully received on Bitbucket. Pull requests on github are great too, but the issue tracker lives on -bitbucket. +`Issues `_ and `Pull requests `_ +will be gratefully received on Github. The project used to be hosted +`on bitbucket `_ and old issues can still be +found there. -If possible, please make separate pull requests for tests and for code: tests will be committed on the stable branch -(which tracks the latest released version) while code will go to default by, erm, default. +If possible, please make separate pull requests for tests and for code: tests will be added to the `latest-release` branch while code will go to `master`. -Unless you request otherwise, your Bitbucket identity will be added to the contributor's list below; if you prefer a +Unless you request otherwise, your Github identity will be added to the contributor's list below; if you prefer a different name feel free to add it in your pull request instead. (If you prefer not to be mentioned you'll have to let the maintainer know somehow.) Build/test status ================= -Both default and stable branches are tested with Travis: https://travis-ci.org/tikitu/jsmin +Both branches are tested with Travis: https://travis-ci.org/tikitu/jsmin -Stable (latest released version plus any new tests) is tested against CPython 2.6, 2.7, 3.2, and 3.3. +The `latest-release` branch (the version on PyPI plus any new tests) is tested against CPython 3. Currently: -.. image:: https://travis-ci.org/tikitu/jsmin.png?branch=ghstable +.. image:: https://travis-ci.org/tikitu/jsmin.png?branch=latest-release -If stable is failing that means there's a new test that fails on *the latest released version on pypi*, with no fix yet +If that branch is failing that means there's a new test that fails on *the latest released version on pypi*, with no fix yet released. -Default (development version, might be ahead of latest released version) is tested against CPython 2.6, 2.7, 3.2, and -3.3. Currently: +The `master` branch (development version, might be ahead of latest released version) is tested against CPython 3. +Currently: .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=master -If default is failing don't use it, but as long as stable is passing the pypi release should be ok. +If `master` is failing don't use it, but as long as `latest-release` is passing the pypi release should be ok. Contributors (chronological commit order) ========================================= @@ -78,3 +89,7 @@ Contributors (chronological commit order) * `Tikitu de Jager `_ (current maintainer) * https://bitbucket.org/rennat * `Nick Alexander `_ +* `Gennady Kovshenin `_ +* `Matt Molyneaux `_ +* `Albert Wang `_ +* `Ben Bradshaw `_ diff --git a/third_party/python/jsmin/jsmin.egg-info/PKG-INFO b/third_party/python/jsmin/jsmin.egg-info/PKG-INFO index 27f88212e5755..c4bbd566aad82 100644 --- a/third_party/python/jsmin/jsmin.egg-info/PKG-INFO +++ b/third_party/python/jsmin/jsmin.egg-info/PKG-INFO @@ -1,11 +1,12 @@ -Metadata-Version: 1.1 +Metadata-Version: 1.2 Name: jsmin -Version: 2.1.0 +Version: 3.0.0 Summary: JavaScript minifier. -PLEASE UPDATE TO VERSION >= 2.0.6. Older versions have a serious bug related to comments. -Home-page: https://bitbucket.org/dcs/jsmin/ -Author: Tikitu de Jager -Author-email: tikitu+jsmin@logophile.org +Home-page: https://github.com/tikitu/jsmin/ +Author: Dave St.Germain +Author-email: dave@st.germa.in +Maintainer: Tikitu de Jager +Maintainer-email: tikitu+jsmin@logophile.org License: MIT License Description: ===== jsmin @@ -26,9 +27,14 @@ Description: ===== python -m jsmin myfile.js - As yet, ``jsmin`` makes no attempt to be compatible with + NB: ``jsmin`` makes no attempt to be compatible with `ECMAScript 6 / ES.next / Harmony `_. - If you're using it on Harmony code, though, you might find the ``quote_chars`` + The current maintainer does not intend to add ES6-compatibility. If you would + like to take over maintenance and update ``jsmin`` for ES6, please contact + `Tikitu de Jager `_. Pull requests are also + welcome, of course, but my time to review them is somewhat limited these days. + + If you're using ``jsmin`` on ES6 code, though, you might find the ``quote_chars`` parameter useful: .. code:: python @@ -42,42 +48,48 @@ Description: ===== =============== * install the package `from pypi `_ - * get the latest release `from the stable branch on bitbucket `_ - * get the development version `from the default branch on bitbucket `_ + * get the latest release `from latest-release on github `_ + * get the development version `from master on github `_ + + + Python 2 support removed + ======================== + + Python 2 support was removed in version 3.0.0. If you need to support Python 2, please use version 2.2.2 with setuptools<58. Contributing ============ - `Issues `_ and `Pull requests `_ - will be gratefully received on Bitbucket. Pull requests on github are great too, but the issue tracker lives on - bitbucket. + `Issues `_ and `Pull requests `_ + will be gratefully received on Github. The project used to be hosted + `on bitbucket `_ and old issues can still be + found there. - If possible, please make separate pull requests for tests and for code: tests will be committed on the stable branch - (which tracks the latest released version) while code will go to default by, erm, default. + If possible, please make separate pull requests for tests and for code: tests will be added to the `latest-release` branch while code will go to `master`. - Unless you request otherwise, your Bitbucket identity will be added to the contributor's list below; if you prefer a + Unless you request otherwise, your Github identity will be added to the contributor's list below; if you prefer a different name feel free to add it in your pull request instead. (If you prefer not to be mentioned you'll have to let the maintainer know somehow.) Build/test status ================= - Both default and stable branches are tested with Travis: https://travis-ci.org/tikitu/jsmin + Both branches are tested with Travis: https://travis-ci.org/tikitu/jsmin - Stable (latest released version plus any new tests) is tested against CPython 2.6, 2.7, 3.2, and 3.3. + The `latest-release` branch (the version on PyPI plus any new tests) is tested against CPython 3. Currently: - .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=ghstable + .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=latest-release - If stable is failing that means there's a new test that fails on *the latest released version on pypi*, with no fix yet + If that branch is failing that means there's a new test that fails on *the latest released version on pypi*, with no fix yet released. - Default (development version, might be ahead of latest released version) is tested against CPython 2.6, 2.7, 3.2, and - 3.3. Currently: + The `master` branch (development version, might be ahead of latest released version) is tested against CPython 3. + Currently: .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=master - If default is failing don't use it, but as long as stable is passing the pypi release should be ok. + If `master` is failing don't use it, but as long as `latest-release` is passing the pypi release should be ok. Contributors (chronological commit order) ========================================= @@ -87,17 +99,89 @@ Description: ===== * `Tikitu de Jager `_ (current maintainer) * https://bitbucket.org/rennat * `Nick Alexander `_ + * `Gennady Kovshenin `_ + * `Matt Molyneaux `_ + * `Albert Wang `_ + * `Ben Bradshaw `_ Changelog ========= + v3.0.0 (2021-09-08) Ben Bradshaw + -------------------------------- + + - Breaking Change: Removed support for Python 2 + + - Removed usage of use_2to3 in setup.py + + v2.2.2 (2017-05-01) Tikitu de Jager + ----------------------------------- + + - Add license headers to code files (fixes i#17) + + - Remove mercurial files (fixes #20) + + v2.2.1 (2016-03-06) Tikitu de Jager + ----------------------------------- + + - Fix #14: Infinite loop on `return x / 1;` + + v2.2.0 (2015-12-19) Tikitu de Jager + ----------------------------------- + + - Merge #13: Preserve "loud comments" starting with `/*!` + + These are commonly used for copyright notices, and are preserved by various + other minifiers (e.g. YUI Compressor). + + v2.1.6 (2015-10-14) Tikitu de Jager + ----------------------------------- + + - Fix #12: Newline following a regex literal should not be elided. + + v2.1.5 (2015-10-11) Tikitu de Jager + ----------------------------------- + + - Fix #9: Premature end of statement caused by multi-line comment not + adding newline. + + - Fix #10: Removing multiline comment separating tokens must leave a space. + + - Refactor comment handling for maintainability. + + v2.1.4 (2015-08-23) Tikitu de Jager + ----------------------------------- + + - Fix #6: regex literal matching comment was not correctly matched. + + - Refactor regex literal handling for robustness. + + v2.1.3 (2015-08-09) Tikitu de Jager + ----------------------------------- + + - Reset issue numbering: issues live in github from now on. + + - Fix #1: regex literal was not recognised when occurring directly after `{`. + + v2.1.2 (2015-07-12) Tikitu de Jager + ----------------------------------- + + - Issue numbers here and below refer to the bitbucket repository. + + - Fix #17: bug when JS starts with comment then literal regex. + + v2.1.1 (2015-02-14) Tikitu de Jager + ----------------------------------- + + - Fix #16: bug returning a literal regex containing escaped forward-slashes. + v2.1.0 (2014-12-24) Tikitu de Jager ----------------------------------- - * First changelog entries; see README.rst for prior contributors. + - First changelog entries; see README.rst for prior contributors. - * Expose quote_chars parameter to provide just enough unofficial Harmony - support to be useful. + - Expose quote_chars parameter to provide just enough unofficial Harmony + support to be useful. Platform: UNKNOWN @@ -106,12 +190,7 @@ Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Pre-processors Classifier: Topic :: Text Processing :: Filters diff --git a/third_party/python/jsmin/jsmin/__init__.py b/third_party/python/jsmin/jsmin/__init__.py index 44bb6b8cb3a90..c906d14a03480 100644 --- a/third_party/python/jsmin/jsmin/__init__.py +++ b/third_party/python/jsmin/jsmin/__init__.py @@ -1,3 +1,5 @@ +# vim: set fileencoding=utf-8 : + # This code is original from jsmin by Douglas Crockford, it was translated to # Python by Baruch Even. It was rewritten by Dave St.Germain for speed. # @@ -24,35 +26,17 @@ # THE SOFTWARE. -import sys -is_3 = sys.version_info >= (3, 0) -if is_3: - import io -else: - import StringIO - try: - import cStringIO - except ImportError: - cStringIO = None - +import io __all__ = ['jsmin', 'JavascriptMinify'] -__version__ = '2.1.0' +__version__ = '3.0.0' def jsmin(js, **kwargs): """ returns a minified version of the javascript string """ - if not is_3: - if cStringIO and not isinstance(js, unicode): - # strings can use cStringIO for a 3x performance - # improvement, but unicode (in python2) cannot - klass = cStringIO.StringIO - else: - klass = StringIO.StringIO - else: - klass = io.StringIO + klass = io.StringIO ins = klass(js) outs = klass() JavascriptMinify(ins, outs, **kwargs).minify() @@ -83,6 +67,9 @@ def write(char): if char in 'return': self.return_buf += char self.is_return = self.return_buf == 'return' + else: + self.return_buf = '' + self.is_return = self.is_return and char < '!' self.outs.write(char) if self.is_return: self.return_buf = '' @@ -91,73 +78,26 @@ def write(char): space_strings = "abcdefghijklmnopqrstuvwxyz"\ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$\\" - starters, enders = '{[(+-', '}])+-' + self.quote_chars - newlinestart_strings = starters + space_strings - newlineend_strings = enders + space_strings + self.space_strings = space_strings + starters, enders = '{[(+-', '}])+-/' + self.quote_chars + newlinestart_strings = starters + space_strings + self.quote_chars + newlineend_strings = enders + space_strings + self.quote_chars + self.newlinestart_strings = newlinestart_strings + self.newlineend_strings = newlineend_strings + do_newline = False do_space = False escape_slash_count = 0 - doing_single_comment = False - previous_before_comment = '' - doing_multi_comment = False - in_re = False in_quote = '' quote_buf = [] - - previous = read(1) - if previous == '\\': - escape_slash_count += 1 + + previous = ';' + previous_non_space = ';' next1 = read(1) - if previous == '/': - if next1 == '/': - doing_single_comment = True - elif next1 == '*': - doing_multi_comment = True - previous = next1 - next1 = read(1) - else: - in_re = True # literal regex at start of script - write(previous) - elif not previous: - return - elif previous >= '!': - if previous in self.quote_chars: - in_quote = previous - write(previous) - previous_non_space = previous - else: - previous_non_space = ' ' - if not next1: - return - - while 1: + + while next1: next2 = read(1) - if not next2: - last = next1.strip() - if not (doing_single_comment or doing_multi_comment)\ - and last not in ('', '/'): - if in_quote: - write(''.join(quote_buf)) - write(last) - break - if doing_multi_comment: - if next1 == '*' and next2 == '/': - doing_multi_comment = False - if previous_before_comment and previous_before_comment in space_strings: - do_space = True - next2 = read(1) - elif doing_single_comment: - if next1 in '\r\n': - doing_single_comment = False - while next2 in '\r\n': - next2 = read(1) - if not next2: - break - if previous_before_comment in ')}]': - do_newline = True - elif previous_before_comment in space_strings: - write('\n') - elif in_quote: + if in_quote: quote_buf.append(next1) if next1 == in_quote: @@ -171,19 +111,9 @@ def write(char): in_quote = '' write(''.join(quote_buf)) elif next1 in '\r\n': - if previous_non_space in newlineend_strings \ - or previous_non_space > '~': - while 1: - if next2 < '!': - next2 = read(1) - if not next2: - break - else: - if next2 in newlinestart_strings \ - or next2 > '~' or next2 == '/': - do_newline = True - break - elif next1 < '!' and not in_re: + next2, do_newline = self.newline( + previous_non_space, next2, do_newline) + elif next1 < '!': if (previous_non_space in space_strings \ or previous_non_space > '~') \ and (next2 in space_strings or next2 > '~'): @@ -197,42 +127,126 @@ def write(char): elif next1 == '/': if do_space: write(' ') - if in_re: - if previous != '\\' or (not escape_slash_count % 2) or next2 in 'gimy': - in_re = False - write('/') - elif next2 == '/': - doing_single_comment = True - previous_before_comment = previous_non_space + if next2 == '/': + # Line comment: treat it as a newline, but skip it + next2 = self.line_comment(next1, next2) + next1 = '\n' + next2, do_newline = self.newline( + previous_non_space, next2, do_newline) elif next2 == '*': - doing_multi_comment = True - previous_before_comment = previous_non_space - previous = next1 - next1 = next2 + self.block_comment(next1, next2) next2 = read(1) + if previous_non_space in space_strings: + do_space = True + next1 = previous else: - in_re = previous_non_space in '(,=:[?!&|;' or self.is_return # literal regular expression - write('/') + if previous_non_space in '{(,=:[?!&|;' or self.is_return: + self.regex_literal(next1, next2) + # hackish: after regex literal next1 is still / + # (it was the initial /, now it's the last /) + next2 = read(1) + else: + write('/') else: - if do_space: - do_space = False - write(' ') if do_newline: write('\n') do_newline = False + do_space = False + if do_space: + do_space = False + write(' ') write(next1) - if not in_re and next1 in self.quote_chars: + if next1 in self.quote_chars: in_quote = next1 quote_buf = [] - previous = next1 - next1 = next2 - - if previous >= '!': - previous_non_space = previous + if next1 >= '!': + previous_non_space = next1 - if previous == '\\': + if next1 == '\\': escape_slash_count += 1 else: escape_slash_count = 0 + + previous = next1 + next1 = next2 + + def regex_literal(self, next1, next2): + assert next1 == '/' # otherwise we should not be called! + + self.return_buf = '' + + read = self.ins.read + write = self.outs.write + + in_char_class = False + + write('/') + + next = next2 + while next and (next != '/' or in_char_class): + write(next) + if next == '\\': + write(read(1)) # whatever is next is escaped + elif next == '[': + write(read(1)) # character class cannot be empty + in_char_class = True + elif next == ']': + in_char_class = False + next = read(1) + + write('/') + + def line_comment(self, next1, next2): + assert next1 == next2 == '/' + + read = self.ins.read + + while next1 and next1 not in '\r\n': + next1 = read(1) + while next1 and next1 in '\r\n': + next1 = read(1) + + return next1 + + def block_comment(self, next1, next2): + assert next1 == '/' + assert next2 == '*' + + read = self.ins.read + + # Skip past first /* and avoid catching on /*/...*/ + next1 = read(1) + next2 = read(1) + + comment_buffer = '/*' + while next1 != '*' or next2 != '/': + comment_buffer += next1 + next1 = next2 + next2 = read(1) + + if comment_buffer.startswith("/*!"): + # comment needs preserving + self.outs.write(comment_buffer) + self.outs.write("*/\n") + + + def newline(self, previous_non_space, next2, do_newline): + read = self.ins.read + + if previous_non_space and ( + previous_non_space in self.newlineend_strings + or previous_non_space > '~'): + while 1: + if next2 < '!': + next2 = read(1) + if not next2: + break + else: + if next2 in self.newlinestart_strings \ + or next2 > '~' or next2 == '/': + do_newline = True + break + + return next2, do_newline diff --git a/third_party/python/jsmin/jsmin/__main__.py b/third_party/python/jsmin/jsmin/__main__.py index 58da2e2221403..6d37a3eb7b66d 100644 --- a/third_party/python/jsmin/jsmin/__main__.py +++ b/third_party/python/jsmin/jsmin/__main__.py @@ -1,3 +1,30 @@ +# vim: set fileencoding=utf-8 : + +# This code is original from jsmin by Douglas Crockford, it was translated to +# Python by Baruch Even. It was rewritten by Dave St.Germain for speed. +# +# The MIT License (MIT) +#· +# Copyright (c) 2013 Dave St.Germain +#· +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +#· +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +#· +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import sys, os, glob from jsmin import JavascriptMinify diff --git a/third_party/python/jsmin/jsmin/test.py b/third_party/python/jsmin/jsmin/test.py index 6f7f627fde829..74fbaadeae5cf 100644 --- a/third_party/python/jsmin/jsmin/test.py +++ b/third_party/python/jsmin/jsmin/test.py @@ -1,6 +1,33 @@ +# vim: set fileencoding=utf-8 : + +# This code is original from jsmin by Douglas Crockford, it was translated to +# Python by Baruch Even. It was rewritten by Dave St.Germain for speed. +# +# The MIT License (MIT) +#· +# Copyright (c) 2013 Dave St.Germain +#· +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +#· +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +#· +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import unittest import jsmin -import sys + class JsTests(unittest.TestCase): def _minify(self, js): @@ -14,7 +41,7 @@ def assertEqual(self, thing1, thing2): def assertMinified(self, js_input, expected, **kwargs): minified = jsmin.jsmin(js_input, **kwargs) - assert minified == expected, "%r != %r" % (minified, expected) + assert minified == expected, "\ngot: %r\nexp: %r" % (minified, expected) def testQuoted(self): js = r''' @@ -52,10 +79,8 @@ def testSingleComment(self): } //bye ''' - expected = r""" -if(Object.isFunction(Array.prototype.forEach)) -Array.prototype._each=Array.prototype.forEach;if(!Array.prototype.indexOf)Array.prototype.indexOf=function(item,i){ function(){ foo; location='http://foo.com;';}""" - # print expected + expected = r"""if(Object.isFunction(Array.prototype.forEach)) +Array.prototype._each=Array.prototype.forEach;if(!Array.prototype.indexOf)Array.prototype.indexOf=function(item,i){function(){foo;location='http://foo.com;';}""" self.assertMinified(js, expected) def testEmpty(self): @@ -116,13 +141,22 @@ def testBlockCommentMultipleOpen(self): def testJustAComment(self): self.assertMinified(' // a comment', '') - def test_issue_10(self): + def test_issue_bitbucket_10(self): js = ''' files = [{name: value.replace(/^.*\\\\/, '')}]; // comment A ''' - expected = '''files=[{name:value.replace(/^.*\\\\/,'')}]; A''' + expected = '''files=[{name:value.replace(/^.*\\\\/,'')}];A''' + self.assertMinified(js, expected) + + def test_issue_bitbucket_10_without_semicolon(self): + js = ''' + files = [{name: value.replace(/^.*\\\\/, '')}] + // comment + A + ''' + expected = '''files=[{name:value.replace(/^.*\\\\/,'')}]\nA''' self.assertMinified(js, expected) def testRe(self): @@ -154,7 +188,7 @@ def testIgnoreComment(self): Element.cleanWhitespace(element); """ expected = r"""var options_for_droppable={overlap:options.overlap,containment:options.containment,tree:options.tree,hoverclass:options.hoverclass,onHover:Sortable.onHover} -var options_for_tree={onHover:Sortable.onEmptyHover,overlap:options.overlap,containment:options.containment,hoverclass:options.hoverclass} +var options_for_tree={onHover:Sortable.onEmptyHover,overlap:options.overlap,containment:options.containment,hoverclass:options.hoverclass} Element.cleanWhitespace(element);""" self.assertMinified(js, expected) @@ -206,10 +240,22 @@ def testNoBracesWithComment(self): onFailure: this.onFailure }); """ - expected = r"""onSuccess:function(transport){var js=transport.responseText.strip();if(!/^\[.*\]$/.test(js)) + expected = r"""onSuccess:function(transport){var js=transport.responseText.strip();if(!/^\[.*\]$/.test(js)) throw'Server returned an invalid collection representation.';this._collection=eval(js);this.checkForExternalText();}.bind(this),onFailure:this.onFailure});""" self.assertMinified(js, expected) - + js_without_comment = r""" + onSuccess: function(transport) { + var js = transport.responseText.strip(); + if (!/^\[.*\]$/.test(js)) + throw 'Server returned an invalid collection representation.'; + this._collection = eval(js); + this.checkForExternalText(); + }.bind(this), + onFailure: this.onFailure + }); + """ + self.assertMinified(js_without_comment, expected) + def testSpaceInRe(self): js = r""" num = num.replace(/ /g,''); @@ -274,16 +320,22 @@ def testCommentInObj(self): }""", "{a:1,}") def testCommentInObj2(self): - self.assertMinified("{a: 1//comment\r\n}", "{a:1\n}") + self.assertMinified("{a: 1//comment\r\n}", "{a:1}") def testImplicitSemicolon(self): # return \n 1 is equivalent with return; 1 # so best make sure jsmin retains the newline + self.assertMinified("return\na", "return\na") + + def test_explicit_semicolon(self): self.assertMinified("return;//comment\r\na", "return;a") def testImplicitSemicolon2(self): + self.assertMinified("return//comment...\r\nar", "return\nar") + + def testImplicitSemicolon3(self): self.assertMinified("return//comment...\r\na", "return\na") - + def testSingleComment2(self): self.assertMinified('x.replace(/\//, "_")// slash to underscore', 'x.replace(/\//,"_")') @@ -300,7 +352,12 @@ def testReturn(self): original = ''' return foo;//comment return bar;''' - expected = 'return foo; return bar;' + expected = 'return foo;return bar;' + self.assertMinified(original, expected) + original = ''' + return foo + return bar;''' + expected = 'return foo\nreturn bar;' self.assertMinified(original, expected) def test_space_plus(self): @@ -325,12 +382,17 @@ def test_space_in_regex(self): original = '/a (a)/.test("a")' self.assertMinified(original, original) + def test_brackets_around_slashed_regex(self): + original = 'function a() { /\//.test("a") }' + expected = 'function a(){/\//.test("a")}' + self.assertMinified(original, expected) + def test_angular_1(self): original = '''var /** holds major version number for IE or NaN for real browsers */ msie, jqLite, // delay binding since jQuery could be loaded after us.''' minified = jsmin.jsmin(original) - self.assertTrue('var msie' in minified) + self.assertTrue('var\nmsie' in minified) def test_angular_2(self): original = 'var/* comment */msie;' @@ -390,5 +452,193 @@ def testBackticksTagged(self): original = 'tag`Hello ${ a + b } world ${ a * b}`;' self.assertMinified(original, original, quote_chars="'\"`") + def test_issue_bitbucket_16(self): + original = """ + f = function() { + return /DataTree\/(.*)\//.exec(this._url)[1]; + } + """ + self.assertMinified( + original, + 'f=function(){return /DataTree\/(.*)\//.exec(this._url)[1];}') + + def test_issue_bitbucket_17(self): + original = "// hi\n/^(get|post|head|put)$/i.test('POST')" + self.assertMinified(original, + "/^(get|post|head|put)$/i.test('POST')") + + def test_issue_6(self): + original = ''' + respond.regex = { + comments: /\/\*[^*]*\*+([^/][^*]*\*+)*\//gi, + urls: 'whatever' + }; + ''' + expected = original.replace(' ', '').replace('\n', '') + self.assertMinified(original, expected) + + def test_issue_9(self): + original = '\n'.join([ + 'var a = \'hi\' // this is a comment', + 'var a = \'hi\' /* this is also a comment */', + 'console.log(1) // this is a comment', + 'console.log(1) /* this is also a comment */', + '1 // this is a comment', + '1 /* this is also a comment */', + '{} // this is a comment', + '{} /* this is also a comment */', + '"YOLO" /* this is a comment */', + '"YOLO" // this is a comment', + '(1 + 2) // comment', + '(1 + 2) /* yup still comment */', + 'var b' + ]) + expected = '\n'.join([ + 'var a=\'hi\'', + 'var a=\'hi\'', + 'console.log(1)', + 'console.log(1)', + '1', + '1', + '{}', + '{}', + '"YOLO"', + '"YOLO"', + '(1+2)', + '(1+2)', + 'var b' + ]) + self.assertMinified(expected, expected) + self.assertMinified(original, expected) + + def test_newline_between_strings(self): + self.assertMinified('"yolo"\n"loyo"', '"yolo"\n"loyo"') + + def test_issue_10_comments_between_tokens(self): + self.assertMinified('var/* comment */a', 'var a') + + def test_ends_with_string(self): + self.assertMinified('var s = "s"', 'var s="s"') + + def test_short_comment(self): + self.assertMinified('a;/**/b', 'a;b') + + def test_shorter_comment(self): + self.assertMinified('a;/*/*/b', 'a;b') + + def test_block_comment_with_semicolon(self): + self.assertMinified('a;/**/\nb', 'a;b') + + def test_block_comment_With_implicit_semicolon(self): + self.assertMinified('a/**/\nvar b', 'a\nvar b') + + def test_issue_9_single_comments(self): + original = ''' + var a = "hello" // this is a comment + a += " world" + ''' + self.assertMinified(original, 'var a="hello"\na+=" world"') + + def test_issue_9_multi_comments(self): + original = ''' + var a = "hello" /* this is a comment */ + a += " world" + ''' + self.assertMinified(original, 'var a="hello"\na+=" world"') + + def test_issue_12_re_nl_if(self): + original = ''' + var re = /\d{4}/ + if (1) { console.log(2); }''' + self.assertMinified( + original, 'var re=/\d{4}/\nif(1){console.log(2);}') + + def test_issue_12_re_nl_other(self): + original = ''' + var re = /\d{4}/ + g = 10''' + self.assertMinified(original , 'var re=/\d{4}/\ng=10') + + def test_preserve_copyright(self): + original = ''' + function this() { + /*! Copyright year person */ + console.log('hello!'); + } + + /*! Copyright blah blah + * + * Some other text + */ + + var a; + ''' + expected = """function this(){/*! Copyright year person */ +console.log('hello!');}/*! Copyright blah blah + * + * Some other text + */\n\nvar a;""" + self.assertMinified(original, expected) + + def test_issue_14(self): + original = 'return x / 1;' + self.assertMinified(original, 'return x/1;') + + def test_issue_14_with_char_from_return(self): + original = 'return r / 1;' + self.assertMinified(original, 'return r/1;') + + +class RegexTests(unittest.TestCase): + + def regex_recognise(self, js): + if not jsmin.is_3: + if jsmin.cStringIO and not isinstance(js, unicode): + # strings can use cStringIO for a 3x performance + # improvement, but unicode (in python2) cannot + klass = jsmin.cStringIO.StringIO + else: + klass = jsmin.StringIO.StringIO + else: + klass = jsmin.io.StringIO + ins = klass(js[2:]) + outs = klass() + jsmin.JavascriptMinify(ins, outs).regex_literal(js[0], js[1]) + return outs.getvalue() + + def assert_regex(self, js_input, expected): + assert js_input[0] == '/' # otherwise we should not be testing! + recognised = self.regex_recognise(js_input) + assert recognised == expected, "\n in: %r\ngot: %r\nexp: %r" % (js_input, recognised, expected) + + def test_simple(self): + self.assert_regex('/123/g', '/123/') + + def test_character_class(self): + self.assert_regex('/a[0-9]b/g', '/a[0-9]b/') + + def test_character_class_with_slash(self): + self.assert_regex('/a[/]b/g', '/a[/]b/') + + def test_escaped_forward_slash(self): + self.assert_regex(r'/a\/b/g', r'/a\/b/') + + def test_escaped_back_slash(self): + self.assert_regex(r'/a\\/g', r'/a\\/') + + def test_empty_character_class(self): + # This one is subtle: an empty character class is not allowed, afaics + # from http://regexpal.com/ Chrome Version 44.0.2403.155 (64-bit) Mac + # so this char class is interpreted as containing ]/ *not* as char + # class [] followed by end-of-regex /. + self.assert_regex('/a[]/]b/g', '/a[]/]b/') + + def test_precedence_of_parens(self): + # judging from + # http://regexpal.com/ Chrome Version 44.0.2403.155 (64-bit) Mac + # () have lower precedence than [] + self.assert_regex('/a([)])b/g', '/a([)])b/') + self.assert_regex('/a[(]b/g', '/a[(]b/') + if __name__ == '__main__': unittest.main() diff --git a/third_party/python/jsmin/setup.py b/third_party/python/jsmin/setup.py index 22639d1e356c7..7968aecdd0527 100644 --- a/third_party/python/jsmin/setup.py +++ b/third_party/python/jsmin/setup.py @@ -5,11 +5,6 @@ os.environ['COPYFILE_DISABLE'] = 'true' # this disables including resource forks in tar files on os x -extra = {} -if sys.version_info >= (3,0): - extra['use_2to3'] = True - - def long_description(): return open('README.rst').read() + '\n' + open('CHANGELOG.txt').read() @@ -18,30 +13,24 @@ def long_description(): name="jsmin", version=re.search(r'__version__ = ["\']([^"\']+)', open('jsmin/__init__.py').read()).group(1), packages=['jsmin'], - description='JavaScript minifier.\nPLEASE UPDATE TO VERSION >= 2.0.6. Older versions have a serious bug related to comments.', + description='JavaScript minifier.', long_description=long_description(), author='Dave St.Germain', author_email='dave@st.germa.in', maintainer='Tikitu de Jager', maintainer_email='tikitu+jsmin@logophile.org', - test_suite='jsmin.test.JsTests', + test_suite='jsmin.test', license='MIT License', - url='https://bitbucket.org/dcs/jsmin/', + url='https://github.com/tikitu/jsmin/', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3 :: Only', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Pre-processors', 'Topic :: Text Processing :: Filters', - ], - **extra + ] ) diff --git a/third_party/python/poetry.lock b/third_party/python/poetry.lock index ff6ec80fe2dd7..74bae76879bae 100644 --- a/third_party/python/poetry.lock +++ b/third_party/python/poetry.lock @@ -270,8 +270,8 @@ i18n = ["Babel (>=0.8)"] [[package]] name = "jsmin" -version = "2.1.0" -description = "JavaScript minifier.\nPLEASE UPDATE TO VERSION >= 2.0.6. Older versions have a serious bug related to comments." +version = "3.0.0" +description = "JavaScript minifier." category = "main" optional = false python-versions = "*" @@ -728,7 +728,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "2c8645955b35c4a1b90c8f5473f2b29a8d492613ea054cb17dbc20dadcaf19d6" +content-hash = "6bafc87724a8533dedeaecea8d62ed557ee62de71d0e79c43ca7736558543c56" [metadata.files] aiohttp = [ @@ -865,7 +865,7 @@ jinja2 = [ {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, ] jsmin = [ - {file = "jsmin-2.1.0.tar.gz", hash = "sha256:5d07bf0251a4128e5e8e8eef603849b6b5741c337bff087731a248f9cc774f56"}, + {file = "jsmin-3.0.0.tar.gz", hash = "sha256:88fc1bd6033a47c5911dbcada7d279c7a8b7ad0841909590f6a742c20c4d2e08"}, ] json-e = [ {file = "json-e-2.7.0.tar.gz", hash = "sha256:d8c1ec3f5bbc7728c3a504ebe58829f283c64eca230871e4eefe974b4cdaae4a"}, diff --git a/third_party/python/requirements.in b/third_party/python/requirements.in index 5431b5166a02d..6f99989d844a0 100644 --- a/third_party/python/requirements.in +++ b/third_party/python/requirements.in @@ -13,7 +13,7 @@ fluent.syntax==0.18.1 glean_parser==5.0.1 # Pin importlib-metadata to a version compatible with poetry importlib-metadata==1.7.0 -jsmin==2.1.0 +jsmin==3.0.0 json-e==2.7.0 mozilla-version==0.3.4 packaging==20.9 diff --git a/third_party/python/requirements.txt b/third_party/python/requirements.txt index 3e1f0a142fa97..a390686b0b48f 100644 --- a/third_party/python/requirements.txt +++ b/third_party/python/requirements.txt @@ -106,8 +106,8 @@ iso8601==0.1.14; python_version <= "3.6" \ jinja2==2.11.3; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ --hash=sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419 \ --hash=sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6 -jsmin==2.1.0 \ - --hash=sha256:5d07bf0251a4128e5e8e8eef603849b6b5741c337bff087731a248f9cc774f56 +jsmin==3.0.0 \ + --hash=sha256:88fc1bd6033a47c5911dbcada7d279c7a8b7ad0841909590f6a742c20c4d2e08 json-e==2.7.0 \ --hash=sha256:d8c1ec3f5bbc7728c3a504ebe58829f283c64eca230871e4eefe974b4cdaae4a jsonschema==3.2.0 \