Merge branch 'master' into add-tests

This commit is contained in:
MacKLess 2018-09-27 09:32:29 -07:00 committed by GitHub
commit 1e99b57827
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
99 changed files with 19216 additions and 8492 deletions

View File

@ -8,10 +8,10 @@ ratings:
- "**.js"
- "**.py"
exclude_paths:
- test/resources/**
- webpack.config.js
- "**/resources/**"
- js/test/**
- test/data/**
- web/lib/**
- js/lib/beautify.js
- js/lib/beautify-css.js
- js/lib/beautify-html.js
- tools/template/*
- "**/generated/*"

View File

@ -6,8 +6,7 @@ node_modules/**
python/**
target/**
tools/**
test/resources/underscore-min.js
test/resources/underscore.js
test/resources/*
web/lib/**
build/**

View File

@ -4,8 +4,10 @@
"eqeqeq": true,
"noarg": true,
"nocomma": true,
"node": true,
"nonbsp": true,
"nonew": true,
"strict": true,
"unused": true,
"esversion": 3
}

View File

@ -1,42 +1,112 @@
# Changelog
## v1.8.0-rc2
## v1.8.6
### Description
Beautifier has moved to https://beautifier.io
### Closed Issues
* JS beautify break the angular compile ([#1544](https://github.com/beautify-web/js-beautify/issues/1544))
* base64 string is broken with v1.8.4 ([#1535](https://github.com/beautify-web/js-beautify/issues/1535))
* Bookmarklet becomes totally useless ([#1408](https://github.com/beautify-web/js-beautify/issues/1408))
* HTTPS ([#1399](https://github.com/beautify-web/js-beautify/issues/1399))
* Beautify breaks when js starts with space followed by multi-line comment ([#789](https://github.com/beautify-web/js-beautify/issues/789))
## v1.8.4
### Description
Broader adoption of 1.8.x revealed a few more high priority fixes
### Closed Issues
* Multiple newlines added between empty textarea and "unformatted" inline elements ([#1534](https://github.com/beautify-web/js-beautify/issues/1534))
* unindent_chained_methods broken ([#1533](https://github.com/beautify-web/js-beautify/issues/1533))
## v1.8.3
### Description
### Closed Issues
* Missing Bower Assets ([#1521](https://github.com/beautify-web/js-beautify/issues/1521))
* Javascript ternary breaked with `await` ([#1519](https://github.com/beautify-web/js-beautify/issues/1519))
* Object property indented after `await` ([#1517](https://github.com/beautify-web/js-beautify/issues/1517))
* Handlebars formatting problems ([#870](https://github.com/beautify-web/js-beautify/issues/870))
* beautify.js doesn't have indent_level option ([#724](https://github.com/beautify-web/js-beautify/issues/724))
## v1.8.1
### Description
### Closed Issues
* Why npm is a dependency? ([#1516](https://github.com/beautify-web/js-beautify/issues/1516))
* indent_inner_html not working in v1.8.0 ([#1514](https://github.com/beautify-web/js-beautify/issues/1514))
## v1.8.0
### Description
Massive set of fixes and improvements.
Thanks to contributors: @cheerypick, @swan46, @MacKLess, @Elrendio, @madman-bob, @amanda-bot, @Hirse
Thanks to contributors: @cheerypick, @swan46, @MacKLess, @Elrendio, @madman-bob, @amanda-bot, @Hirse, @aeschli, and many more.
Special thanks to @astronomersiva and @garretwilson for finding key bugs in the RC releases,
and to @MacKLess for driving down the open bug count with tons of regression tests.
Highlights:
* CSS: `newline_between_rules` support for nested rules - CSS/SASS/SCSS/LESS (@MacKLess)
* CSS: @import support in CSS (@MacKLess)
* HTML: inline element support (@madman-bob)
* HTML: `wrap_attributes` setting `align-multiple` (@cheerypick)
* HTML: optional close tags do not over indent - li, tr, etc.
* HTML: Improved line wrapping in HTML - still not fully correct
* HTML: 10x performance improvement in HTML beautifier
* JS: ES6 BigInt support (@thejoshwolfe)
* JS: ES6 Dynamic import support
* CSS: :hover for @extend formatting (@MacKLess)
* HTML: Incorrect line wrapping issue (@andreyvolokitin)
* JS: Javascript ++ Operator Indentation (@Elrendio)
* JS: Better packer handling in Python (@swan46)
### Closed Issues
* list items of nested lists get indented backwards ([#1501](https://github.com/beautify-web/js-beautify/issues/1501))
* Make beautifier auto-convert options with dashes into underscores ([#1497](https://github.com/beautify-web/js-beautify/issues/1497))
* ReferenceError: token is not defined ([#1496](https://github.com/beautify-web/js-beautify/issues/1496))
* Publish v1.8.0 ([#1495](https://github.com/beautify-web/js-beautify/issues/1495))
* still probem #1439 / #1337 ([#1491](https://github.com/beautify-web/js-beautify/issues/1491))
* Duplicating HTML Code Nested In PHP ([#1483](https://github.com/beautify-web/js-beautify/issues/1483))
* Handlebars - `if` tags are broken when using helper with `textarea` ([#1482](https://github.com/beautify-web/js-beautify/issues/1482))
* TypeError: Cannot read property '1' of null ([#1481](https://github.com/beautify-web/js-beautify/issues/1481))
* Space in Self Closing Tag Issue ([#1478](https://github.com/beautify-web/js-beautify/issues/1478))
* Weird Formatting in VSCode ([#1475](https://github.com/beautify-web/js-beautify/issues/1475))
* Indent with tab issue on website ([#1470](https://github.com/beautify-web/js-beautify/issues/1470))
* Contents of hbs tags are converted to lowercase ([#1464](https://github.com/beautify-web/js-beautify/issues/1464))
* HTML tags are indented wrongly when attributes are present ([#1462](https://github.com/beautify-web/js-beautify/issues/1462))
* hbs tags are stripped when there is a comment below or inline ([#1461](https://github.com/beautify-web/js-beautify/issues/1461))
* Spaces added to handlebars with '=' ([#1460](https://github.com/beautify-web/js-beautify/issues/1460))
* jsbeautifier.org don't works ([#1445](https://github.com/beautify-web/js-beautify/issues/1445))
* Commenting code and then beautifying removes line breaks ([#1440](https://github.com/beautify-web/js-beautify/issues/1440))
* Less: Where is my space? ([#1411](https://github.com/beautify-web/js-beautify/issues/1411))
* No newline after @import ([#1406](https://github.com/beautify-web/js-beautify/issues/1406))
* "html.format.wrapAttributes": "force-aligned" adds empty line on long attributes ([#1403](https://github.com/beautify-web/js-beautify/issues/1403))
* HTML: wrap_line_length is handled incorrectly ([#1401](https://github.com/beautify-web/js-beautify/issues/1401))
* js-beautify is breaking code by adding space after import ([#1393](https://github.com/beautify-web/js-beautify/issues/1393))
* JS-Beautify should format XML tags inside HTML files ([#1383](https://github.com/beautify-web/js-beautify/issues/1383))
* python unpacker can not handle if radix given as [] and not as a number ([#1381](https://github.com/beautify-web/js-beautify/issues/1381))
* unindent_chained_methods breaks indentation for if statements without brackets ([#1378](https://github.com/beautify-web/js-beautify/issues/1378))
* function parameters merged into single line when starting with ! or [ ([#1374](https://github.com/beautify-web/js-beautify/issues/1374))
* CSS selector issue (header > div[class~="div-all"]) in SCSS file ([#1373](https://github.com/beautify-web/js-beautify/issues/1373))
* Add "Create Issue for Unexpected Output" button to website ([#1371](https://github.com/beautify-web/js-beautify/issues/1371))
* Add combobox to control type of beautification ([#1370](https://github.com/beautify-web/js-beautify/issues/1370))
* Add Options textbox to website for debugging ([#1369](https://github.com/beautify-web/js-beautify/issues/1369))
* Python version fails to properly beautify packed code ([#1367](https://github.com/beautify-web/js-beautify/issues/1367))
* How to keep comments on their own lines after formating ([#1348](https://github.com/beautify-web/js-beautify/issues/1348))
* Beautification of Multiline PHP ([#1346](https://github.com/beautify-web/js-beautify/issues/1346))
* Beautification of PHP with echo short tags ([#1339](https://github.com/beautify-web/js-beautify/issues/1339))
* URGENT: @extend with :hover, :focus and so on... ([#1331](https://github.com/beautify-web/js-beautify/issues/1331))
* JSBeautify options for programmatic use? ([#1327](https://github.com/beautify-web/js-beautify/issues/1327))
* js-beautify: fix handling for --eol and --outfile ([#1315](https://github.com/beautify-web/js-beautify/pull/1315))
* Note that `gsort` is GNU sort ([#1314](https://github.com/beautify-web/js-beautify/issues/1314))
* `pip` doesn't use same version as `/usr/bin/env python` ([#1312](https://github.com/beautify-web/js-beautify/issues/1312))
* Wrap and align html attributes when line reaches wrap-line-length ([#1285](https://github.com/beautify-web/js-beautify/issues/1285))
* Javascript ++ Operator get wrong indent ([#1283](https://github.com/beautify-web/js-beautify/issues/1283))
* Create beta channel for releases ([#1255](https://github.com/beautify-web/js-beautify/issues/1255))
* await import(...) ([#1228](https://github.com/beautify-web/js-beautify/issues/1228))
* The result of "Format document" is weird of certain HTML content. ([#1223](https://github.com/beautify-web/js-beautify/issues/1223))
* (next_tag || "").match is not a function ([#1202](https://github.com/beautify-web/js-beautify/issues/1202))
* `force-expand-multiline` doesn't work as expected ([#1186](https://github.com/beautify-web/js-beautify/issues/1186))
* HTML text content formatted incorrectly ([#1184](https://github.com/beautify-web/js-beautify/issues/1184))
* Nested span tags not indenting properly ([#1167](https://github.com/beautify-web/js-beautify/issues/1167))
* SCSS Comment Issue ([#1165](https://github.com/beautify-web/js-beautify/issues/1165))
* Less function parameters are wrapped unexpected ([#1156](https://github.com/beautify-web/js-beautify/issues/1156))
* TypeError: Cannot read property 'replace' of undefined ([#1120](https://github.com/beautify-web/js-beautify/issues/1120))
* HTML formatting wraps ending block tag for no reason with nested inline elements ([#1041](https://github.com/beautify-web/js-beautify/issues/1041))
* not correctly joining lines for HTML ([#1033](https://github.com/beautify-web/js-beautify/issues/1033))
## v1.7.5

46
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at team@beautifier.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -9,12 +9,12 @@ Fixes and enhancements are totally welcome. We prefer you to file an issue befo
### 0. Prereqisites for development
* bash
* bash
* make
* nodejs - v10.x (with npm)
* python - v2.7.x or v3.x (with pip)
If you encounter issues and cannot build, come chat on gitter.im. We're happy to help.
If you encounter issues and cannot build, come chat on gitter.im. We're happy to help.
### 1. Build and Test Locally
This repository holds two mostly identical implementations of the beautifiers: a JavaScript implementation and a Python implementation.
@ -62,7 +62,7 @@ Files related to the JavaScript implementations of the beautifiers.
Files related to the Python implementations of the beautifiers.
## `web`
Files related to http://jsbeautifier.org/.
Files related to https://beautifier.io/.
## `test`
Test data files and support files used to generate implementation-specific test files.
@ -73,8 +73,11 @@ Test data files and support files used to generate implementation-specific test
## Master
We use the `master` branch as the primary development branch.
## Milestone Release Tags
Each release is has a tag created for it when it is released. The latest release is linked from the `README.md`.
## Release
We use the `release` branch to hold releases, including built files for bower and the website.
# Tags
Each release is has a tag created for it in the `release` branch when it is published. The latest release is linked from the `README.md`.
## Attic
This project has been around for a while. While some parts have improved significantly over time, others fell
@ -119,6 +122,7 @@ This is script will:
* Update README.md with correct cdn links
* Update CHANGLOG.md with the milestone description and a list of closed issues
* Commit the built files to the `release` branch
* Publish the python version to PyPI
* Publish the javascript version to npm
* Merge the changes and publish them on the gh-pages branch

View File

@ -32,14 +32,14 @@ py: generate-tests $(BUILD_DIR)/python
@echo Testing python beautify functionality...
$(SCRIPT_DIR)/python-dev python python/js-beautify-test.py || exit 1
jstest: depends js package
jstest: depends js build/*.tgz
@echo Testing javascript implementation...
@$(NODE) js/test/node-beautify-tests.js || exit 1
@$(NODE) js/test/amd-beautify-tests.js || exit 1
@$(NODE) --version && \
./js/test/shell-test.sh
pytest: depends py package
pytest: depends py python/dist/*
@echo Testing python implementation...
@cd python && \
$(PYTHON) --version && \
@ -50,12 +50,16 @@ package: js py build/*.tgz python/dist/*
perf:
@echo ----------------------------------------
@echo Testing beautify performance...
@echo Testing node js beautify performance...
$(NODE) js/test/node-beautify-perf-tests.js || exit 1
@echo Testing node css beautify performance...
$(NODE) js/test/node-beautify-css-perf-tests.js || exit 1
@echo Testing html-beautify performance...
$(NODE) js/test/node-beautify-html-perf-tests.js || exit 1
@echo Testing python beautify performance...
@echo Testing python js beautify performance...
$(SCRIPT_DIR)/python-dev python python/test-perf-jsbeautifier.py || exit 1
@echo Testing python css beautify performance...
$(SCRIPT_DIR)/python-dev python python/test-perf-cssbeautifier.py || exit 1
@echo ----------------------------------------
generate-tests: $(BUILD_DIR)/generate
@ -67,7 +71,7 @@ beautify:
#######################################################
# javascript bundle generation
js/lib/*.js: $(BUILD_DIR)/node $(BUILD_DIR)/generate $(wildcard js/src/**/*) js/index.js tools/template/* webpack.config.js
js/lib/*.js: $(BUILD_DIR)/node $(BUILD_DIR)/generate $(wildcard js/src/**/*) $(wildcard web/*.js) js/index.js tools/template/* webpack.config.js
$(SCRIPT_DIR)/build.sh js

View File

@ -15,10 +15,10 @@
This little beautifier will reformat and re-indent bookmarklets, ugly
JavaScript, unpack scripts packed by Dean Edwards popular packer,
as well as deobfuscate scripts processed by
[javascriptobfuscator.com](http://javascriptobfuscator.com/).
as well as partly deobfuscate scripts processed by the npm package
[javascript-obfuscator](https://github.com/javascript-obfuscator/javascript-obfuscator).
Open [jsbeautifier.org](http://jsbeautifier.org/) to try it out. Options are available via the UI.
Open [beautifier.io](https://beautifier.io/) to try it out. Options are available via the UI.
# Contributors Needed
I'm putting this front and center above because existing owners have very limited time to work on this project currently.
@ -62,17 +62,17 @@ JS Beautifier is hosted on two CDN services: [cdnjs](https://cdnjs.com/libraries
To pull the latest version from one of these services include one set of the script tags below in your document:
```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.0-rc4/beautify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.0-rc4/beautify-css.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.0-rc4/beautify-html.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.6/beautify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.6/beautify-css.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.6/beautify-html.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.0-rc4/beautify.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.0-rc4/beautify-css.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.0-rc4/beautify-html.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.6/beautify.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.6/beautify-css.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.8.6/beautify-html.min.js"></script>
<script src="https://cdn.rawgit.com/beautify-web/js-beautify/gh-pages/js/lib/beautify.js"></script>
<script src="https://cdn.rawgit.com/beautify-web/js-beautify/gh-pages/js/lib/beautify-css.js"></script>
<script src="https://cdn.rawgit.com/beautify-web/js-beautify/gh-pages/js/lib/beautify-html.js"></script>
<script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.8.6/js/lib/beautify.js"></script>
<script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.8.6/js/lib/beautify-css.js"></script>
<script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.8.6/js/lib/beautify-html.js"></script>
```
Older versions are available by changing the version number.
@ -92,7 +92,7 @@ $ pip install jsbeautifier
You can beautify javascript using JS Beautifier in your web browser, or on the command-line using node.js or python.
## Web Browser
Open [jsbeautifier.org](http://jsbeautifier.org/). Options are available via the UI.
Open [beautifier.io](https://beautifier.io/). Options are available via the UI.
## Web Libary
The script tags above expose three functions: `js_beautify`, `css_beautify`, and `html_beautify`.
@ -180,6 +180,7 @@ Beautifier Options:
-E, --space-in-empty-paren Add a single space inside empty paren, ie. f( )
-j, --jslint-happy Enable jslint-stricter mode
-a, --space-after-anon-function Add a space before an anonymous function's parens, ie. function ()
--space-after-named-function Add a space before a named function's parens, i.e. function example ()
-b, --brace-style [collapse|expand|end-expand|none][,preserve-inline] [collapse,preserve-inline]
-u, --unindent-chained-methods Don't indent chained method calls
-B, --break-chained-methods Break chained method calls across subsequent lines
@ -365,13 +366,13 @@ You are free to use this in any way you want, in case you find this useful or wo
# Credits
* Created by Einar Lielmanis, <einar@jsbeautifier.org>
* Created by Einar Lielmanis, <einar@beautifier.io>
* Python version flourished by Stefano Sanfilippo <a.little.coder@gmail.com>
* Command-line for node.js by Daniel Stockman <daniel.stockman@gmail.com>
* Maintained and expanded by Liam Newman <bitwiseman@gmail.com>
* Maintained and expanded by Liam Newman <bitwiseman@beautifier.io>
Thanks also to Jason Diamond, Patrick Hof, Nochum Sossonko, Andreas Schneider, Dave
Vasilevsky, Vital Batmanov, Ron Baldwin, Gabriel Harrison, Chris J. Shull,
Mathias Bynens, Vittorio Gambaletta and others.
(README.md: js-beautify@1.8.0-rc4)
(README.md: js-beautify@1.8.6)

View File

@ -25,254 +25,255 @@
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<head>
<meta charset="utf-8" /><meta http-equiv="Content-type" content="text/html;charset=utf-8" />
<!-- if you feel an urge to move this to bootstrap or something, you're most welcome -->
<title>Online JavaScript beautifier</title>
<link rel="icon" href="web/favicon.png" type="image/png">
<link rel="stylesheet" href="web/lib/codemirror/lib/codemirror.css">
<script src="web/lib/codemirror/lib/codemirror.js"></script>
<script src="web/lib/codemirror/mode/javascript/javascript.js"></script>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html;charset=utf-8" />
<!-- if you feel an urge to move this to bootstrap or something, you're most welcome -->
<title>Online JavaScript beautifier</title>
<link rel="icon" href="web/favicon.png" type="image/png">
<link rel="stylesheet" href="web/lib/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="web/common-style.css"></style>
<script src="web/lib/codemirror/lib/codemirror.js"></script>
<script src="web/lib/codemirror/mode/javascript/javascript.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.2.0/js.cookie.min.js"
integrity="sha256-9Nt2r+tJnSd2A2CRUvnjgsD+ES1ExvjbjBNqidm9doI="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.min.js"
integrity="sha256-0SGl1PJNDyJwcV5T+weg2zpEMrh7xvlwO4oXgvZCeZk="
crossorigin="anonymous"></script>
<link rel="stylesheet" href="web/common-style.css">
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=default&flags=gated"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.2.0/js.cookie.min.js" integrity="sha256-9Nt2r+tJnSd2A2CRUvnjgsD+ES1ExvjbjBNqidm9doI=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.min.js" integrity="sha256-0SGl1PJNDyJwcV5T+weg2zpEMrh7xvlwO4oXgvZCeZk=" crossorigin="anonymous"></script>
<script src="js/lib/unpackers/javascriptobfuscator_unpacker.js"></script>
<script src="js/lib/unpackers/urlencode_unpacker.js"></script>
<script src="js/lib/unpackers/p_a_c_k_e_r_unpacker.js"></script>
<script src="js/lib/unpackers/myobfuscate_unpacker.js"></script>
<script src="web/common-function.js"></script>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=default&flags=gated"></script>
<script src="js/lib/unpackers/javascriptobfuscator_unpacker.js"></script>
<script src="js/lib/unpackers/urlencode_unpacker.js"></script>
<script src="js/lib/unpackers/p_a_c_k_e_r_unpacker.js"></script>
<script src="js/lib/unpackers/myobfuscate_unpacker.js"></script>
<script src="web/common-function.js"></script>
</head>
<h1>Online JavaScript Beautifier <span id="version-number"></span></h1>
<body>
<div id="about">
<p>
<a class="self" href="./">Beautify, unpack or deobfuscate JavaScript and HTML, make JSON/JSONP readable, etc.</a>
</p>
<p>
All of the source code is completely free and open, available on <a href="https://github.com/beautify-web/js-beautify">GitHub</a> under MIT licence,
<br>and we have a command-line version, python library and a <a href="https://npmjs.org/package/js-beautify">node package</a> as well.
</p>
</div>
<div id="about">
<p>
<a class="self" href="./">Beautify, unpack or deobfuscate JavaScript and HTML, make JSON/JSONP readable, etc.</a>
</p>
<p>
All of the source code is completely free and open, available on <a href="https://github.com/beautify-web/js-beautify">GitHub</a> under MIT licence,
<br>and we have a command-line version, python library and a <a href="https://npmjs.org/package/js-beautify">node package</a> as well.
</p>
</div>
<table id="options">
<tr>
<td>
<select name="tabsize" id="tabsize">
<option value="1">Indent with a tab character</option>
<option value="2">Indent with 2 spaces</option>
<option value="3">Indent with 3 spaces</option>
<option value="4">Indent with 4 spaces</option>
<option value="8">Indent with 8 spaces</option>
</select>
<br>
<select name="max-preserve-newlines" id="max-preserve-newlines">
<option value="-1">Remove all extra newlines</option>
<option value="1">Allow 1 newline between tokens</option>
<option value="2">Allow 2 newlines between tokens</option>
<option value="5">Allow 5 newlines between tokens</option>
<option value="10">Allow 10 newlines between tokens</option>
<option value="0">Allow unlimited newlines between tokens</option>
</select>
<br>
<select name="wrap-line-length" id="wrap-line-length">
<option value="0">Do not wrap lines</option>
<option value="40">Wrap lines near 40 characters</option>
<option value="70">Wrap lines near 70 characters</option>
<option value="80">Wrap lines near 80 characters</option>
<option value="110">Wrap lines near 110 characters</option>
<option value="120">Wrap lines near 120 characters</option>
<option value="160">Wrap lines near 160 characters</option>
</select>
<br>
<select id="brace-style">
<option value="collapse">Braces with control statement</option>
<option value="expand">Braces on own line</option>
<option value="end-expand">End braces on own line</option>
<option value="none">Attempt to keep braces where they are</option>
</select>
<p style="margin:6px 0 0 0">HTML &lt;style&gt;, &lt;script&gt; formatting:</p>
<select id="indent-scripts">
<option value="keep">Keep indent level of the tag</option>
<option value="normal">Add one indent level</option>
<option value="separate">Separate indentation</option>
</select>
<p style="margin:6px 0 0 0">Additional Settings (JSON):</p>
<textarea id="additional-options" rows="5" cols="32">{}</textarea>
<p id ="additional-options-error" hidden style="margin:6px 0 0 0; color:red ">Could Not Parse JSON!</p>
</td>
<td>
<input class="checkbox" type="checkbox" id="end-with-newline">
<label for="end-with-newline">End script and style with newline?</label>
<br>
<input class="checkbox" type="checkbox" id="e4x">
<label for="e4x">Support e4x/jsx syntax</label>
<br>
<input class="checkbox" type="checkbox" id="comma-first">
<label for="comma-first">Use comma-first list style?</label>
<br>
<input class="checkbox" type="checkbox" id="detect-packers">
<label for="detect-packers">Detect packers and obfuscators?</label>
<br>
<input class="checkbox" type="checkbox" id="brace-preserve-inline">
<label for="brace-preserve-inline">Preserve inline braces/code blocks?</label>
<br>
<input class="checkbox" type="checkbox" id="keep-array-indentation">
<label for="keep-array-indentation">Keep array indentation?</label>
<br>
<input class="checkbox" type="checkbox" id="break-chained-methods">
<label for="break-chained-methods">Break lines on chained methods?</label>
<br>
<input class="checkbox" type="checkbox" id="space-before-conditional">
<label for="space-before-conditional">Space before conditional: "if(x)" / "if (x)"</label>
<br>
<input class="checkbox" type="checkbox" id="unescape-strings">
<label for="unescape-strings">Unescape printable chars encoded as \xNN or \uNNNN?</label>
<br>
<input class="checkbox" type="checkbox" id="jslint-happy">
<label for="jslint-happy">Use JSLint-happy formatting tweaks?</label>
<br>
<input class="checkbox" type="checkbox" id="indent-inner-html">
<label for="indent-inner-html">Indent &lt;head&gt; and &lt;body&gt; sections?</label>
<br><a href="?without-codemirror" class="turn-off-codemirror">Use a simple textarea for code input?</a>
</td>
</tr>
</table>
<div style="line-height: 0">
<select name="language" id="language">
<option value="auto">Auto Detect</option>
<option value="css">CSS</option>
<option value="html">HTML</option>
<option value="js">JavaScript</option>
<table id="options">
<tr>
<td>
<select name="tabsize" id="tabsize">
<option value="1">Indent with a tab character</option>
<option value="2">Indent with 2 spaces</option>
<option value="3">Indent with 3 spaces</option>
<option value="4">Indent with 4 spaces</option>
<option value="8">Indent with 8 spaces</option>
</select>
<button class="submit"><strong>Beautify JavaScript or HTML</strong> <em>(ctrl-enter)</em>
</button>
<textarea id="source" rows="30" cols="160"></textarea>
<button class="submit"><strong>Beautify JavaScript or HTML</strong> <em>(ctrl-enter)</em>
</button>
</div>
<br>
<p style="margin:6px 0 0 0">Your Selected Options (JSON):</p>
<div style="line-height: 0">
<textarea readonly id="options-selected" rows="10" cols="40"></textarea>
</div>
<select name="max-preserve-newlines" id="max-preserve-newlines">
<option value="-1">Remove all extra newlines</option>
<option value="1">Allow 1 newline between tokens</option>
<option value="2">Allow 2 newlines between tokens</option>
<option value="5">Allow 5 newlines between tokens</option>
<option value="10">Allow 10 newlines between tokens</option>
<option value="0">Allow unlimited newlines between tokens</option>
</select>
<br>
<p id="open-issue" hidden>Not pretty enough for you?
<select name="wrap-line-length" id="wrap-line-length">
<option value="0">Do not wrap lines</option>
<option value="40">Wrap lines near 40 characters</option>
<option value="70">Wrap lines near 70 characters</option>
<option value="80">Wrap lines near 80 characters</option>
<option value="110">Wrap lines near 110 characters</option>
<option value="120">Wrap lines near 120 characters</option>
<option value="160">Wrap lines near 160 characters</option>
</select>
<br>
<select id="brace-style">
<option value="collapse">Braces with control statement</option>
<option value="expand">Braces on own line</option>
<option value="end-expand">End braces on own line</option>
<option value="none">Attempt to keep braces where they are</option>
</select>
<p style="margin:6px 0 0 0">HTML &lt;style&gt;, &lt;script&gt; formatting:</p>
<select id="indent-scripts">
<option value="keep">Keep indent level of the tag</option>
<option value="normal">Add one indent level</option>
<option value="separate">Separate indentation</option>
</select>
<p style="margin:6px 0 0 0">Additional Settings (JSON):</p>
<textarea id="additional-options" rows="5" cols="32">{}</textarea>
<p id="additional-options-error" hidden style="margin:6px 0 0 0; color:red ">Could Not Parse JSON!</p>
</td>
<td>
<input class="checkbox" type="checkbox" id="end-with-newline">
<label for="end-with-newline">End script and style with newline?</label>
<br>
<input class="checkbox" type="checkbox" id="e4x">
<label for="e4x">Support e4x/jsx syntax</label>
<br>
<input class="checkbox" type="checkbox" id="comma-first">
<label for="comma-first">Use comma-first list style?</label>
<br>
<input class="checkbox" type="checkbox" id="detect-packers">
<label for="detect-packers">Detect packers and obfuscators?</label>
<br>
<input class="checkbox" type="checkbox" id="brace-preserve-inline">
<label for="brace-preserve-inline">Preserve inline braces/code blocks?</label>
<br>
<input class="checkbox" type="checkbox" id="keep-array-indentation">
<label for="keep-array-indentation">Keep array indentation?</label>
<br>
<input class="checkbox" type="checkbox" id="break-chained-methods">
<label for="break-chained-methods">Break lines on chained methods?</label>
<br>
<input class="checkbox" type="checkbox" id="space-before-conditional">
<label for="space-before-conditional">Space before conditional: "if(x)" / "if (x)"</label>
<br>
<input class="checkbox" type="checkbox" id="unescape-strings">
<label for="unescape-strings">Unescape printable chars encoded as \xNN or \uNNNN?</label>
<br>
<input class="checkbox" type="checkbox" id="jslint-happy">
<label for="jslint-happy">Use JSLint-happy formatting tweaks?</label>
<br>
<input class="checkbox" type="checkbox" id="indent-inner-html">
<label for="indent-inner-html">Indent &lt;head&gt; and &lt;body&gt; sections?</label>
<br><a href="?without-codemirror" class="turn-off-codemirror">Use a simple textarea for code input?</a>
</td>
</tr>
</table>
<div style="line-height: 0">
<select name="language" id="language">
<option value="auto">Auto Detect</option>
<option value="css">CSS</option>
<option value="html">HTML</option>
<option value="js">JavaScript</option>
</select>
<button class="submit"><strong>Beautify JavaScript or HTML</strong> <em>(ctrl-enter)</em>
</button>
<textarea id="source" rows="30" cols="160"></textarea>
<button class="submit"><strong>Beautify JavaScript or HTML</strong> <em>(ctrl-enter)</em>
</button>
</div>
<p style="margin:6px 0 0 0">Your Selected Options (JSON):</p>
<div style="line-height: 0">
<textarea readonly id="options-selected" rows="10" cols="40"></textarea>
</div>
<p id="open-issue" hidden>Not pretty enough for you?
<button type="button" onclick="submitIssue()" name="issue-button">Report an issue</button>
</p>
<div class="blurb">
<h2>Browser extensions and other uses</h2>
<div class="col-6">
<ul class="uses">
<li>A
<a href="javascript:(function(){s=document.getElementsByTagName('SCRIPT');tx='';sr=[];for(i=0;i<s.length;i++){with(s.item(i)){t=text;if(t){tx+=t;}else{sr.push(src)};}};with(window.open()){document.write('<textarea id=&quot;t&quot;>'+(sr.join(&quot;\n&quot;))+&quot;\n\n-----\n\n&quot;+tx+'</textarea><script src=&quot;https://beautifier.io/js/lib/beautify.js&quot;></script><script>with(document.getElementById(&quot;t&quot;)){value=js_beautify(value);with(style){width=&quot;99%&quot;;height=&quot;99%&quot;;borderStyle=&quot;none&quot;;}};</script>');document.close();}})();">
<strong>bookmarklet</strong></a>
(drag it to your bookmarks) by Ichiro Hiroshi to see all scripts used on the page,</li>
<li><strong>Chrome</strong>, in case the built-in CSS and javascript formatting isn't enough for you:<br>
<a href="https://chrome.google.com/webstore/detail/cfmcghennfbpmhemnnfjhkdmnbidpanb">Quick source viewer</a> by Tomi Mickelsson (<a href="https://github.com/tomimick/chrome-ext-view-src">github</a>, <a href="http://tomicloud.com/2012/07/viewsrc-chrome-ext">blog</a>),<br>
<a href="https://chrome.google.com/webstore/detail/javascript-and-css-code-b/iiglodndmmefofehaibmaignglbpdald">Javascript and CSS Code beautifier</a> by c7sky,<br>
<a href="https://chrome.google.com/webstore/detail/jsbeautify-for-google-chr/kkioiolcacgoihiiekambdciinadbpfk">jsbeautify-for-chrome</a> by Tom Rix (<a href="https://github.com/rixth/jsbeautify-for-chrome">github</a>),<br>
<a href="https://chrome.google.com/webstore/detail/piekbefgpgdecckjcpffhnacjflfoddg">Pretty Beautiful JavaScript</a> by Will McSweeney<br>
<a href="https://chrome.google.com/webstore/detail/stackoverflow-code-beauti/pljeafjjkkbacckkollfejkciddacmeb">Stackoverflow Code Beautify</a> by Making Odd Edit Studios (<a href="https://github.com/MakingOddEdit/CodeBeautify">github</a>).
</li>
<li><strong>Firefox</strong>: <a href="https://addons.mozilla.org/en-US/firefox/addon/javascript-deminifier/">Javascript deminifier</a> by Ben Murphy, to be
used together with the firebug (<a href="https://github.com/benmmurphy/jsdeminifier_xpi/">github</a>),</li>
<li><strong>Safari</strong>: <a href="http://spadin.github.com/js-beautify-safari-extension">Safari extension</a> by Sandro Padin,</li>
<li><strong>Opera</strong>: <a href="https://addons.opera.com/addons/extensions/details/readable-javascript/">Readable JavaScript</a>
(<a href="https://github.com/Dither/readable-javascript">github</a>) by Dither,</li>
<li><strong>Opera</strong>: <a href="https://addons.opera.com/addons/extensions/details/source/">Source</a> extension by Deathamns,</li>
<li><strong>Sublime Text 2/3:</strong> <a href="https://github.com/akalongman/sublimetext-codeformatter">CodeFormatter</a>, a python plugin by Avtandil Kikabidze, supports HTML, CSS, JS and a bunch of other languages,</li>
<li><strong>Sublime Text 2/3:</strong> <a href="https://github.com/victorporof/Sublime-HTMLPrettify">HTMLPrettify</a>, a javascript plugin by Victor Porof,</li>
<li><strong>Sublime Text 2:</strong> <a href="https://github.com/jdc0589/JsFormat">JsFormat</a>, a javascript formatting plugin for this nice editor by Davis
Clark,</li>
<li><strong>vim:</strong> <a href="https://github.com/michalliu/sourcebeautify.vim">sourcebeautify.vim</a>, a plugin by michalliu (requires node.js, V8, SpiderMonkey
or cscript js engine),</li>
<li><strong>vim:</strong> <a href="https://github.com/maksimr/vim-jsbeautify">vim-jsbeautify</a>, a plugin by Maksim Ryzhikov (node.js or V8 required),</li>
<li><strong>Emacs:</strong> <a href="https://github.com/yasuyk/web-beautify">Web-beautify</a> formatting package by Yasuyuki Oka,</li>
<li><strong>Komodo IDE:</strong> <a href="http://komodoide.com/packages/addons/beautify-js/">Beautify-js</a> addon by Bob de Haas (<a href="https://github.com/babobski/Beautify-js">github</a>),</li>
<li><strong>C#:</strong> ghost6991 <a href="https://github.com/ghost6991/Jsbeautifier">ported the javascript formatter to C#</a>,</li>
<li><strong>Go:</strong> ditashi has <a href="https://github.com/ditashi/jsbeautifier-go">ported the javascript formatter to golang</a>,</li>
</ul>
</div>
<div class="col-6">
<ul class="uses">
<li><a href="https://marketplace.visualstudio.com/items/HookyQR.beautify">Beautify plugin</a> (<a href="https://github.com/HookyQR/VSCodeBeautify">github</a>) by HookyQR for the <a href="https://code.visualstudio.com/">Visual Studio Code</a> IDE</a>,</li>
<li><a href="http://fiddler2.com/">Fiddler</a> proxy: <a href="http://fiddler2.com/Fiddler2/extensions.asp">JavaScript Formatter addon</a>,</li>
<li><a href="https://github.com/nagaozen/gedit-tunnings/">gEdit tips</a> by Fabio Nagao,</li>
<li><a href="http://akelpad.sourceforge.net/forum/viewtopic.php?p=11246#11246">Akelpad extension</a> by Infocatcher,</li>
<li>Beautifier in <a href="http://sethmason.com/2011/04/28/jsbeautify-in-emacs.html">Emacs</a> write-up by Seth Mason,</li>
<li><a href="http://c9.io">Cloud9</a>, a lovely IDE running in a browser, working in the node/cloud, uses jsbeautifier (<a href="https://github.com/ajaxorg/cloud9">github</a>),</li>
<li><a href="https://www.comment-devenir-un-hacker.com/app.html">Devenir Hacker App</a>, a non-free JavaScript packer for Mac,</li>
<li><a href="http://www.restconsole.com/">REST Console</a>, a request debugging tool for Chrome, beautifies JSON responses (<a href="https://github.com/codeinchaos/rest-console">github</a>),</li>
<li><a href="http://mitmproxy.org/">mitmproxy</a>, a nifty SSL-capable HTTP proxy, provides pretty javascript responses (<a href="https://github.com/cortesi/mitmproxy">github</a>).</li>
<li><a href="http://www.wakanda.org/">wakanda</a>, a neat IDE for web and mobile applications has a <a href="http://forum.wakanda.org/showthread.php?1483-3-new-extensions-JSLint-Beautifier-and-Snippet">Beautifier extension</a>
(<a href="https://github.com/Wakanda/wakanda-extensions/tree/master/Beautifier">github</a>).</li>
<li><a href="http://portswigger.net/burp/">Burp Suite</a> now has a <a href="https://github.com/irsdl/BurpSuiteJSBeautifier/">beautfier extension</a>,
thanks to Soroush Dalili,</li>
<li><a href="http://plugins.netbeans.org/plugin/43263/jsbeautify">Netbeans jsbeautify</a> plugin by Drew Hamlett
(<a href="https://github.com/drewhjava/netbeans-jsbeautify">github</a>).</li>
<li><a href="https://github.com/drewhjava/brackets-beautify">brackets-beautify-extension</a> for <a href="http://brackets.io">Adobe Brackets</a> by Drew
Hamlett (<a href="https://github.com/drewhjava/brackets-beautify">github</a>),</li>
<li><a href="http://codecaddy.net/">codecaddy.net</a>, a collection of webdev-related tools, assembled by Darik Hall,</li>
<li><a href="http://www.editey.com/">editey.com</a>, an interesting and free Google-Drive oriented editor uses this beautifier,</li>
<li><a href="https://github.com/vkadam/grunt-jsbeautifier">a beautifier plugin for Grunt</a> by Vishal Kadam,</li>
<li><a href="http://www.uvviewsoft.com/synwrite/">SynWrite</a> editor has a JsFormat plugin (<a href="https://sourceforge.net/projects/synwrite-addons/files/PyPlugins/Alexey.JsFormat/">rar</a>, <a href="http://synwrite.sourceforge.net/forums/viewtopic.php?f=19&t=865">readme</a>),</li>
<li><a href="http://liveditor.com/">LIVEditor</a>, a live-editing HTML/CSS/JS IDE (commercial, Windows-only) uses the library,</li>
</ul>
</div>
<p>Doing anything interesting? Write us to <b>team@beautifier.io</b> so we can add your project to the list.</p>
<p class="contributor-sep">Written by <a href="https://github.com/einars">Einar Lielmanis</a>, maintained and evolved by <a href="https://github.com/bitwiseman/">Liam Newman</a>.</p>
<p>We use the wonderful <a href="http://codemirror.net">CodeMirror</a> syntax highlighting editor, written by Marijn Haverbeke.
</p>
<p class="contributors">Made with a great help of Jason&nbsp;Diamond, Patrick&nbsp;Hof, Nochum&nbsp;Sossonko, Andreas&nbsp;Schneider,
<br>Dave&nbsp;Vasilevsky,
<a href="https://moikrug.ru/vital">Vital&nbsp;Batmanov</a>, Ron&nbsp;Baldwin, Gabriel&nbsp;Harrison,
<a href="http://shullian.com">Chris J. Shull</a>,
<a href="http://mathiasbynens.be/">Mathias Bynens</a>,
<br>
<a href="https://www.vittgam.net/">Vittorio Gambaletta</a>,
<a href="https://github.com/esseks">Stefano Sanfilippo</a> and
<a href="https://github.com/evocateur">Daniel Stockman</a>.
</p>
<div class="blurb">
<p style="text-align:right">
<a href="#" style="color: #ccc; border-bottom: 1px dashed #ccc; text-decoration: none;" onclick="run_tests(); return false;">Run the tests</a>
</p>
<h2>Browser extensions and other uses</h2>
<div class="col-6">
<ul class="uses">
<li>A <a href="javascript:(function(){s=document.getElementsByTagName('SCRIPT');tx='';sr=[];for(i=0;i<s.length;i++){with(s.item(i)){t=text;if(t){tx+=t;}else{sr.push(src)};}};with(window.open()){document.write('<textarea id=&quot;t&quot;>'+(sr.join(&quot;\n&quot;))+&quot;\n\n-----\n\n&quot;+tx+'</textarea><script src=&quot;http://jsbeautifier.org/beautify.js&quot;></script><script>with(document.getElementById(&quot;t&quot;)){value=js_beautify(value);with(style){width=&quot;99%&quot;;height=&quot;99%&quot;;borderStyle=&quot;none&quot;;}};</script>');document.close();}})();"><strong>bookmarklet</strong></a> (drag
it to your bookmarks) by Ichiro Hiroshi to see all scripts used on the page,</li>
<li><strong>Chrome</strong>, in case the built-in CSS and javascript formatting isn't enough for you:<br>
<a href="https://chrome.google.com/webstore/detail/cfmcghennfbpmhemnnfjhkdmnbidpanb">Quick source viewer</a> by Tomi Mickelsson (<a href="https://github.com/tomimick/chrome-ext-view-src">github</a>, <a href="http://tomicloud.com/2012/07/viewsrc-chrome-ext">blog</a>),<br>
<a href="https://chrome.google.com/webstore/detail/javascript-and-css-code-b/iiglodndmmefofehaibmaignglbpdald">Javascript and CSS Code beautifier</a> by c7sky,<br>
<a href="https://chrome.google.com/webstore/detail/jsbeautify-for-google-chr/kkioiolcacgoihiiekambdciinadbpfk">jsbeautify-for-chrome</a> by Tom Rix (<a href="https://github.com/rixth/jsbeautify-for-chrome">github</a>),<br>
<a href="https://chrome.google.com/webstore/detail/piekbefgpgdecckjcpffhnacjflfoddg">Pretty Beautiful JavaScript</a> by Will McSweeney<br>
<a href="https://chrome.google.com/webstore/detail/stackoverflow-code-beauti/pljeafjjkkbacckkollfejkciddacmeb">Stackoverflow Code Beautify</a> by Making Odd Edit Studios (<a href="https://github.com/MakingOddEdit/CodeBeautify">github</a>).
</li>
<li><strong>Firefox</strong>: <a href="https://addons.mozilla.org/en-US/firefox/addon/javascript-deminifier/">Javascript deminifier</a> by Ben Murphy, to be
used together with the firebug (<a href="https://github.com/benmmurphy/jsdeminifier_xpi/">github</a>),</li>
<li><strong>Safari</strong>: <a href="http://spadin.github.com/js-beautify-safari-extension">Safari extension</a> by Sandro Padin,</li>
<li><strong>Opera</strong>: <a href="https://addons.opera.com/addons/extensions/details/readable-javascript/">Readable JavaScript</a>
(<a href="https://github.com/Dither/readable-javascript">github</a>) by Dither,</li>
<li><strong>Opera</strong>: <a href="https://addons.opera.com/addons/extensions/details/source/">Source</a> extension by Deathamns,</li>
<li><strong>Sublime Text 2/3:</strong> <a href="https://github.com/akalongman/sublimetext-codeformatter">CodeFormatter</a>, a python plugin by Avtandil Kikabidze, supports HTML, CSS, JS and a bunch of other languages,</li>
<li><strong>Sublime Text 2/3:</strong> <a href="https://github.com/victorporof/Sublime-HTMLPrettify">HTMLPrettify</a>, a javascript plugin by Victor Porof,</li>
<li><strong>Sublime Text 2:</strong> <a href="https://github.com/jdc0589/JsFormat">JsFormat</a>, a javascript formatting plugin for this nice editor by Davis
Clark,</li>
<li><strong>vim:</strong> <a href="https://github.com/michalliu/sourcebeautify.vim">sourcebeautify.vim</a>, a plugin by michalliu (requires node.js, V8, SpiderMonkey
or cscript js engine),</li>
<li><strong>vim:</strong> <a href="https://github.com/maksimr/vim-jsbeautify">vim-jsbeautify</a>, a plugin by Maksim Ryzhikov (node.js or V8 required),</li>
<li><strong>Emacs:</strong> <a href="https://github.com/yasuyk/web-beautify">Web-beautify</a> formatting package by Yasuyuki Oka,</li>
<li><strong>Komodo IDE:</strong> <a href="http://komodoide.com/packages/addons/beautify-js/">Beautify-js</a> addon by Bob de Haas (<a href="https://github.com/babobski/Beautify-js">github</a>),</li>
<li><strong>C#:</strong> ghost6991 <a href="https://github.com/ghost6991/Jsbeautifier">ported the javascript formatter to C#</a>,
<li><strong>Go:</strong> ditashi has <a href="https://github.com/ditashi/jsbeautifier-go">ported the javascript formatter to golang</a>,
</ul>
</div>
<div class="col-6">
<ul class="uses">
<li><a href="https://marketplace.visualstudio.com/items/HookyQR.beautify">Beautify plugin</a> (<a href="https://github.com/HookyQR/VSCodeBeautify">github</a>) by HookyQR for the <a href="https://code.visualstudio.com/">Visual Studio Code</a> IDE</a>,
<li><a href="http://fiddler2.com/">Fiddler</a> proxy: <a href="http://fiddler2.com/Fiddler2/extensions.asp">JavaScript Formatter addon</a>,
<li><a href="https://github.com/nagaozen/gedit-tunnings/">gEdit tips</a> by Fabio Nagao,</li>
<li><a href="http://akelpad.sourceforge.net/forum/viewtopic.php?p=11246#11246">Akelpad extension</a> by Infocatcher,</li>
<li>Beautifier in <a href="http://sethmason.com/2011/04/28/jsbeautify-in-emacs.html">Emacs</a> write-up by Seth Mason,</li>
<li><a href="http://c9.io">Cloud9</a>, a lovely IDE running in a browser, working in the node/cloud, uses jsbeautifier (<a href="https://github.com/ajaxorg/cloud9">github</a>),</li>
<li><a href="https://www.comment-devenir-un-hacker.com/app.html">Devenir Hacker App</a>, a non-free JavaScript packer for Mac,</li>
<li><a href="http://www.restconsole.com/">REST Console</a>, a request debugging tool for Chrome, beautifies JSON responses (<a href="https://github.com/codeinchaos/rest-console">github</a>),</li>
<li><a href="http://mitmproxy.org/">mitmproxy</a>, a nifty SSL-capable HTTP proxy, provides pretty javascript responses (<a href="https://github.com/cortesi/mitmproxy">github</a>).</li>
<li><a href="http://www.wakanda.org/">wakanda</a>, a neat IDE for web and mobile applications has a <a href="http://forum.wakanda.org/showthread.php?1483-3-new-extensions-JSLint-Beautifier-and-Snippet">Beautifier extension</a>
(<a href="https://github.com/Wakanda/wakanda-extensions/tree/master/Beautifier">github</a>).</li>
<li><a href="http://portswigger.net/burp/">Burp Suite</a> now has a <a href="https://github.com/irsdl/BurpSuiteJSBeautifier/">beautfier extension</a>,
thanks to Soroush Dalili,</li>
<li><a href="http://plugins.netbeans.org/plugin/43263/jsbeautify">Netbeans jsbeautify</a> plugin by Drew Hamlett
(<a href="https://github.com/drewhjava/netbeans-jsbeautify">github</a>).</li>
<li><a href="https://github.com/drewhjava/brackets-beautify">brackets-beautify-extension</a> for <a href="http://brackets.io">Adobe Brackets</a> by Drew
Hamlett (<a href="https://github.com/drewhjava/brackets-beautify">github</a>),</li>
<li><a href="http://codecaddy.net/">codecaddy.net</a>, a collection of webdev-related tools, assembled by Darik Hall,
<li><a href="http://www.editey.com/">editey.com</a>, an interesting and free Google-Drive oriented editor uses this beautifier,
<li><a href="https://github.com/vkadam/grunt-jsbeautifier">a beautifier plugin for Grunt</a> by Vishal Kadam,
<li><a href="http://www.uvviewsoft.com/synwrite/">SynWrite</a> editor has a JsFormat plugin (<a href="https://sourceforge.net/projects/synwrite-addons/files/PyPlugins/Alexey.JsFormat/">rar</a>, <a href="http://synwrite.sourceforge.net/forums/viewtopic.php?f=19&t=865">readme</a>),
<li><a href="http://liveditor.com/">LIVEditor</a>, a live-editing HTML/CSS/JS IDE (commercial, Windows-only) uses the library,
</ul>
</div>
<p>Doing anything interesting? Write us to <b>team@jsbeautifier.org</b> so we can add your project to the list.</p>
<p class="contributor-sep">Written by <a href="https://github.com/einars">Einar Lielmanis</a>, maintained and evolved by <a href="https://github.com/bitwiseman/">Liam Newman</a>.</p>
<p>We use the wonderful <a href="http://codemirror.net">CodeMirror</a> syntax highlighting editor, written by Marijn Haverbeke.
</p>
<p class="contributors">Made with a great help of Jason&nbsp;Diamond, Patrick&nbsp;Hof, Nochum&nbsp;Sossonko, Andreas&nbsp;Schneider,
<br>Dave&nbsp;Vasilevsky,
<a href="https://moikrug.ru/vital">Vital&nbsp;Batmanov</a>, Ron&nbsp;Baldwin, Gabriel&nbsp;Harrison,
<a href="http://shullian.com">Chris J. Shull</a>,
<a href="http://mathiasbynens.be/">Mathias Bynens</a>,
<br>
<a href="https://www.vittgam.net/">Vittorio Gambaletta</a>,
<a href="https://github.com/esseks">Stefano Sanfilippo</a> and
<a href="https://github.com/evocateur">Daniel Stockman</a>.
</p>
<p style="text-align:right">
<a href="#" style="color: #ccc; border-bottom: 1px dashed #ccc; text-decoration: none;" onclick="run_tests(); return false;">Run the tests</a>
</p>
</div>
<div id="testresults"></div>
<script src="web/onload.js"></script>
<script src="web/google-analytics.js"></script>
</div>
<div id="testresults"></div>
<script src="web/onload.js">
</script>
<script src="web/google-analytics.js">
</script>
</body>
</html>
</html>

View File

@ -1,3 +1,5 @@
/*jshint node:true */
/* globals define */
/*
The MIT License (MIT)
@ -25,6 +27,8 @@
*/
'use strict';
/**
The following batches are equivalent:

View File

@ -31,6 +31,7 @@
Written by Daniel Stockman (daniel.stockman@gmail.com)
*/
/*jshint strict:false */
var debug = process.env.DEBUG_JSBEAUTIFY || process.env.JSBEAUTIFY_DEBUG ? function() {
console.error.apply(console, arguments);
@ -77,13 +78,14 @@ var path = require('path'),
"space_in_empty_paren": Boolean,
"jslint_happy": Boolean,
"space_after_anon_function": Boolean,
"space_after_named_function": Boolean,
"brace_style": "brace_style", //See above for validation
"unindent_chained_methods": Boolean,
"break_chained_methods": Boolean,
"keep_array_indentation": Boolean,
"unescape_strings": Boolean,
"wrap_line_length": Number,
"wrap_attributes": ["auto", "force", "force-aligned"],
"wrap_attributes": ["auto", "force", "force-aligned", "force-expand-multiline", "aligned-multiple"],
"wrap_attributes_indent_size": Number,
"e4x": Boolean,
"end_with_newline": Boolean,
@ -353,6 +355,7 @@ function usage(err) {
msg.push(' -E, --space-in-empty-paren Add a single space inside empty paren, ie. f( )');
msg.push(' -j, --jslint-happy Enable jslint-stricter mode');
msg.push(' -a, --space-after-anon-function Add a space before an anonymous function\'s parens, ie. function ()');
msg.push(' --space_after_named_function Add a space before a named function\'s parens, ie. function example ()');
msg.push(' -b, --brace-style [collapse|expand|end-expand|none][,preserve-inline] [collapse,preserve-inline]');
msg.push(' -u, --unindent-chained-methods Don\'t indent chained method calls');
msg.push(' -B, --break-chained-methods Break chained method calls across subsequent lines');

View File

@ -1,60 +1,61 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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:
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 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.
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.
*/
'use strict';
function Directives(start_block_pattern, end_block_pattern) {
start_block_pattern = typeof start_block_pattern === 'string' ? start_block_pattern : start_block_pattern.source;
end_block_pattern = typeof end_block_pattern === 'string' ? end_block_pattern : end_block_pattern.source;
this._directives_block_pattern = new RegExp(start_block_pattern + / beautify( \w+[:]\w+)+ /.source + end_block_pattern, 'g');
this._directive_pattern = / (\w+)[:](\w+)/g;
this.__directives_block_pattern = new RegExp(start_block_pattern + / beautify( \w+[:]\w+)+ /.source + end_block_pattern, 'g');
this.__directive_pattern = / (\w+)[:](\w+)/g;
this._directives_end_ignore_pattern = new RegExp('(?:[\\s\\S]*?)((?:' + start_block_pattern + /\sbeautify\signore:end\s/.source + end_block_pattern + ')|$)', 'g');
this.__directives_end_ignore_pattern = new RegExp('(?:[\\s\\S]*?)((?:' + start_block_pattern + /\sbeautify\signore:end\s/.source + end_block_pattern + ')|$)', 'g');
}
Directives.prototype.get_directives = function(text) {
if (!text.match(this._directives_block_pattern)) {
if (!text.match(this.__directives_block_pattern)) {
return null;
}
var directives = {};
this._directive_pattern.lastIndex = 0;
var directive_match = this._directive_pattern.exec(text);
this.__directive_pattern.lastIndex = 0;
var directive_match = this.__directive_pattern.exec(text);
while (directive_match) {
directives[directive_match[1]] = directive_match[2];
directive_match = this._directive_pattern.exec(text);
directive_match = this.__directive_pattern.exec(text);
}
return directives;
};
Directives.prototype.readIgnored = function(input) {
return input.read(this._directives_end_ignore_pattern);
return input.read(this.__directives_end_ignore_pattern);
};

View File

@ -1,4 +1,4 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
@ -26,31 +26,33 @@
SOFTWARE.
*/
'use strict';
function InputScanner(input_string) {
this._input = input_string || '';
this._input_length = this._input.length;
this._position = 0;
this.__input = input_string || '';
this.__input_length = this.__input.length;
this.__position = 0;
}
InputScanner.prototype.restart = function() {
this._position = 0;
this.__position = 0;
};
InputScanner.prototype.back = function() {
if (this._position > 0) {
this._position -= 1;
if (this.__position > 0) {
this.__position -= 1;
}
};
InputScanner.prototype.hasNext = function() {
return this._position < this._input_length;
return this.__position < this.__input_length;
};
InputScanner.prototype.next = function() {
var val = null;
if (this.hasNext()) {
val = this._input.charAt(this._position);
this._position += 1;
val = this.__input.charAt(this.__position);
this.__position += 1;
}
return val;
};
@ -58,20 +60,20 @@ InputScanner.prototype.next = function() {
InputScanner.prototype.peek = function(index) {
var val = null;
index = index || 0;
index += this._position;
if (index >= 0 && index < this._input_length) {
val = this._input.charAt(index);
index += this.__position;
if (index >= 0 && index < this.__input_length) {
val = this.__input.charAt(index);
}
return val;
};
InputScanner.prototype.test = function(pattern, index) {
index = index || 0;
index += this._position;
index += this.__position;
pattern.lastIndex = index;
if (index >= 0 && index < this._input_length) {
var pattern_match = pattern.exec(this._input);
if (index >= 0 && index < this.__input_length) {
var pattern_match = pattern.exec(this.__input);
return pattern_match && pattern_match.index === index;
} else {
return false;
@ -85,10 +87,10 @@ InputScanner.prototype.testChar = function(pattern, index) {
};
InputScanner.prototype.match = function(pattern) {
pattern.lastIndex = this._position;
var pattern_match = pattern.exec(this._input);
if (pattern_match && pattern_match.index === this._position) {
this._position += pattern_match[0].length;
pattern.lastIndex = this.__position;
var pattern_match = pattern.exec(this.__input);
if (pattern_match && pattern_match.index === this.__position) {
this.__position += pattern_match[0].length;
} else {
pattern_match = null;
}
@ -106,9 +108,9 @@ InputScanner.prototype.read = function(pattern) {
InputScanner.prototype.readUntil = function(pattern, include_match) {
var val = '';
var match_index = this._position;
pattern.lastIndex = this._position;
var pattern_match = pattern.exec(this._input);
var match_index = this.__position;
pattern.lastIndex = this.__position;
var pattern_match = pattern.exec(this.__input);
if (pattern_match) {
if (include_match) {
match_index = pattern_match.index + pattern_match[0].length;
@ -116,11 +118,11 @@ InputScanner.prototype.readUntil = function(pattern, include_match) {
match_index = pattern_match.index;
}
} else {
match_index = this._input_length;
match_index = this.__input_length;
}
val = this._input.substring(this._position, match_index);
this._position = match_index;
val = this.__input.substring(this.__position, match_index);
this.__position = match_index;
return val;
};
@ -130,15 +132,15 @@ InputScanner.prototype.readUntilAfter = function(pattern) {
/* css beautifier legacy helpers */
InputScanner.prototype.peekUntilAfter = function(pattern) {
var start = this._position;
var start = this.__position;
var val = this.readUntilAfter(pattern);
this._position = start;
this.__position = start;
return val;
};
InputScanner.prototype.lookBack = function(testVal) {
var start = this._position - 1;
return start >= testVal.length && this._input.substring(start - testVal.length, start)
var start = this.__position - 1;
return start >= testVal.length && this.__input.substring(start - testVal.length, start)
.toLowerCase() === testVal;
};

View File

@ -1,38 +1,150 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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:
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 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.
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.
*/
'use strict';
function Options(options, merge_child_field) {
options = _mergeOpts(options, merge_child_field);
this.raw_options = _normalizeOpts(options);
// Support passing the source text back with no change
this.disabled = this._get_boolean('disabled');
this.eol = this._get_characters('eol', 'auto');
this.end_with_newline = this._get_boolean('end_with_newline');
this.indent_size = this._get_number('indent_size', 4);
this.indent_char = this._get_characters('indent_char', ' ');
this.indent_level = this._get_number('indent_level');
this.preserve_newlines = this._get_boolean('preserve_newlines', true);
this.max_preserve_newlines = this._get_number('max_preserve_newlines', 32786);
if (!this.preserve_newlines) {
this.max_preserve_newlines = 0;
}
this.indent_with_tabs = this._get_boolean('indent_with_tabs');
if (this.indent_with_tabs) {
this.indent_char = '\t';
this.indent_size = 1;
}
// Backwards compat with 1.3.x
this.wrap_line_length = this._get_number('wrap_line_length', this._get_number('max_char'));
}
Options.prototype._get_array = function(name, default_value) {
var option_value = this.raw_options[name];
var result = default_value || [];
if (typeof option_value === 'object') {
if (option_value !== null && typeof option_value.concat === 'function') {
result = option_value.concat();
}
} else if (typeof option_value === 'string') {
result = option_value.split(/[^a-zA-Z0-9_\/\-]+/);
}
return result;
};
Options.prototype._get_boolean = function(name, default_value) {
var option_value = this.raw_options[name];
var result = option_value === undefined ? !!default_value : !!option_value;
return result;
};
Options.prototype._get_characters = function(name, default_value) {
var option_value = this.raw_options[name];
var result = default_value || '';
if (typeof option_value === 'string') {
result = option_value.replace(/\\r/, '\r').replace(/\\n/, '\n').replace(/\\t/, '\t');
}
return result;
};
Options.prototype._get_number = function(name, default_value) {
var option_value = this.raw_options[name];
default_value = parseInt(default_value, 10);
if (isNaN(default_value)) {
default_value = 0;
}
var result = parseInt(option_value, 10);
if (isNaN(result)) {
result = default_value;
}
return result;
};
Options.prototype._get_selection = function(name, selection_list, default_value) {
var result = this._get_selection_list(name, selection_list, default_value);
if (result.length !== 1) {
throw new Error(
"Invalid Option Value: The option '" + name + "' can only be one of the following values:\n" +
selection_list + "\nYou passed in: '" + this.raw_options[name] + "'");
}
return result[0];
};
Options.prototype._get_selection_list = function(name, selection_list, default_value) {
if (!selection_list || selection_list.length === 0) {
throw new Error("Selection list cannot be empty.");
}
default_value = default_value || [selection_list[0]];
if (!this._is_valid_selection(default_value, selection_list)) {
throw new Error("Invalid Default Value!");
}
var result = this._get_array(name, default_value);
if (!this._is_valid_selection(result, selection_list)) {
throw new Error(
"Invalid Option Value: The option '" + name + "' can contain only the following values:\n" +
selection_list + "\nYou passed in: '" + this.raw_options[name] + "'");
}
return result;
};
Options.prototype._is_valid_selection = function(result, selection_list) {
return result.length && selection_list.length &&
!result.some(function(item) { return selection_list.indexOf(item) === -1; });
};
// merges child options up with the parent options object
// Example: obj = {a: 1, b: {a: 2}}
// mergeOpts(obj, 'b')
//
// Returns: {a: 2, b: {a: 2}}
function mergeOpts(allOptions, childFieldName) {
function _mergeOpts(allOptions, childFieldName) {
var finalOpts = {};
allOptions = allOptions || {};
var name;
for (name in allOptions) {
@ -42,7 +154,7 @@ function mergeOpts(allOptions, childFieldName) {
}
//merge in the per type settings for the childFieldName
if (childFieldName in allOptions) {
if (childFieldName && allOptions[childFieldName]) {
for (name in allOptions[childFieldName]) {
finalOpts[name] = allOptions[childFieldName][name];
}
@ -50,4 +162,17 @@ function mergeOpts(allOptions, childFieldName) {
return finalOpts;
}
module.exports.mergeOpts = mergeOpts;
function _normalizeOpts(options) {
var convertedOpts = {};
var key;
for (key in options) {
var newKey = key.replace(/-/g, "_");
convertedOpts[newKey] = options[key];
}
return convertedOpts;
}
module.exports.Options = Options;
module.exports.normalizeOpts = _normalizeOpts;
module.exports.mergeOpts = _mergeOpts;

View File

@ -1,4 +1,4 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
@ -26,123 +26,193 @@
SOFTWARE.
*/
function OutputLine(parent) {
this._parent = parent;
this._character_count = 0;
// use indent_count as a marker for this._lines that have preserved indentation
this._indent_count = -1;
'use strict';
this._items = [];
function OutputLine(parent) {
this.__parent = parent;
this.__character_count = 0;
// use indent_count as a marker for this.__lines that have preserved indentation
this.__indent_count = -1;
this.__alignment_count = 0;
this.__items = [];
}
OutputLine.prototype.set_indent = function(level) {
this._character_count = this._parent.baseIndentLength + level * this._parent.indent_length;
this._indent_count = level;
OutputLine.prototype.item = function(index) {
if (index < 0) {
return this.__items[this.__items.length + index];
} else {
return this.__items[index];
}
};
OutputLine.prototype.has_match = function(pattern) {
for (var lastCheckedOutput = this.__items.length - 1; lastCheckedOutput >= 0; lastCheckedOutput--) {
if (this.__items[lastCheckedOutput].match(pattern)) {
return true;
}
}
return false;
};
OutputLine.prototype.set_indent = function(indent, alignment) {
this.__indent_count = indent || 0;
this.__alignment_count = alignment || 0;
this.__character_count = this.__parent.baseIndentLength + this.__alignment_count + this.__indent_count * this.__parent.indent_length;
};
OutputLine.prototype.get_character_count = function() {
return this._character_count;
return this.__character_count;
};
OutputLine.prototype.is_empty = function() {
return this._items.length === 0;
return this.__items.length === 0;
};
OutputLine.prototype.last = function() {
if (!this.is_empty()) {
return this._items[this._items.length - 1];
return this.__items[this.__items.length - 1];
} else {
return null;
}
};
OutputLine.prototype.push = function(item) {
this._items.push(item);
this._character_count += item.length;
this.__items.push(item);
this.__character_count += item.length;
};
OutputLine.prototype.push_raw = function(item) {
this.push(item);
var last_newline_index = item.lastIndexOf('\n');
if (last_newline_index !== -1) {
this.__character_count = item.length - last_newline_index;
}
};
OutputLine.prototype.pop = function() {
var item = null;
if (!this.is_empty()) {
item = this._items.pop();
this._character_count -= item.length;
item = this.__items.pop();
this.__character_count -= item.length;
}
return item;
};
OutputLine.prototype.remove_indent = function() {
if (this._indent_count > 0) {
this._indent_count -= 1;
this._character_count -= this._parent.indent_length;
if (this.__indent_count > 0) {
this.__indent_count -= 1;
this.__character_count -= this.__parent.indent_length;
}
};
OutputLine.prototype.trim = function() {
while (this.last() === ' ') {
this._items.pop();
this._character_count -= 1;
this.__items.pop();
this.__character_count -= 1;
}
};
OutputLine.prototype.toString = function() {
var result = '';
if (!this.is_empty()) {
if (this._indent_count >= 0) {
result = this._parent.indent_cache[this._indent_count];
if (this.__indent_count >= 0) {
result = this.__parent.get_indent_string(this.__indent_count);
}
result += this._items.join('');
if (this.__alignment_count >= 0) {
result += this.__parent.get_alignment_string(this.__alignment_count);
}
result += this.__items.join('');
}
return result;
};
function IndentCache(base_string, level_string) {
this.__cache = [base_string];
this.__level_string = level_string;
}
function Output(indent_string, baseIndentString) {
IndentCache.prototype.__ensure_cache = function(level) {
while (level >= this.__cache.length) {
this.__cache.push(this.__cache[this.__cache.length - 1] + this.__level_string);
}
};
IndentCache.prototype.get_level_string = function(level) {
this.__ensure_cache(level);
return this.__cache[level];
};
function Output(options, baseIndentString) {
var indent_string = options.indent_char;
if (options.indent_size > 1) {
indent_string = new Array(options.indent_size + 1).join(options.indent_char);
}
// Set to null to continue support for auto detection of base indent level.
baseIndentString = baseIndentString || '';
this.indent_cache = [baseIndentString];
if (options.indent_level > 0) {
baseIndentString = new Array(options.indent_level + 1).join(indent_string);
}
this.__indent_cache = new IndentCache(baseIndentString, indent_string);
this.__alignment_cache = new IndentCache('', ' ');
this.baseIndentLength = baseIndentString.length;
this.indent_length = indent_string.length;
this.raw = false;
this._end_with_newline = options.end_with_newline;
this._lines = [];
this.baseIndentString = baseIndentString;
this.indent_string = indent_string;
this.__lines = [];
this.previous_line = null;
this.current_line = null;
this.space_before_token = false;
// initialize
this.add_outputline();
this.__add_outputline();
}
Output.prototype.add_outputline = function() {
Output.prototype.__add_outputline = function() {
this.previous_line = this.current_line;
this.current_line = new OutputLine(this);
this._lines.push(this.current_line);
this.__lines.push(this.current_line);
};
Output.prototype.get_line_number = function() {
return this._lines.length;
return this.__lines.length;
};
Output.prototype.get_indent_string = function(level) {
return this.__indent_cache.get_level_string(level);
};
Output.prototype.get_alignment_string = function(level) {
return this.__alignment_cache.get_level_string(level);
};
Output.prototype.is_empty = function() {
return !this.previous_line && this.current_line.is_empty();
};
// Using object instead of string to allow for later expansion of info about each line
Output.prototype.add_new_line = function(force_newline) {
if (this.get_line_number() === 1 && this.just_added_newline()) {
return false; // no newline on start of file
// never newline at the start of file
// otherwise, newline only if we didn't just add one or we're forced
if (this.is_empty() ||
(!force_newline && this.just_added_newline())) {
return false;
}
if (force_newline || !this.just_added_newline()) {
if (!this.raw) {
this.add_outputline();
}
return true;
// if raw output is enabled, don't print additional newlines,
// but still return True as though you had
if (!this.raw) {
this.__add_outputline();
}
return false;
return true;
};
Output.prototype.get_code = function(end_with_newline, eol) {
var sweet_code = this._lines.join('\n').replace(/[\r\n\t ]+$/, '');
Output.prototype.get_code = function(eol) {
var sweet_code = this.__lines.join('\n').replace(/[\r\n\t ]+$/, '');
if (end_with_newline) {
if (this._end_with_newline) {
sweet_code += '\n';
}
@ -153,26 +223,25 @@ Output.prototype.get_code = function(end_with_newline, eol) {
return sweet_code;
};
Output.prototype.set_indent = function(level) {
// Never indent your first output indent at the start of the file
if (this._lines.length > 1) {
while (level >= this.indent_cache.length) {
this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
}
Output.prototype.set_indent = function(indent, alignment) {
indent = indent || 0;
alignment = alignment || 0;
this.current_line.set_indent(level);
// Never indent your first output indent at the start of the file
if (this.__lines.length > 1) {
this.current_line.set_indent(indent, alignment);
return true;
}
this.current_line.set_indent(0);
this.current_line.set_indent();
return false;
};
Output.prototype.add_raw_token = function(token) {
for (var x = 0; x < token.newlines; x++) {
this.add_outputline();
this.__add_outputline();
}
this.current_line.push(token.whitespace_before);
this.current_line.push(token.text);
this.current_line.push_raw(token.text);
this.space_before_token = false;
};
@ -189,9 +258,9 @@ Output.prototype.add_space_before_token = function() {
};
Output.prototype.remove_indent = function(index) {
var output_length = this._lines.length;
var output_length = this.__lines.length;
while (index < output_length) {
this._lines[index].remove_indent();
this.__lines[index].remove_indent();
index++;
}
};
@ -201,14 +270,15 @@ Output.prototype.trim = function(eat_newlines) {
this.current_line.trim(this.indent_string, this.baseIndentString);
while (eat_newlines && this._lines.length > 1 &&
while (eat_newlines && this.__lines.length > 1 &&
this.current_line.is_empty()) {
this._lines.pop();
this.current_line = this._lines[this._lines.length - 1];
this.__lines.pop();
this.current_line = this.__lines[this.__lines.length - 1];
this.current_line.trim();
}
this.previous_line = this._lines.length > 1 ? this._lines[this._lines.length - 2] : null;
this.previous_line = this.__lines.length > 1 ?
this.__lines[this.__lines.length - 2] : null;
};
Output.prototype.just_added_newline = function() {
@ -216,16 +286,24 @@ Output.prototype.just_added_newline = function() {
};
Output.prototype.just_added_blankline = function() {
if (this.just_added_newline()) {
if (this._lines.length === 1) {
return true; // start of the file and newline = blank
}
var line = this._lines[this._lines.length - 2];
return line.is_empty();
}
return false;
return this.is_empty() ||
(this.current_line.is_empty() && this.previous_line.is_empty());
};
Output.prototype.ensure_empty_line_above = function(starts_with, ends_with) {
var index = this.__lines.length - 2;
while (index >= 0) {
var potentialEmptyLine = this.__lines[index];
if (potentialEmptyLine.is_empty()) {
break;
} else if (potentialEmptyLine.item(0).indexOf(starts_with) !== 0 &&
potentialEmptyLine.item(-1) !== ends_with) {
this.__lines.splice(index + 1, 0, new OutputLine(this));
this.previous_line = this.__lines[this.__lines.length - 2];
break;
}
index--;
}
};
module.exports.Output = Output;

View File

@ -1,4 +1,4 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
@ -26,6 +26,8 @@
SOFTWARE.
*/
'use strict';
function Token(type, text, newlines, whitespace_before) {
this.type = type;
this.text = text;
@ -41,7 +43,10 @@ function Token(type, text, newlines, whitespace_before) {
this.newlines = newlines || 0;
this.whitespace_before = whitespace_before || '';
this.parent = null;
this.next = null;
this.previous = null;
this.opened = null;
this.closed = null;
this.directives = null;
}

View File

@ -1,31 +1,33 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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:
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 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.
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.
*/
'use strict';
var InputScanner = require('../core/inputscanner').InputScanner;
var Token = require('../core/token').Token;
var TokenStream = require('../core/tokenstream').TokenStream;
@ -36,33 +38,34 @@ var TOKEN = {
EOF: 'TK_EOF'
};
var Tokenizer = function(input_string) { // jshint unused:false
var Tokenizer = function(input_string, options) {
this._input = new InputScanner(input_string);
this._tokens = null;
this._newline_count = 0;
this._whitespace_before_token = '';
this._options = options || {};
this.__tokens = null;
this.__newline_count = 0;
this.__whitespace_before_token = '';
this._whitespace_pattern = /[\n\r\u2028\u2029\t ]+/g;
this._newline_pattern = /([\t ]*)(\r\n|[\n\r\u2028\u2029])?/g;
this._whitespace_pattern = /[\n\r\t ]+/g;
this._newline_pattern = /([^\n\r]*)(\r\n|[\n\r])?/g;
};
Tokenizer.prototype.tokenize = function() {
this._input.restart();
this._tokens = new TokenStream();
this.__tokens = new TokenStream();
this.reset();
this._reset();
var current;
var last = new Token(TOKEN.START, '');
var previous = new Token(TOKEN.START, '');
var open_token = null;
var open_stack = [];
var comments = new TokenStream();
while (last.type !== TOKEN.EOF) {
current = this.get_next_token(last);
while (this.is_comment(current)) {
while (previous.type !== TOKEN.EOF) {
current = this._get_next_token(previous, open_token);
while (this._is_comment(current)) {
comments.add(current);
current = this.get_next_token(last);
current = this._get_next_token(previous, open_token);
}
if (!comments.isEmpty()) {
@ -70,71 +73,76 @@ Tokenizer.prototype.tokenize = function() {
comments = new TokenStream();
}
if (this.is_opening(current)) {
current.parent = last;
current.parent = open_token;
if (this._is_opening(current)) {
open_stack.push(open_token);
open_token = current;
} else if (open_token && this.is_closing(current, open_token)) {
current.parent = open_token.parent;
} else if (open_token && this._is_closing(current, open_token)) {
current.opened = open_token;
open_token.closed = current;
open_token = open_stack.pop();
current.parent = open_token;
}
this._tokens.add(current);
last = current;
current.previous = previous;
previous.next = current;
this.__tokens.add(current);
previous = current;
}
return this._tokens;
return this.__tokens;
};
Tokenizer.prototype.reset = function() {};
Tokenizer.prototype._is_first_token = function() {
return this.__tokens.isEmpty();
};
Tokenizer.prototype.get_next_token = function(last_token) { // jshint unused:false
this.readWhitespace();
Tokenizer.prototype._reset = function() {};
Tokenizer.prototype._get_next_token = function(previous_token, open_token) { // jshint unused:false
this._readWhitespace();
var resulting_string = this._input.read(/.+/g);
if (resulting_string) {
return this.create_token(TOKEN.RAW, resulting_string);
return this._create_token(TOKEN.RAW, resulting_string);
} else {
return this.create_token(TOKEN.EOF, '');
return this._create_token(TOKEN.EOF, '');
}
};
Tokenizer.prototype.is_comment = function(current_token) { // jshint unused:false
Tokenizer.prototype._is_comment = function(current_token) { // jshint unused:false
return false;
};
Tokenizer.prototype.is_opening = function(current_token) { // jshint unused:false
Tokenizer.prototype._is_opening = function(current_token) { // jshint unused:false
return false;
};
Tokenizer.prototype.is_closing = function(current_token, open_token) { // jshint unused:false
Tokenizer.prototype._is_closing = function(current_token, open_token) { // jshint unused:false
return false;
};
Tokenizer.prototype.create_token = function(type, text) {
var token = new Token(type, text, this._newline_count, this._whitespace_before_token);
this._newline_count = 0;
this._whitespace_before_token = '';
Tokenizer.prototype._create_token = function(type, text) {
var token = new Token(type, text, this.__newline_count, this.__whitespace_before_token);
this.__newline_count = 0;
this.__whitespace_before_token = '';
return token;
};
Tokenizer.prototype.readWhitespace = function() {
Tokenizer.prototype._readWhitespace = function() {
var resulting_string = this._input.read(this._whitespace_pattern);
if (resulting_string !== '') {
if (resulting_string === ' ') {
this._whitespace_before_token = resulting_string;
} else {
this._newline_pattern.lastIndex = 0;
var nextMatch = this._newline_pattern.exec(resulting_string);
while (nextMatch[2]) {
this._newline_count += 1;
nextMatch = this._newline_pattern.exec(resulting_string);
}
this._whitespace_before_token = nextMatch[1];
if (resulting_string === ' ') {
this.__whitespace_before_token = resulting_string;
} else if (resulting_string !== '') {
this._newline_pattern.lastIndex = 0;
var nextMatch = this._newline_pattern.exec(resulting_string);
while (nextMatch[2]) {
this.__newline_count += 1;
nextMatch = this._newline_pattern.exec(resulting_string);
}
this.__whitespace_before_token = nextMatch[1];
}
};

View File

@ -1,56 +1,58 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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:
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 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.
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.
*/
'use strict';
function TokenStream(parent_token) {
// private
this._tokens = [];
this._tokens_length = this._tokens.length;
this._position = 0;
this._parent_token = parent_token;
this.__tokens = [];
this.__tokens_length = this.__tokens.length;
this.__position = 0;
this.__parent_token = parent_token;
}
TokenStream.prototype.restart = function() {
this._position = 0;
this.__position = 0;
};
TokenStream.prototype.isEmpty = function() {
return this._tokens_length === 0;
return this.__tokens_length === 0;
};
TokenStream.prototype.hasNext = function() {
return this._position < this._tokens_length;
return this.__position < this.__tokens_length;
};
TokenStream.prototype.next = function() {
var val = null;
if (this.hasNext()) {
val = this._tokens[this._position];
this._position += 1;
val = this.__tokens[this.__position];
this.__position += 1;
}
return val;
};
@ -58,19 +60,19 @@ TokenStream.prototype.next = function() {
TokenStream.prototype.peek = function(index) {
var val = null;
index = index || 0;
index += this._position;
if (index >= 0 && index < this._tokens_length) {
val = this._tokens[index];
index += this.__position;
if (index >= 0 && index < this.__tokens_length) {
val = this.__tokens[index];
}
return val;
};
TokenStream.prototype.add = function(token) {
if (this._parent_token) {
token.parent = this._parent_token;
if (this.__parent_token) {
token.parent = this.__parent_token;
}
this._tokens.push(token);
this._tokens_length += 1;
this.__tokens.push(token);
this.__tokens_length += 1;
};
module.exports.TokenStream = TokenStream;

View File

@ -1,4 +1,4 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
@ -26,409 +26,28 @@
SOFTWARE.
*/
var mergeOpts = require('../core/options').mergeOpts;
var acorn = require('../core/acorn');
'use strict';
var Options = require('./options').Options;
var Output = require('../core/output').Output;
var InputScanner = require('../core/inputscanner').InputScanner;
var lineBreak = acorn.lineBreak;
var allLineBreaks = acorn.allLineBreaks;
var lineBreak = /\r\n|[\r\n]/;
var allLineBreaks = /\r\n|[\r\n]/g;
// tokenizer
var whitespaceChar = /\s/;
var whitespacePattern = /(?:\s|\n)+/g;
var block_comment_pattern = /\/\*(?:[\s\S]*?)((?:\*\/)|$)/g;
var comment_pattern = /\/\/(?:[^\n\r\u2028\u2029]*)/g;
function Beautifier(source_text, options) {
source_text = source_text || '';
options = options || {};
this._source_text = source_text || '';
// Allow the setting of language/file-type specific options
// with inheritance of overall settings
options = mergeOpts(options, 'css');
var indentSize = options.indent_size ? parseInt(options.indent_size, 10) : 4;
var indentCharacter = options.indent_char || ' ';
var preserve_newlines = (options.preserve_newlines === undefined) ? false : options.preserve_newlines;
var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules;
var space_around_combinator = (options.space_around_combinator === undefined) ? false : options.space_around_combinator;
space_around_combinator = space_around_combinator || ((options.space_around_selector_separator === undefined) ? false : options.space_around_selector_separator);
var eol = options.eol ? options.eol : 'auto';
if (options.indent_with_tabs) {
indentCharacter = '\t';
indentSize = 1;
}
if (eol === 'auto') {
eol = '\n';
if (source_text && lineBreak.test(source_text || '')) {
eol = source_text.match(lineBreak)[0];
}
}
eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n');
// HACK: newline parsing inconsistent. This brute force normalizes the input.
source_text = source_text.replace(allLineBreaks, '\n');
// tokenizer
var whitespaceChar = /\s/;
var whitespacePattern = /(?:\s|\n)+/g;
var block_comment_pattern = /\/\*(?:[\s\S]*?)((?:\*\/)|$)/g;
var comment_pattern = /\/\/(?:[^\n\r\u2028\u2029]*)/g;
var ch;
var parenLevel = 0;
var input;
function eatString(endChars) {
var result = '';
ch = input.next();
while (ch) {
result += ch;
if (ch === "\\") {
result += input.next();
} else if (endChars.indexOf(ch) !== -1 || ch === "\n") {
break;
}
ch = input.next();
}
return result;
}
// Skips any white space in the source text from the current position.
// When allowAtLeastOneNewLine is true, will output new lines for each
// newline character found; if the user has preserve_newlines off, only
// the first newline will be output
function eatWhitespace(allowAtLeastOneNewLine) {
var result = whitespaceChar.test(input.peek());
var isFirstNewLine = true;
while (whitespaceChar.test(input.peek())) {
ch = input.next();
if (allowAtLeastOneNewLine && ch === '\n') {
if (preserve_newlines || isFirstNewLine) {
isFirstNewLine = false;
output.add_new_line(true);
}
}
}
return result;
}
// Nested pseudo-class if we are insideRule
// and the next special character found opens
// a new block
function foundNestedPseudoClass() {
var openParen = 0;
var i = 1;
var ch = input.peek(i);
while (ch) {
if (ch === "{") {
return true;
} else if (ch === '(') {
// pseudoclasses can contain ()
openParen += 1;
} else if (ch === ')') {
if (openParen === 0) {
return false;
}
openParen -= 1;
} else if (ch === ";" || ch === "}") {
return false;
}
i++;
ch = input.peek(i);
}
return false;
}
// printer
var baseIndentString = '';
var preindent_index = 0;
if (source_text && source_text.length) {
while ((source_text.charAt(preindent_index) === ' ' ||
source_text.charAt(preindent_index) === '\t')) {
preindent_index += 1;
}
baseIndentString = source_text.substring(0, preindent_index);
source_text = source_text.substring(preindent_index);
}
var singleIndent = new Array(indentSize + 1).join(indentCharacter);
var indentLevel;
var nestedLevel;
var output;
function print_string(output_string) {
if (output.just_added_newline()) {
output.set_indent(indentLevel);
}
output.add_token(output_string);
}
function preserveSingleSpace(isAfterSpace) {
if (isAfterSpace) {
output.space_before_token = true;
}
}
function indent() {
indentLevel++;
}
function outdent() {
if (indentLevel > 0) {
indentLevel--;
}
}
/*_____________________--------------------_____________________*/
this.beautify = function() {
// reset
output = new Output(singleIndent, baseIndentString);
input = new InputScanner(source_text);
indentLevel = 0;
nestedLevel = 0;
ch = null;
parenLevel = 0;
var insideRule = false;
// This is the value side of a property value pair (blue in the following ex)
// label { content: blue }
var insidePropertyValue = false;
var enteringConditionalGroup = false;
var insideAtExtend = false;
while (true) {
var whitespace = input.read(whitespacePattern);
var isAfterSpace = whitespace !== '';
ch = input.next();
if (!ch) {
break;
} else if (ch === '/' && input.peek() === '*') {
// /* css comment */
// Always start block comments on a new line.
// This handles scenarios where a block comment immediately
// follows a property definition on the same line or where
// minified code is being beautified.
output.add_new_line();
input.back();
print_string(input.read(block_comment_pattern));
// Ensures any new lines following the comment are preserved
eatWhitespace(true);
// Block comments are followed by a new line so they don't
// share a line with other properties
output.add_new_line();
} else if (ch === '/' && input.peek() === '/') {
// // single line comment
// Preserves the space before a comment
// on the same line as a rule
output.space_before_token = true;
input.back();
print_string(input.read(comment_pattern));
// Ensures any new lines following the comment are preserved
eatWhitespace(true);
} else if (ch === '@') {
preserveSingleSpace(isAfterSpace);
// deal with less propery mixins @{...}
if (input.peek() === '{') {
print_string(ch + eatString('}'));
} else {
print_string(ch);
// strip trailing space, if present, for hash property checks
var variableOrRule = input.peekUntilAfter(/[: ,;{}()[\]\/='"]/g);
if (variableOrRule.match(/[ :]$/)) {
// we have a variable or pseudo-class, add it and insert one space before continuing
variableOrRule = eatString(": ").replace(/\s$/, '');
print_string(variableOrRule);
output.space_before_token = true;
}
variableOrRule = variableOrRule.replace(/\s$/, '');
if (variableOrRule === 'extend') {
insideAtExtend = true;
}
// might be a nesting at-rule
if (variableOrRule in this.NESTED_AT_RULE) {
nestedLevel += 1;
if (variableOrRule in this.CONDITIONAL_GROUP_RULE) {
enteringConditionalGroup = true;
}
// might be less variable
} else if (!insideRule && parenLevel === 0 && variableOrRule.indexOf(':') !== -1) {
insidePropertyValue = true;
}
}
} else if (ch === '#' && input.peek() === '{') {
preserveSingleSpace(isAfterSpace);
print_string(ch + eatString('}'));
} else if (ch === '{') {
if (input.match(/[\t\n ]*}/g)) {
output.space_before_token = true;
print_string("{}");
eatWhitespace(true);
output.add_new_line();
if (newline_between_rules && indentLevel === 0 && !output.just_added_blankline()) {
output.add_new_line(true);
}
} else {
indent();
output.space_before_token = true;
print_string(ch);
eatWhitespace(true);
output.add_new_line();
// when entering conditional groups, only rulesets are allowed
if (enteringConditionalGroup) {
enteringConditionalGroup = false;
insideRule = (indentLevel > nestedLevel);
} else {
// otherwise, declarations are also allowed
insideRule = (indentLevel >= nestedLevel);
}
}
} else if (ch === '}') {
outdent();
output.add_new_line();
print_string(ch);
insideRule = false;
insidePropertyValue = false;
if (nestedLevel) {
nestedLevel--;
}
eatWhitespace(true);
output.add_new_line();
if (newline_between_rules && indentLevel === 0 && !output.just_added_blankline()) {
output.add_new_line(true);
}
} else if (ch === ":") {
if ((insideRule || enteringConditionalGroup) &&
!(input.lookBack("&") || foundNestedPseudoClass()) &&
!input.lookBack("(") && !insideAtExtend) {
// 'property: value' delimiter
// which could be in a conditional group query
print_string(':');
if (!insidePropertyValue) {
insidePropertyValue = true;
output.space_before_token = true;
}
} else {
// sass/less parent reference don't use a space
// sass nested pseudo-class don't use a space
// preserve space before pseudoclasses/pseudoelements, as it means "in any child"
if (input.lookBack(" ")) {
output.space_before_token = true;
}
if (input.peek() === ":") {
// pseudo-element
ch = input.next();
print_string("::");
} else {
// pseudo-class
print_string(':');
}
}
} else if (ch === '"' || ch === '\'') {
preserveSingleSpace(isAfterSpace);
print_string(ch + eatString(ch));
} else if (ch === ';') {
insidePropertyValue = false;
insideAtExtend = false;
print_string(ch);
eatWhitespace(true);
// This maintains single line comments on the same
// line. Block comments are also affected, but
// a new line is always output before one inside
// that section
if (input.peek() !== '/') {
output.add_new_line();
}
} else if (ch === '(') { // may be a url
if (input.lookBack("url")) {
print_string(ch);
eatWhitespace();
ch = input.next();
if (ch) {
if (ch !== ')' && ch !== '"' && ch !== '\'') {
print_string(ch + eatString(')'));
} else {
input.back();
parenLevel++;
}
}
} else {
parenLevel++;
preserveSingleSpace(isAfterSpace);
print_string(ch);
eatWhitespace();
}
} else if (ch === ')') {
print_string(ch);
parenLevel--;
} else if (ch === ',') {
print_string(ch);
eatWhitespace(true);
if (selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) {
output.add_new_line();
} else {
output.space_before_token = true;
}
} else if ((ch === '>' || ch === '+' || ch === '~') &&
!insidePropertyValue && parenLevel < 1) {
//handle combinator spacing
if (space_around_combinator) {
output.space_before_token = true;
print_string(ch);
output.space_before_token = true;
} else {
print_string(ch);
eatWhitespace();
// squash extra whitespace
if (ch && whitespaceChar.test(ch)) {
ch = '';
}
}
} else if (ch === ']') {
print_string(ch);
} else if (ch === '[') {
preserveSingleSpace(isAfterSpace);
print_string(ch);
} else if (ch === '=') { // no whitespace before or after
eatWhitespace();
print_string('=');
if (whitespaceChar.test(ch)) {
ch = '';
}
} else if (ch === '!') { // !important
print_string(' ');
print_string(ch);
} else {
preserveSingleSpace(isAfterSpace);
print_string(ch);
}
}
var sweetCode = output.get_code(end_with_newline, eol);
return sweetCode;
};
this._options = new Options(options);
this._ch = null;
this._input = null;
// https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
this.NESTED_AT_RULE = {
@ -445,6 +64,375 @@ function Beautifier(source_text, options) {
"@supports": true,
"@document": true
};
}
Beautifier.prototype.eatString = function(endChars) {
var result = '';
this._ch = this._input.next();
while (this._ch) {
result += this._ch;
if (this._ch === "\\") {
result += this._input.next();
} else if (endChars.indexOf(this._ch) !== -1 || this._ch === "\n") {
break;
}
this._ch = this._input.next();
}
return result;
};
// Skips any white space in the source text from the current position.
// When allowAtLeastOneNewLine is true, will output new lines for each
// newline character found; if the user has preserve_newlines off, only
// the first newline will be output
Beautifier.prototype.eatWhitespace = function(allowAtLeastOneNewLine) {
var result = whitespaceChar.test(this._input.peek());
var isFirstNewLine = true;
while (whitespaceChar.test(this._input.peek())) {
this._ch = this._input.next();
if (allowAtLeastOneNewLine && this._ch === '\n') {
if (this._options.preserve_newlines || isFirstNewLine) {
isFirstNewLine = false;
this._output.add_new_line(true);
}
}
}
return result;
};
// Nested pseudo-class if we are insideRule
// and the next special character found opens
// a new block
Beautifier.prototype.foundNestedPseudoClass = function() {
var openParen = 0;
var i = 1;
var ch = this._input.peek(i);
while (ch) {
if (ch === "{") {
return true;
} else if (ch === '(') {
// pseudoclasses can contain ()
openParen += 1;
} else if (ch === ')') {
if (openParen === 0) {
return false;
}
openParen -= 1;
} else if (ch === ";" || ch === "}") {
return false;
}
i++;
ch = this._input.peek(i);
}
return false;
};
Beautifier.prototype.print_string = function(output_string) {
if (this._output.just_added_newline()) {
this._output.set_indent(this._indentLevel);
}
this._output.add_token(output_string);
};
Beautifier.prototype.preserveSingleSpace = function(isAfterSpace) {
if (isAfterSpace) {
this._output.space_before_token = true;
}
};
Beautifier.prototype.indent = function() {
this._indentLevel++;
};
Beautifier.prototype.outdent = function() {
if (this._indentLevel > 0) {
this._indentLevel--;
}
};
/*_____________________--------------------_____________________*/
Beautifier.prototype.beautify = function() {
if (this._options.disabled) {
return this._source_text;
}
var source_text = this._source_text;
var eol = this._options.eol;
if (eol === 'auto') {
eol = '\n';
if (source_text && lineBreak.test(source_text || '')) {
eol = source_text.match(lineBreak)[0];
}
}
// HACK: newline parsing inconsistent. This brute force normalizes the this._input.
source_text = source_text.replace(allLineBreaks, '\n');
// reset
var baseIndentString = source_text.match(/^[\t ]*/)[0];
this._output = new Output(this._options, baseIndentString);
this._input = new InputScanner(source_text);
this._indentLevel = 0;
this._nestedLevel = 0;
this._ch = null;
var parenLevel = 0;
var insideRule = false;
// This is the value side of a property value pair (blue in the following ex)
// label { content: blue }
var insidePropertyValue = false;
var enteringConditionalGroup = false;
var insideAtExtend = false;
var insideAtImport = false;
var topCharacter = this._ch;
while (true) {
var whitespace = this._input.read(whitespacePattern);
var isAfterSpace = whitespace !== '';
var previous_ch = topCharacter;
this._ch = this._input.next();
topCharacter = this._ch;
if (!this._ch) {
break;
} else if (this._ch === '/' && this._input.peek() === '*') {
// /* css comment */
// Always start block comments on a new line.
// This handles scenarios where a block comment immediately
// follows a property definition on the same line or where
// minified code is being beautified.
this._output.add_new_line();
this._input.back();
this.print_string(this._input.read(block_comment_pattern));
// Ensures any new lines following the comment are preserved
this.eatWhitespace(true);
// Block comments are followed by a new line so they don't
// share a line with other properties
this._output.add_new_line();
} else if (this._ch === '/' && this._input.peek() === '/') {
// // single line comment
// Preserves the space before a comment
// on the same line as a rule
this._output.space_before_token = true;
this._input.back();
this.print_string(this._input.read(comment_pattern));
// Ensures any new lines following the comment are preserved
this.eatWhitespace(true);
} else if (this._ch === '@') {
this.preserveSingleSpace(isAfterSpace);
// deal with less propery mixins @{...}
if (this._input.peek() === '{') {
this.print_string(this._ch + this.eatString('}'));
} else {
this.print_string(this._ch);
// strip trailing space, if present, for hash property checks
var variableOrRule = this._input.peekUntilAfter(/[: ,;{}()[\]\/='"]/g);
if (variableOrRule.match(/[ :]$/)) {
// we have a variable or pseudo-class, add it and insert one space before continuing
variableOrRule = this.eatString(": ").replace(/\s$/, '');
this.print_string(variableOrRule);
this._output.space_before_token = true;
}
variableOrRule = variableOrRule.replace(/\s$/, '');
if (variableOrRule === 'extend') {
insideAtExtend = true;
} else if (variableOrRule === 'import') {
insideAtImport = true;
}
// might be a nesting at-rule
if (variableOrRule in this.NESTED_AT_RULE) {
this._nestedLevel += 1;
if (variableOrRule in this.CONDITIONAL_GROUP_RULE) {
enteringConditionalGroup = true;
}
// might be less variable
} else if (!insideRule && parenLevel === 0 && variableOrRule.indexOf(':') !== -1) {
insidePropertyValue = true;
this.indent();
}
}
} else if (this._ch === '#' && this._input.peek() === '{') {
this.preserveSingleSpace(isAfterSpace);
this.print_string(this._ch + this.eatString('}'));
} else if (this._ch === '{') {
if (insidePropertyValue) {
insidePropertyValue = false;
this.outdent();
}
this.indent();
this._output.space_before_token = true;
this.print_string(this._ch);
// when entering conditional groups, only rulesets are allowed
if (enteringConditionalGroup) {
enteringConditionalGroup = false;
insideRule = (this._indentLevel > this._nestedLevel);
} else {
// otherwise, declarations are also allowed
insideRule = (this._indentLevel >= this._nestedLevel);
}
if (this._options.newline_between_rules && insideRule) {
if (this._output.previous_line && this._output.previous_line.item(-1) !== '{') {
this._output.ensure_empty_line_above('/', ',');
}
}
this.eatWhitespace(true);
this._output.add_new_line();
} else if (this._ch === '}') {
this.outdent();
this._output.add_new_line();
if (previous_ch === '{') {
this._output.trim(true);
}
insideAtImport = false;
insideAtExtend = false;
if (insidePropertyValue) {
this.outdent();
insidePropertyValue = false;
}
this.print_string(this._ch);
insideRule = false;
if (this._nestedLevel) {
this._nestedLevel--;
}
this.eatWhitespace(true);
this._output.add_new_line();
if (this._options.newline_between_rules && !this._output.just_added_blankline()) {
if (this._input.peek() !== '}') {
this._output.add_new_line(true);
}
}
} else if (this._ch === ":") {
if ((insideRule || enteringConditionalGroup) && !(this._input.lookBack("&") || this.foundNestedPseudoClass()) && !this._input.lookBack("(") && !insideAtExtend) {
// 'property: value' delimiter
// which could be in a conditional group query
this.print_string(':');
if (!insidePropertyValue) {
insidePropertyValue = true;
this._output.space_before_token = true;
this.eatWhitespace(true);
this.indent();
}
} else {
// sass/less parent reference don't use a space
// sass nested pseudo-class don't use a space
// preserve space before pseudoclasses/pseudoelements, as it means "in any child"
if (this._input.lookBack(" ")) {
this._output.space_before_token = true;
}
if (this._input.peek() === ":") {
// pseudo-element
this._ch = this._input.next();
this.print_string("::");
} else {
// pseudo-class
this.print_string(':');
}
}
} else if (this._ch === '"' || this._ch === '\'') {
this.preserveSingleSpace(isAfterSpace);
this.print_string(this._ch + this.eatString(this._ch));
this.eatWhitespace(true);
} else if (this._ch === ';') {
if (insidePropertyValue) {
this.outdent();
insidePropertyValue = false;
}
insideAtExtend = false;
insideAtImport = false;
this.print_string(this._ch);
this.eatWhitespace(true);
// This maintains single line comments on the same
// line. Block comments are also affected, but
// a new line is always output before one inside
// that section
if (this._input.peek() !== '/') {
this._output.add_new_line();
}
} else if (this._ch === '(') { // may be a url
if (this._input.lookBack("url")) {
this.print_string(this._ch);
this.eatWhitespace();
this._ch = this._input.next();
if (this._ch === ')' || this._ch === '"' || this._ch === '\'') {
this._input.back();
parenLevel++;
} else if (this._ch) {
this.print_string(this._ch + this.eatString(')'));
}
} else {
parenLevel++;
this.preserveSingleSpace(isAfterSpace);
this.print_string(this._ch);
this.eatWhitespace();
}
} else if (this._ch === ')') {
this.print_string(this._ch);
parenLevel--;
} else if (this._ch === ',') {
this.print_string(this._ch);
this.eatWhitespace(true);
if (this._options.selector_separator_newline && !insidePropertyValue && parenLevel < 1 && !insideAtImport) {
this._output.add_new_line();
} else {
this._output.space_before_token = true;
}
} else if ((this._ch === '>' || this._ch === '+' || this._ch === '~') && !insidePropertyValue && parenLevel < 1) {
//handle combinator spacing
if (this._options.space_around_combinator) {
this._output.space_before_token = true;
this.print_string(this._ch);
this._output.space_before_token = true;
} else {
this.print_string(this._ch);
this.eatWhitespace();
// squash extra whitespace
if (this._ch && whitespaceChar.test(this._ch)) {
this._ch = '';
}
}
} else if (this._ch === ']') {
this.print_string(this._ch);
} else if (this._ch === '[') {
this.preserveSingleSpace(isAfterSpace);
this.print_string(this._ch);
} else if (this._ch === '=') { // no whitespace before or after
this.eatWhitespace();
this.print_string('=');
if (whitespaceChar.test(this._ch)) {
this._ch = '';
}
} else if (this._ch === '!') { // !important
this.print_string(' ');
this.print_string(this._ch);
} else {
this.preserveSingleSpace(isAfterSpace);
this.print_string(this._ch);
}
}
var sweetCode = this._output.get_code(eol);
return sweetCode;
};
module.exports.Beautifier = Beautifier;

View File

@ -1,31 +1,33 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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:
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 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.
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.
*/
'use strict';
var Beautifier = require('./beautifier').Beautifier;
function css_beautify(source_text, options) {

46
js/src/css/options.js Normal file
View File

@ -0,0 +1,46 @@
/*jshint node:true */
/*
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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.
*/
'use strict';
var BaseOptions = require('../core/options').Options;
function Options(options) {
BaseOptions.call(this, options, 'css');
this.selector_separator_newline = this._get_boolean('selector_separator_newline', true);
this.newline_between_rules = this._get_boolean('newline_between_rules', true);
var space_around_selector_separator = this._get_boolean('space_around_selector_separator');
this.space_around_combinator = this._get_boolean('space_around_combinator') || space_around_selector_separator;
}
Options.prototype = new BaseOptions();
module.exports.Options = Options;

View File

@ -1,4 +1,4 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
@ -24,4 +24,6 @@
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.
*/
*/
'use strict';

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,33 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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:
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 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.
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.
*/
'use strict';
var Beautifier = require('./beautifier').Beautifier;
function style_html(html_source, options, js_beautify, css_beautify) {

82
js/src/html/options.js Normal file
View File

@ -0,0 +1,82 @@
/*jshint node:true */
/*
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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.
*/
'use strict';
var BaseOptions = require('../core/options').Options;
function Options(options) {
BaseOptions.call(this, options, 'html');
this.indent_inner_html = this._get_boolean('indent_inner_html');
this.indent_body_inner_html = this._get_boolean('indent_body_inner_html', true);
this.indent_head_inner_html = this._get_boolean('indent_head_inner_html', true);
this.indent_handlebars = this._get_boolean('indent_handlebars', true);
this.wrap_attributes = this._get_selection('wrap_attributes',
['auto', 'force', 'force-aligned', 'force-expand-multiline', 'aligned-multiple']);
this.wrap_attributes_indent_size = this._get_number('wrap_attributes_indent_size', this.indent_size);
this.extra_liners = this._get_array('extra_liners', ['head', 'body', '/html']);
this.inline = this._get_array('inline', [
// https://www.w3.org/TR/html5/dom.html#phrasing-content
'a', 'abbr', 'area', 'audio', 'b', 'bdi', 'bdo', 'br', 'button', 'canvas', 'cite',
'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed', 'i', 'iframe', 'img',
'input', 'ins', 'kbd', 'keygen', 'label', 'map', 'mark', 'math', 'meter', 'noscript',
'object', 'output', 'progress', 'q', 'ruby', 's', 'samp', /* 'script', */ 'select', 'small',
'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var',
'video', 'wbr', 'text',
// prexisting - not sure of full effect of removing, leaving in
'acronym', 'address', 'big', 'dt', 'ins', 'strike', 'tt'
]);
this.void_elements = this._get_array('void_elements', [
// HTLM void elements - aka self-closing tags - aka singletons
// https://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr',
// NOTE: Optional tags are too complex for a simple list
// they are hard coded in _do_optional_end_element
// Doctype and xml elements
'!doctype', '?xml',
// ?php and ?= tags
'?php', '?=',
// other tags that were in this list, keeping just in case
'basefont', 'isindex'
]);
this.unformatted = this._get_array('unformatted', []);
this.content_unformatted = this._get_array('content_unformatted', [
'pre', 'textarea'
]);
this.indent_scripts = this._get_selection('indent_scripts', ['normal', 'keep', 'separate']);
}
Options.prototype = new BaseOptions();
module.exports.Options = Options;

View File

@ -1,4 +1,4 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
@ -24,4 +24,265 @@
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.
*/
*/
'use strict';
var BaseTokenizer = require('../core/tokenizer').Tokenizer;
var BASETOKEN = require('../core/tokenizer').TOKEN;
var Directives = require('../core/directives').Directives;
var TOKEN = {
TAG_OPEN: 'TK_TAG_OPEN',
TAG_CLOSE: 'TK_TAG_CLOSE',
ATTRIBUTE: 'TK_ATTRIBUTE',
EQUALS: 'TK_EQUALS',
VALUE: 'TK_VALUE',
COMMENT: 'TK_COMMENT',
TEXT: 'TK_TEXT',
UNKNOWN: 'TK_UNKNOWN',
START: BASETOKEN.START,
RAW: BASETOKEN.RAW,
EOF: BASETOKEN.EOF
};
var directives_core = new Directives(/<\!--/, /-->/);
var Tokenizer = function(input_string, options) {
BaseTokenizer.call(this, input_string, options);
this._current_tag_name = '';
// Words end at whitespace or when a tag starts
// if we are indenting handlebars, they are considered tags
this._word_pattern = this._options.indent_handlebars ? /[\n\r\t <]|{{/g : /[\n\r\t <]/g;
};
Tokenizer.prototype = new BaseTokenizer();
Tokenizer.prototype._is_comment = function(current_token) { // jshint unused:false
return false; //current_token.type === TOKEN.COMMENT || current_token.type === TOKEN.UNKNOWN;
};
Tokenizer.prototype._is_opening = function(current_token) {
return current_token.type === TOKEN.TAG_OPEN;
};
Tokenizer.prototype._is_closing = function(current_token, open_token) {
return current_token.type === TOKEN.TAG_CLOSE &&
(open_token && (
((current_token.text === '>' || current_token.text === '/>') && open_token.text[0] === '<') ||
(current_token.text === '}}' && open_token.text[0] === '{' && open_token.text[1] === '{')));
};
Tokenizer.prototype._reset = function() {
this._current_tag_name = '';
};
Tokenizer.prototype._get_next_token = function(previous_token, open_token) { // jshint unused:false
this._readWhitespace();
var token = null;
var c = this._input.peek();
if (c === null) {
return this._create_token(TOKEN.EOF, '');
}
token = token || this._read_attribute(c, previous_token, open_token);
token = token || this._read_raw_content(previous_token, open_token);
token = token || this._read_comment(c);
token = token || this._read_open(c, open_token);
token = token || this._read_close(c, open_token);
token = token || this._read_content_word();
token = token || this._create_token(TOKEN.UNKNOWN, this._input.next());
return token;
};
Tokenizer.prototype._read_comment = function(c) { // jshint unused:false
var token = null;
if (c === '<' || c === '{') {
var peek1 = this._input.peek(1);
var peek2 = this._input.peek(2);
if ((c === '<' && (peek1 === '!' || peek1 === '?' || peek1 === '%')) ||
this._options.indent_handlebars && c === '{' && peek1 === '{' && peek2 === '!') {
//if we're in a comment, do something special
// We treat all comments as literals, even more than preformatted tags
// we just look for the appropriate close tag
// this is will have very poor perf, but will work for now.
var comment = '',
delimiter = '>',
matched = false;
var input_char = this._input.next();
while (input_char) {
comment += input_char;
// only need to check for the delimiter if the last chars match
if (comment.charAt(comment.length - 1) === delimiter.charAt(delimiter.length - 1) &&
comment.indexOf(delimiter) !== -1) {
break;
}
// only need to search for custom delimiter for the first few characters
if (!matched) {
matched = comment.length > 10;
if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment
delimiter = '<![endif]>';
matched = true;
} else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment...
delimiter = ']]>';
matched = true;
} else if (comment.indexOf('<![') === 0) { // some other ![ comment? ...
delimiter = ']>';
matched = true;
} else if (comment.indexOf('<!--') === 0) { // <!-- comment ...
delimiter = '-->';
matched = true;
} else if (comment.indexOf('{{!--') === 0) { // {{!-- handlebars comment
delimiter = '--}}';
matched = true;
} else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment
if (comment.length === 5 && comment.indexOf('{{!--') === -1) {
delimiter = '}}';
matched = true;
}
} else if (comment.indexOf('<?') === 0) { // {{! handlebars comment
delimiter = '?>';
matched = true;
} else if (comment.indexOf('<%') === 0) { // {{! handlebars comment
delimiter = '%>';
matched = true;
}
}
input_char = this._input.next();
}
var directives = directives_core.get_directives(comment);
if (directives && directives.ignore === 'start') {
comment += directives_core.readIgnored(this._input);
}
token = this._create_token(TOKEN.COMMENT, comment);
token.directives = directives;
}
}
return token;
};
Tokenizer.prototype._read_open = function(c, open_token) {
var resulting_string = null;
var token = null;
if (!open_token) {
if (c === '<') {
resulting_string = this._input.read(/<(?:[^\n\r\t >{][^\n\r\t >{/]*)?/g);
token = this._create_token(TOKEN.TAG_OPEN, resulting_string);
} else if (this._options.indent_handlebars && c === '{' && this._input.peek(1) === '{') {
resulting_string = this._input.readUntil(/[\n\r\t }]/g);
token = this._create_token(TOKEN.TAG_OPEN, resulting_string);
}
}
return token;
};
Tokenizer.prototype._read_close = function(c, open_token) {
var resulting_string = null;
var token = null;
if (open_token) {
if (open_token.text[0] === '<' && (c === '>' || (c === '/' && this._input.peek(1) === '>'))) {
resulting_string = this._input.next();
if (c === '/') { // for close tag "/>"
resulting_string += this._input.next();
}
token = this._create_token(TOKEN.TAG_CLOSE, resulting_string);
} else if (open_token.text[0] === '{' && c === '}' && this._input.peek(1) === '}') {
this._input.next();
this._input.next();
token = this._create_token(TOKEN.TAG_CLOSE, '}}');
}
}
return token;
};
Tokenizer.prototype._read_attribute = function(c, previous_token, open_token) {
var token = null;
var resulting_string = '';
if (open_token && open_token.text[0] === '<') {
if (c === '=') {
token = this._create_token(TOKEN.EQUALS, this._input.next());
} else if (c === '"' || c === "'") {
var content = this._input.next();
var input_string = '';
var string_pattern = new RegExp(c + '|{{', 'g');
while (this._input.hasNext()) {
input_string = this._input.readUntilAfter(string_pattern);
content += input_string;
if (input_string[input_string.length - 1] === '"' || input_string[input_string.length - 1] === "'") {
break;
} else if (this._input.hasNext()) {
content += this._input.readUntilAfter(/}}/g);
}
}
token = this._create_token(TOKEN.VALUE, content);
} else {
if (c === '{' && this._input.peek(1) === '{') {
resulting_string = this._input.readUntilAfter(/}}/g);
} else {
resulting_string = this._input.readUntil(/[\n\r\t =\/>]/g);
}
if (resulting_string) {
if (previous_token.type === TOKEN.EQUALS) {
token = this._create_token(TOKEN.VALUE, resulting_string);
} else {
token = this._create_token(TOKEN.ATTRIBUTE, resulting_string);
}
}
}
}
return token;
};
Tokenizer.prototype._is_content_unformatted = function(tag_name) {
// void_elements have no content and so cannot have unformatted content
// script and style tags should always be read as unformatted content
// finally content_unformatted and unformatted element contents are unformatted
return this._options.void_elements.indexOf(tag_name) === -1 &&
(tag_name === 'script' || tag_name === 'style' ||
this._options.content_unformatted.indexOf(tag_name) !== -1 ||
this._options.unformatted.indexOf(tag_name) !== -1);
};
Tokenizer.prototype._read_raw_content = function(previous_token, open_token) { // jshint unused:false
var resulting_string = '';
if (open_token && open_token.text[0] === '{') {
resulting_string = this._input.readUntil(/}}/g);
} else if (previous_token.type === TOKEN.TAG_CLOSE && (previous_token.opened.text[0] === '<')) {
var tag_name = previous_token.opened.text.substr(1).toLowerCase();
if (this._is_content_unformatted(tag_name)) {
resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
}
}
if (resulting_string) {
return this._create_token(TOKEN.TEXT, resulting_string);
}
return null;
};
Tokenizer.prototype._read_content_word = function() {
// if we get here and we see handlebars treat them as plain text
var resulting_string = this._input.readUntil(this._word_pattern);
if (resulting_string) {
return this._create_token(TOKEN.TEXT, resulting_string);
}
};
module.exports.Tokenizer = Tokenizer;
module.exports.TOKEN = TOKEN;

View File

@ -1,4 +1,4 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
@ -26,6 +26,8 @@
SOFTWARE.
*/
'use strict';
var js_beautify = require('./javascript/index');
var css_beautify = require('./css/index');
var html_beautify = require('./html/index');

View File

@ -1,5 +1,5 @@
/* jshint curly: false */
// This section of code is taken from acorn.
/* jshint node: true, curly: false */
// Parts of this section of code is taken from acorn.
//
// Acorn was written by Marijn Haverbeke and released under an MIT
// license. The Unicode regexps (for identifiers and whitespace) were
@ -12,25 +12,37 @@
// ## Character categories
'use strict';
// acorn used char codes to squeeze the last bit of performance out
// Beautifier is okay without that, so we're using regex
// permit $ (36) and @ (64). @ is used in ES7 decorators.
// 65 through 91 are uppercase letters.
// permit _ (95).
// 97 through 123 are lowercase letters.
var baseASCIIidentifierStartChars = "\x24\x40\x41-\x5a\x5f\x61-\x7a";
// inside an identifier @ is not allowed but 0-9 are.
var baseASCIIidentifierChars = "\x24\x30-\x39\x41-\x5a\x5f\x61-\x7a";
// Big ugly regular expressions that match characters in the
// whitespace, identifier, and identifier-start categories. These
// are only applied when a character is found to actually have a
// code point above 128.
var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
var baseASCIIidentifierStartChars = "\x24\x40\x41-\x5a\x5f\x61-\x7a";
var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
var baseASCIIidentifierChars = "\x24\x30-\x39\x41-\x5a\x5f\x61-\x7a";
var nonASCIIidentifierChars = "\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f";
//var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
//var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
var identifierStart = new RegExp("[" + baseASCIIidentifierStartChars + nonASCIIidentifierStartChars + "]");
var identifierChars = new RegExp("[" + baseASCIIidentifierChars + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
var identifierStart = "[" + baseASCIIidentifierStartChars + nonASCIIidentifierStartChars + "]";
var identifierChars = "[" + baseASCIIidentifierChars + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]*";
exports.identifier = new RegExp("[" + baseASCIIidentifierStartChars + nonASCIIidentifierStartChars + "][" + baseASCIIidentifierChars + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]*", 'g');
exports.identifier = new RegExp(identifierStart + identifierChars, 'g');
var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
// Whether a single character denotes a newline.
exports.newline = /[\n\r\u2028\u2029]/;
@ -41,31 +53,4 @@ exports.newline = /[\n\r\u2028\u2029]/;
// in javascript, these two differ
// in python they are the same, different methods are called on them
exports.lineBreak = new RegExp('\r\n|' + exports.newline.source);
exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');
// Test whether a given character code starts an identifier.
exports.isIdentifierStart = function(code) {
// // permit $ (36) and @ (64). @ is used in ES7 decorators.
// if (code < 65) return code === 36 || code === 64;
// // 65 through 91 are uppercase letters.
// if (code < 91) return true;
// // permit _ (95).
// if (code < 97) return code === 95;
// // 97 through 123 are lowercase letters.
// if (code < 123) return true;
return identifierStart.test(String.fromCharCode(code));
};
// Test whether a given character is part of an identifier.
exports.isIdentifierChar = function(code) {
// if (code < 48) return code === 36;
// if (code < 58) return true;
// if (code < 65) return false;
// if (code < 91) return true;
// if (code < 97) return code === 95;
// if (code < 123) return true;
return identifierChars.test(String.fromCharCode(code));
};
exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,33 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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:
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 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.
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.
*/
'use strict';
var Beautifier = require('./beautifier').Beautifier;
function js_beautify(js_source_text, options) {

View File

@ -0,0 +1,93 @@
/*jshint node:true */
/*
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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.
*/
'use strict';
var BaseOptions = require('../core/options').Options;
var validPositionValues = ['before-newline', 'after-newline', 'preserve-newline'];
function Options(options) {
BaseOptions.call(this, options, 'js');
// compatibility, re
var raw_brace_style = this.raw_options.brace_style || null;
if (raw_brace_style === "expand-strict") { //graceful handling of deprecated option
this.raw_options.brace_style = "expand";
} else if (raw_brace_style === "collapse-preserve-inline") { //graceful handling of deprecated option
this.raw_options.brace_style = "collapse,preserve-inline";
} else if (this.raw_options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
this.raw_options.brace_style = this.raw_options.braces_on_own_line ? "expand" : "collapse";
// } else if (!raw_brace_style) { //Nothing exists to set it
// raw_brace_style = "collapse";
}
//preserve-inline in delimited string will trigger brace_preserve_inline, everything
//else is considered a brace_style and the last one only will have an effect
var brace_style_split = this._get_selection_list('brace_style', ['collapse', 'expand', 'end-expand', 'none', 'preserve-inline']);
this.brace_preserve_inline = false; //Defaults in case one or other was not specified in meta-option
this.brace_style = "collapse";
for (var bs = 0; bs < brace_style_split.length; bs++) {
if (brace_style_split[bs] === "preserve-inline") {
this.brace_preserve_inline = true;
} else {
this.brace_style = brace_style_split[bs];
}
}
this.unindent_chained_methods = this._get_boolean('unindent_chained_methods');
this.break_chained_methods = this._get_boolean('break_chained_methods');
this.space_in_paren = this._get_boolean('space_in_paren');
this.space_in_empty_paren = this._get_boolean('space_in_empty_paren');
this.jslint_happy = this._get_boolean('jslint_happy');
this.space_after_anon_function = this._get_boolean('space_after_anon_function');
this.space_after_named_function = this._get_boolean('space_after_named_function');
this.keep_array_indentation = this._get_boolean('keep_array_indentation');
this.space_before_conditional = this._get_boolean('space_before_conditional', true);
this.unescape_strings = this._get_boolean('unescape_strings');
this.e4x = this._get_boolean('e4x');
this.comma_first = this._get_boolean('comma_first');
this.operator_position = this._get_selection('operator_position', validPositionValues);
// For testing of beautify preserve:start directive
this.test_output_raw = this._get_boolean('test_output_raw');
// force this._options.space_after_anon_function to true if this._options.jslint_happy
if (this.jslint_happy) {
this.space_after_anon_function = true;
}
}
Options.prototype = new BaseOptions();
module.exports.Options = Options;

View File

@ -1,36 +1,38 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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:
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 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.
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.
*/
'use strict';
var InputScanner = require('../core/inputscanner').InputScanner;
var BaseTokenizer = require('../core/tokenizer').Tokenizer;
var BASETOKEN = require('../core/tokenizer').TOKEN;
var acorn = require('../core/acorn');
var Directives = require('../core/directives').Directives;
var acorn = require('./acorn');
function in_array(what, arr) {
return arr.indexOf(what) !== -1;
@ -101,23 +103,23 @@ var template_pattern = /(?:(?:<\?php|<\?=)[\s\S]*?\?>)|(?:<%[\s\S]*?%>)/g;
var in_html_comment;
var Tokenizer = function(input_string, opts) {
BaseTokenizer.call(this, input_string);
this._opts = opts;
this.positionable_operators = positionable_operators;
this.line_starters = line_starters;
var Tokenizer = function(input_string, options) {
BaseTokenizer.call(this, input_string, options);
this._whitespace_pattern = /[\n\r\u2028\u2029\t\u000B\u00A0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff ]+/g;
this._newline_pattern = /([^\n\r\u2028\u2029]*)(\r\n|[\n\r\u2028\u2029])?/g;
};
Tokenizer.prototype = new BaseTokenizer();
Tokenizer.prototype.is_comment = function(current_token) {
Tokenizer.prototype._is_comment = function(current_token) {
return current_token.type === TOKEN.COMMENT || current_token.type === TOKEN.BLOCK_COMMENT || current_token.type === TOKEN.UNKNOWN;
};
Tokenizer.prototype.is_opening = function(current_token) {
Tokenizer.prototype._is_opening = function(current_token) {
return current_token.type === TOKEN.START_BLOCK || current_token.type === TOKEN.START_EXPR;
};
Tokenizer.prototype.is_closing = function(current_token, open_token) {
Tokenizer.prototype._is_closing = function(current_token, open_token) {
return (current_token.type === TOKEN.END_BLOCK || current_token.type === TOKEN.END_EXPR) &&
(open_token && (
(current_token.text === ']' && open_token.text === '[') ||
@ -125,68 +127,68 @@ Tokenizer.prototype.is_closing = function(current_token, open_token) {
(current_token.text === '}' && open_token.text === '{')));
};
Tokenizer.prototype.reset = function() {
Tokenizer.prototype._reset = function() {
in_html_comment = false;
};
Tokenizer.prototype.get_next_token = function(last_token) {
this.readWhitespace();
Tokenizer.prototype._get_next_token = function(previous_token, open_token) { // jshint unused:false
this._readWhitespace();
var token = null;
var c = this._input.peek();
token = token || this._read_singles(c);
token = token || this._read_word(last_token);
token = token || this._read_word(previous_token);
token = token || this._read_comment(c);
token = token || this._read_string(c);
token = token || this._read_regexp(c, last_token);
token = token || this._read_xml(c, last_token);
token = token || this._read_regexp(c, previous_token);
token = token || this._read_xml(c, previous_token);
token = token || this._read_non_javascript(c);
token = token || this._read_punctuation();
token = token || this.create_token(TOKEN.UNKNOWN, this._input.next());
token = token || this._create_token(TOKEN.UNKNOWN, this._input.next());
return token;
};
Tokenizer.prototype._read_word = function(last_token) {
Tokenizer.prototype._read_word = function(previous_token) {
var resulting_string;
resulting_string = this._input.read(acorn.identifier);
if (resulting_string !== '') {
if (!(last_token.type === TOKEN.DOT ||
(last_token.type === TOKEN.RESERVED && (last_token.text === 'set' || last_token.text === 'get'))) &&
if (!(previous_token.type === TOKEN.DOT ||
(previous_token.type === TOKEN.RESERVED && (previous_token.text === 'set' || previous_token.text === 'get'))) &&
reserved_word_pattern.test(resulting_string)) {
if (resulting_string === 'in' || resulting_string === 'of') { // hack for 'in' and 'of' operators
return this.create_token(TOKEN.OPERATOR, resulting_string);
return this._create_token(TOKEN.OPERATOR, resulting_string);
}
return this.create_token(TOKEN.RESERVED, resulting_string);
return this._create_token(TOKEN.RESERVED, resulting_string);
}
return this.create_token(TOKEN.WORD, resulting_string);
return this._create_token(TOKEN.WORD, resulting_string);
}
resulting_string = this._input.read(number_pattern);
if (resulting_string !== '') {
return this.create_token(TOKEN.WORD, resulting_string);
return this._create_token(TOKEN.WORD, resulting_string);
}
};
Tokenizer.prototype._read_singles = function(c) {
var token = null;
if (c === null) {
token = this.create_token(TOKEN.EOF, '');
token = this._create_token(TOKEN.EOF, '');
} else if (c === '(' || c === '[') {
token = this.create_token(TOKEN.START_EXPR, c);
token = this._create_token(TOKEN.START_EXPR, c);
} else if (c === ')' || c === ']') {
token = this.create_token(TOKEN.END_EXPR, c);
token = this._create_token(TOKEN.END_EXPR, c);
} else if (c === '{') {
token = this.create_token(TOKEN.START_BLOCK, c);
token = this._create_token(TOKEN.START_BLOCK, c);
} else if (c === '}') {
token = this.create_token(TOKEN.END_BLOCK, c);
token = this._create_token(TOKEN.END_BLOCK, c);
} else if (c === ';') {
token = this.create_token(TOKEN.SEMICOLON, c);
token = this._create_token(TOKEN.SEMICOLON, c);
} else if (c === '.' && dot_pattern.test(this._input.peek(1))) {
token = this.create_token(TOKEN.DOT, c);
token = this._create_token(TOKEN.DOT, c);
} else if (c === ',') {
token = this.create_token(TOKEN.COMMA, c);
token = this._create_token(TOKEN.COMMA, c);
}
if (token) {
@ -200,9 +202,9 @@ Tokenizer.prototype._read_punctuation = function() {
if (resulting_string !== '') {
if (resulting_string === '=') {
return this.create_token(TOKEN.EQUALS, resulting_string);
return this._create_token(TOKEN.EQUALS, resulting_string);
} else {
return this.create_token(TOKEN.OPERATOR, resulting_string);
return this._create_token(TOKEN.OPERATOR, resulting_string);
}
}
};
@ -213,14 +215,14 @@ Tokenizer.prototype._read_non_javascript = function(c) {
if (c === '#') {
c = this._input.next();
if (this._tokens.isEmpty() && this._input.peek() === '!') {
if (this._is_first_token() && this._input.peek() === '!') {
// shebang
resulting_string = c;
while (this._input.hasNext() && c !== '\n') {
c = this._input.next();
resulting_string += c;
}
return this.create_token(TOKEN.UNKNOWN, resulting_string.trim() + '\n');
return this._create_token(TOKEN.UNKNOWN, resulting_string.trim() + '\n');
}
// Spidermonkey-specific sharp variables for circular references. Considered obsolete.
@ -241,7 +243,7 @@ Tokenizer.prototype._read_non_javascript = function(c) {
this._input.next();
this._input.next();
}
return this.create_token(TOKEN.WORD, sharp);
return this._create_token(TOKEN.WORD, sharp);
}
this._input.back();
@ -251,7 +253,7 @@ Tokenizer.prototype._read_non_javascript = function(c) {
resulting_string = this._input.read(template_pattern);
if (resulting_string) {
resulting_string = resulting_string.replace(acorn.allLineBreaks, '\n');
return this.create_token(TOKEN.STRING, resulting_string);
return this._create_token(TOKEN.STRING, resulting_string);
}
} else if (this._input.match(/<\!--/g)) {
c = '<!--';
@ -259,11 +261,11 @@ Tokenizer.prototype._read_non_javascript = function(c) {
c += this._input.next();
}
in_html_comment = true;
return this.create_token(TOKEN.COMMENT, c);
return this._create_token(TOKEN.COMMENT, c);
}
} else if (c === '-' && in_html_comment && this._input.match(/-->/g)) {
in_html_comment = false;
return this.create_token(TOKEN.COMMENT, '-->');
return this._create_token(TOKEN.COMMENT, '-->');
}
return null;
@ -281,12 +283,12 @@ Tokenizer.prototype._read_comment = function(c) {
comment += directives_core.readIgnored(this._input);
}
comment = comment.replace(acorn.allLineBreaks, '\n');
token = this.create_token(TOKEN.BLOCK_COMMENT, comment);
token = this._create_token(TOKEN.BLOCK_COMMENT, comment);
token.directives = directives;
} else if (this._input.peek(1) === '/') {
// peek for comment // ...
comment = this._input.read(comment_pattern);
token = this.create_token(TOKEN.COMMENT, comment);
token = this._create_token(TOKEN.COMMENT, comment);
}
}
return token;
@ -303,32 +305,32 @@ Tokenizer.prototype._read_string = function(c) {
resulting_string += this._read_string_recursive(c);
}
if (this.has_char_escapes && this._opts.unescape_strings) {
if (this.has_char_escapes && this._options.unescape_strings) {
resulting_string = unescape_string(resulting_string);
}
if (this._input.peek() === c) {
resulting_string += this._input.next();
}
return this.create_token(TOKEN.STRING, resulting_string);
return this._create_token(TOKEN.STRING, resulting_string);
}
return null;
};
Tokenizer.prototype._allow_regexp_or_xml = function(last_token) {
Tokenizer.prototype._allow_regexp_or_xml = function(previous_token) {
// regex and xml can only appear in specific locations during parsing
return (last_token.type === TOKEN.RESERVED && in_array(last_token.text, ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
(last_token.type === TOKEN.END_EXPR && last_token.text === ')' &&
last_token.parent && last_token.parent.type === TOKEN.RESERVED && in_array(last_token.parent.text, ['if', 'while', 'for'])) ||
(in_array(last_token.type, [TOKEN.COMMENT, TOKEN.START_EXPR, TOKEN.START_BLOCK, TOKEN.START,
return (previous_token.type === TOKEN.RESERVED && in_array(previous_token.text, ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
(previous_token.type === TOKEN.END_EXPR && previous_token.text === ')' &&
previous_token.opened.previous.type === TOKEN.RESERVED && in_array(previous_token.opened.previous.text, ['if', 'while', 'for'])) ||
(in_array(previous_token.type, [TOKEN.COMMENT, TOKEN.START_EXPR, TOKEN.START_BLOCK, TOKEN.START,
TOKEN.END_BLOCK, TOKEN.OPERATOR, TOKEN.EQUALS, TOKEN.EOF, TOKEN.SEMICOLON, TOKEN.COMMA
]));
};
Tokenizer.prototype._read_regexp = function(c, last_token) {
Tokenizer.prototype._read_regexp = function(c, previous_token) {
if (c === '/' && this._allow_regexp_or_xml(last_token)) {
if (c === '/' && this._allow_regexp_or_xml(previous_token)) {
// handle regexp
//
var resulting_string = this._input.next();
@ -359,7 +361,7 @@ Tokenizer.prototype._read_regexp = function(c, last_token) {
// Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
resulting_string += this._input.read(acorn.identifier);
}
return this.create_token(TOKEN.STRING, resulting_string);
return this._create_token(TOKEN.STRING, resulting_string);
}
return null;
};
@ -368,9 +370,9 @@ Tokenizer.prototype._read_regexp = function(c, last_token) {
var startXmlRegExp = /<()([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{[\s\S]+?}))*\s*(\/?)\s*>/g;
var xmlRegExp = /[\s\S]*?<(\/?)([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{[\s\S]+?}))*\s*(\/?)\s*>/g;
Tokenizer.prototype._read_xml = function(c, last_token) {
Tokenizer.prototype._read_xml = function(c, previous_token) {
if (this._opts.e4x && c === "<" && this._input.test(startXmlRegExp) && this._allow_regexp_or_xml(last_token)) {
if (this._options.e4x && c === "<" && this._input.test(startXmlRegExp) && this._allow_regexp_or_xml(previous_token)) {
// handle e4x xml literals
//
var xmlStr = '';
@ -403,7 +405,7 @@ Tokenizer.prototype._read_xml = function(c, last_token) {
xmlStr += this._input.match(/[\s\S]*/g)[0];
}
xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n');
return this.create_token(TOKEN.STRING, xmlStr);
return this._create_token(TOKEN.STRING, xmlStr);
}
}
@ -526,7 +528,7 @@ Tokenizer.prototype._read_string_recursive = function(delimiter, allow_unescaped
return resulting_string;
};
module.exports.Tokenizer = Tokenizer;
module.exports.TOKEN = TOKEN;
module.exports.TOKEN = TOKEN;
module.exports.positionable_operators = positionable_operators.slice();
module.exports.line_starters = line_starters.slice();

View File

@ -1,6 +1,33 @@
/*
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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.
*/
//
// simple unpacker/deobfuscator for scripts messed up with javascriptobfuscator.com
// written by Einar Lielmanis <einar@jsbeautifier.org>
// written by Einar Lielmanis <einar@beautifier.io>
//
// usage:
//
@ -10,6 +37,8 @@
//
//
/*jshint strict:false */
var JavascriptObfuscator = {
detect: function(str) {
return /^var _0x[a-f0-9]+ ?\= ?\[/.test(str);

View File

@ -1,3 +1,30 @@
/*
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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.
*/
//
// simple unpacker/deobfuscator for scripts messed up with myobfuscate.com
// You really don't want to obfuscate your scripts there: they're tracking
@ -16,7 +43,7 @@
*/
//
// written by Einar Lielmanis <einar@jsbeautifier.org>
// written by Einar Lielmanis <einar@beautifier.io>
//
// usage:
//
@ -26,6 +53,8 @@
//
//
/*jshint strict:false */
var MyObfuscate = {
detect: function(str) {
if (/^var _?[0O1lI]{3}\=('|\[).*\)\)\);/.test(str)) {

View File

@ -1,6 +1,32 @@
/*
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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.
*/
//
// Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
// written by Einar Lielmanis <einar@jsbeautifier.org>
//
// Coincidentally, it can defeat a couple of other eval-based compressors.
//
@ -12,6 +38,8 @@
//
//
/*jshint strict:false */
var P_A_C_K_E_R = {
detect: function(str) {
return (P_A_C_K_E_R.get_chunks(str).length > 0);

View File

@ -1,8 +1,36 @@
/*global unescape */
/*jshint curly: false, scripturl: true */
/*
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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.
*/
//
// trivial bookmarklet/escaped script detector for the javascript beautifier
// written by Einar Lielmanis <einar@jsbeautifier.org>
// written by Einar Lielmanis <einar@beautifier.io>
//
// usage:
//
@ -12,6 +40,9 @@
//
//
/*jshint strict:false */
var isNode = (typeof module !== 'undefined' && module.exports);
if (isNode) {
var SanityTest = require(__dirname + '/../../test/sanitytest');

View File

@ -1,5 +1,7 @@
/*jshint node:true */
'use strict';
var requirejs = require('requirejs'),
SanityTest = require('./sanitytest'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),

View File

@ -1,3 +1,6 @@
/*jshint mocha:true */
'use strict';
var assert = require('assert');
var InputScanner = require('../../src/core/inputscanner').InputScanner;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
/*global js_beautify: true */
/*jshint node:true */
/*jshint unused:false */
'use strict';
var fs = require('fs'),
SanityTest = require('./sanitytest'),
Benchmark = require('benchmark'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),
beautifier = require('../src/index');
function node_beautifier_html_tests() {
console.log('Testing performance...');
var github_css = fs.readFileSync(__dirname + '/../../test/resources/github.css', 'utf8');
var options = {
wrap_line_length: 80
};
//warm-up
beautifier.css(github_css, options);
var suite = new Benchmark.Suite();
suite.add("css-beautify (github.css)", function() {
beautifier.css(github_css, options);
})
// add listeners
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('error', function(event) {
return 1;
})
.on('complete', function(event) {})
.run();
return 0;
}
if (require.main === module) {
process.exit(node_beautifier_html_tests());
}

View File

@ -2,6 +2,7 @@
/*jshint node:true */
/*jshint unused:false */
'use strict';
var fs = require('fs'),
SanityTest = require('./sanitytest'),
@ -11,6 +12,7 @@ var fs = require('fs'),
function node_beautifier_html_tests() {
console.log('Testing performance...');
var github_html = fs.readFileSync(__dirname + '/../../test/resources/github.html', 'utf8');
var index_html = fs.readFileSync(__dirname + '/../../index.html', 'utf8');
var data_attr = fs.readFileSync(__dirname + '/../../test/resources/html-with-base64image.html', 'utf8');
var options = {
@ -18,7 +20,7 @@ function node_beautifier_html_tests() {
};
//warm-up
beautifier.html(index_html, options);
beautifier.html(github_html, options);
beautifier.html(data_attr, options);
var suite = new Benchmark.Suite();
@ -29,6 +31,9 @@ function node_beautifier_html_tests() {
.add("html-beautify (base64 image)", function() {
beautifier.html(data_attr, options);
})
.add("html-beautify (github.html)", function() {
beautifier.html(github_html, options);
})
// add listeners
.on('cycle', function(event) {
console.log(String(event.target));

View File

@ -2,6 +2,8 @@
/*jshint node:true */
/*jshint unused:false */
'use strict';
var fs = require('fs'),
SanityTest = require('./sanitytest'),
Benchmark = require('benchmark'),
@ -12,6 +14,7 @@ function node_beautifier_tests() {
console.log('Testing performance...');
var data = fs.readFileSync(__dirname + '/../../test/resources/underscore.js', 'utf8');
var data_min = fs.readFileSync(__dirname + '/../../test/resources/underscore-min.js', 'utf8');
var github_min = fs.readFileSync(__dirname + '/../../test/resources/github-min.js', 'utf8');
var options = {
wrap_line_length: 80
};
@ -28,6 +31,9 @@ function node_beautifier_tests() {
.add("js-beautify (underscore-min)", function() {
beautifier.js(data_min, options);
})
.add("js-beautify (github-min)", function() {
beautifier.js(github_min, options);
})
// add listeners
.on('cycle', function(event) {
console.log(String(event.target));

View File

@ -1,5 +1,7 @@
/*jshint node:true */
'use strict';
var SanityTest = require('./sanitytest'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),
run_javascript_tests = require('./generated/beautify-javascript-tests').run_javascript_tests,

View File

@ -2,6 +2,8 @@
/*jshint node:true */
'use strict';
var SanityTest = require('./sanitytest'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),
run_javascript_tests = require('./generated/beautify-javascript-tests').run_javascript_tests,

View File

@ -1,6 +1,6 @@
//
// simple testing interface
// written by Einar Lielmanis, einar@jsbeautifier.org
// written by Einar Lielmanis, einar@beautifier.io
//
// usage:
//
@ -10,8 +10,8 @@
// output_somewhere(t.results()); // good for <pre>, html safe-ish
// alert(t.results_raw()); // html unescaped
function SanityTest(func, name_of_test) {
'use strict';
var test_func = func || function(x) {
return x;
@ -39,9 +39,11 @@ function SanityTest(func, name_of_test) {
// proper array checking is a pain. i'll maybe do it later, compare strings representations instead
if ((result === expected_value) || (expected_value instanceof Array && result.join(', ') === expected_value.join(', '))) {
n_succeeded += 1;
return true;
} else {
n_failed += 1;
failures.push([test_name, parameters, expected_value, result]);
return false;
}
};

3503
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "js-beautify",
"version": "1.8.0-rc4",
"description": "jsbeautifier.org for node",
"version": "1.8.6",
"description": "beautifier.io for node",
"main": "js/index.js",
"bin": {
"css-beautify": "./js/bin/css-beautify.js",
@ -21,7 +21,7 @@
],
"scripts": {},
"bugs": "https://github.com/beautify-web/js-beautify/issues",
"homepage": "http://jsbeautifier.org/",
"homepage": "https://beautifier.io/",
"repository": {
"type": "git",
"url": "git://github.com/beautify-web/js-beautify.git"
@ -31,7 +31,7 @@
"beautifier",
"code-quality"
],
"author": "Einar Lielmanis <einar@jsbeautifier.org>",
"author": "Einar Lielmanis <einar@beautifier.io>",
"contributors": [
"Vital Batmanov <vital76@gmail.com>",
"Chris J. Shull <chrisjshull@gmail.com>",
@ -41,24 +41,23 @@
"Daniel Stockman <daniel.stockman@gmail.com>",
"Harutyun Amirjanyan <amirjanyan@gmail.com>",
"Nochum Sossonko <nsossonko@hotmail.com>",
"Liam Newman <bitwiseman@gmail.com>"
"Liam Newman <bitwiseman@beautifier.io>"
],
"license": "MIT",
"dependencies": {
"config-chain": "~1.1.5",
"editorconfig": "^0.15.0",
"mkdirp": "~0.5.0",
"nopt": "~4.0.1",
"npm": "^6.2.0"
"nopt": "~4.0.1"
},
"devDependencies": {
"benchmark": "^2.1.4",
"jshint": "~2.9.1",
"jshint": "^2.9.6",
"mocha": "^5.2.0",
"mustache": "~2.3.0",
"mustache": "^2.3.2",
"node-static": "^0.7.10",
"requirejs": "^2.3.3",
"webpack": "^4.16.2",
"requirejs": "^2.3.6",
"webpack": "^4.17.1",
"webpack-command": "^0.4.1"
}
}

View File

@ -52,7 +52,7 @@ def beautify_file(file_name, opts=default_options()):
raise Exception()
stream = sys.stdin
except Exception as ex:
except Exception:
print("Must pipe input or define input file.\n", file=sys.stderr)
usage(sys.stderr)
raise Exception()
@ -68,7 +68,7 @@ def usage(stream=sys.stdout):
print("cssbeautifier.py@" + __version__ + """
CSS beautifier (http://jsbeautifier.org/)
CSS beautifier (https://beautifier.io/)
Usage: cssbeautifier.py [options] <infile>

View File

@ -3,11 +3,15 @@ import sys
import re
import copy
from .options import BeautifierOptions
from jsbeautifier.core.options import mergeOpts
from jsbeautifier.core.output import Output
from jsbeautifier.core.inputscanner import InputScanner
from jsbeautifier.__version__ import __version__
# This is not pretty, but given how we did the version import
# it is the only way to do this without having setup.py fail on a missing
# six dependency.
six = __import__("six")
#
# The MIT License (MIT)
@ -34,6 +38,7 @@ from jsbeautifier.__version__ import __version__
# SOFTWARE.
whitespaceChar = re.compile(r"\s")
whitespacePattern = re.compile(r"(?:\s|\n)+")
@ -63,7 +68,7 @@ def usage(stream=sys.stdout):
print("cssbeautifier.py@" + __version__ + """
CSS beautifier (http://jsbeautifier.org/)
CSS beautifier (https://beautifier.io/)
""", file=stream)
if stream == sys.stderr:
@ -72,101 +77,58 @@ CSS beautifier (http://jsbeautifier.org/)
return 0
class Printer:
def __init__(self, indent_char, indent_size, default_indent=""):
self.singleIndent = (indent_size) * indent_char
self.indentLevel = 0
self.nestedLevel = 0
self.baseIndentString = default_indent
self.output = Output(self.singleIndent, self.baseIndentString)
def indent(self):
self.indentLevel += 1
def outdent(self):
if self.indentLevel > 0:
self.indentLevel -= 1
def preserveSingleSpace(self, isAfterSpace):
if isAfterSpace:
self.output.space_before_token = True
def print_string(self, output_string):
if self.output.just_added_newline():
self.output.set_indent(self.indentLevel)
self.output.add_token(output_string)
class Beautifier:
def __init__(self, source_text, opts=default_options()):
import jsbeautifier.core.acorn as acorn
self.lineBreak = acorn.lineBreak
self.allLineBreaks = acorn.allLineBreaks
# in javascript, these two differ
# in python they are the same, different methods are called on them
# IMPORTANT: This string must be run through six to handle \u chars
self.lineBreak = re.compile(six.u(r"\r\n|[\n\r]"))
self.allLineBreaks = self.lineBreak
self.comment_pattern = re.compile(
acorn.six.u(r"\/\/(?:[^\n\r\u2028\u2029]*)"))
six.u(r"\/\/(?:[^\n\r\u2028\u2029]*)"))
self.block_comment_pattern = re.compile(
r"\/\*(?:[\s\S]*?)((?:\*\/)|$)")
if not source_text:
source_text = ''
opts = mergeOpts(opts, 'css')
self.__source_text = source_text
# Continue to accept deprecated option
opts.space_around_combinator = opts.space_around_combinator or opts.space_around_selector_separator
self._options = BeautifierOptions(opts)
self._input = None
self._ch = None
self.opts = opts
self.indentSize = opts.indent_size
self.indentChar = opts.indent_char
self.input = None
self.ch = None
if self.opts.indent_with_tabs:
self.indentChar = "\t"
self.indentSize = 1
if self.opts.eol == 'auto':
self.opts.eol = '\n'
if self.lineBreak.search(source_text or ''):
self.opts.eol = self.lineBreak.search(source_text).group()
self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n')
# HACK: newline parsing inconsistent. This brute force normalizes the
# input newlines.
self.source_text = re.sub(self.allLineBreaks, '\n', source_text)
self._indentLevel = 0
self._nestedLevel = 0
self._output = None
# https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
# also in CONDITIONAL_GROUP_RULE below
self.NESTED_AT_RULE = [
self.NESTED_AT_RULE = {
"@page",
"@font-face",
"@keyframes",
"@media",
"@supports",
"@document"]
self.CONDITIONAL_GROUP_RULE = [
"@document"}
self.CONDITIONAL_GROUP_RULE = {
"@media",
"@supports",
"@document"]
m = re.search("^[\t ]*", self.source_text)
self.baseIndentString = m.group(0)
"@document"}
def eatString(self, endChars):
result = ''
self.ch = self.input.next()
while self.ch:
result += self.ch
if self.ch == "\\":
result += self.input.next()
elif self.ch in endChars or self.ch == "\n":
self._ch = self._input.next()
while self._ch:
result += self._ch
if self._ch == "\\":
result += self._input.next()
elif self._ch in endChars or self._ch == "\n":
break
self.ch = self.input.next()
self._ch = self._input.next()
return result
# Skips any white space in the source text from the current position.
@ -174,15 +136,15 @@ class Beautifier:
# newline character found; if the user has preserve_newlines off, only
# the first newline will be output
def eatWhitespace(self, allowAtLeastOneNewLine=False):
result = whitespaceChar.search(self.input.peek() or '') is not None
result = whitespaceChar.search(self._input.peek() or '') is not None
isFirstNewLine = True
while whitespaceChar.search(self.input.peek() or '') is not None:
self.ch = self.input.next()
if allowAtLeastOneNewLine and self.ch == "\n":
if self.opts.preserve_newlines or isFirstNewLine:
while whitespaceChar.search(self._input.peek() or '') is not None:
self._ch = self._input.next()
if allowAtLeastOneNewLine and self._ch == "\n":
if self._options.preserve_newlines or isFirstNewLine:
isFirstNewLine = False
self.output.add_new_line(True)
self._output.add_new_line(True)
return result
# Nested pseudo-class if we are insideRule
@ -191,7 +153,7 @@ class Beautifier:
def foundNestedPseudoClass(self):
openParen = 0
i = 1
ch = self.input.peek(i)
ch = self._input.peek(i)
while ch:
if ch == "{":
return True
@ -205,46 +167,82 @@ class Beautifier:
elif ch == ";" or ch == "}":
return False
i += 1
ch = self.input.peek(i)
ch = self._input.peek(i)
return False
def indent(self):
self._indentLevel += 1
def outdent(self):
if self._indentLevel > 0:
self._indentLevel -= 1
def preserveSingleSpace(self, isAfterSpace):
if isAfterSpace:
self._output.space_before_token = True
def print_string(self, output_string):
if self._output.just_added_newline():
self._output.set_indent(self._indentLevel)
self._output.add_token(output_string)
def beautify(self):
printer = Printer(
self.indentChar,
self.indentSize,
self.baseIndentString)
self.output = printer.output
self.input = InputScanner(self.source_text)
if self._options.disabled:
return self.__source_text
output = self.output
input = self.input
source_text = self.__source_text
self.ch = None
if self._options.eol == 'auto':
self._options.eol = '\n'
if self.lineBreak.search(source_text or ''):
self._options.eol = self.lineBreak.search(source_text).group()
# HACK: newline parsing inconsistent. This brute force normalizes the
# input newlines.
source_text = re.sub(self.allLineBreaks, '\n', source_text)
baseIndentString = re.search("^[\t ]*", source_text).group(0)
self._output = Output(self._options, baseIndentString)
self._input = InputScanner(source_text)
self._indentLevel = 0
self._nestedLevel = 0
self._ch = None
parenLevel = 0
insideRule = False
insidePropertyValue = False
enteringConditionalGroup = False
insideAtExtend = False
parenLevel = 0
insideAtImport = False
topCharacter = self._ch
while True:
whitespace = input.read(whitespacePattern)
whitespace = self._input.read(whitespacePattern)
isAfterSpace = whitespace != ''
self.ch = input.next()
previous_ch = topCharacter
self._ch = self._input.next()
topCharacter = self._ch
if not self.ch:
if not self._ch:
break
elif self.ch == '/' and input.peek() == '*':
elif self._ch == '/' and self._input.peek() == '*':
# /* css comment */
# Always start block comments on a new line.
# This handles scenarios where a block comment immediately
# follows a property definition on the same line or where
# minified code is being beautified.
output.add_new_line()
input.back()
printer.print_string(
input.read(
self._output.add_new_line()
self._input.back()
self.print_string(
self._input.read(
self.block_comment_pattern))
# Ensures any new lines following the comment are preserved
@ -252,27 +250,27 @@ class Beautifier:
# Block comments are followed by a new line so they don't
# share a line with other properties
output.add_new_line()
elif self.ch == '/' and input.peek() == '/':
self._output.add_new_line()
elif self._ch == '/' and self._input.peek() == '/':
# // single line comment
# Preserves the space before a comment
# on the same line as a rule
output.space_before_token = True
input.back()
printer.print_string(input.read(self.comment_pattern))
self._output.space_before_token = True
self._input.back()
self.print_string(self._input.read(self.comment_pattern))
# Ensures any new lines following the comment are preserved
self.eatWhitespace(True)
elif self.ch == '@':
printer.preserveSingleSpace(isAfterSpace)
elif self._ch == '@':
self.preserveSingleSpace(isAfterSpace)
# deal with less propery mixins @{...}
if input.peek() == '{':
printer.print_string(self.ch + self.eatString('}'))
if self._input.peek() == '{':
self.print_string(self._ch + self.eatString('}'))
else:
printer.print_string(self.ch)
# strip trailing space, if present, for hash property check
variableOrRule = input.peekUntilAfter(
self.print_string(self._ch)
# strip trailing space, for hash property check
variableOrRule = self._input.peekUntilAfter(
re.compile(r"[: ,;{}()[\]\/='\"]"))
if variableOrRule[-1] in ": ":
@ -281,163 +279,182 @@ class Beautifier:
variableOrRule = self.eatString(": ")
if variableOrRule[-1].isspace():
variableOrRule = variableOrRule[:-1]
printer.print_string(variableOrRule)
output.space_before_token = True
self.print_string(variableOrRule)
self._output.space_before_token = True
if variableOrRule[-1].isspace():
variableOrRule = variableOrRule[:-1]
if variableOrRule == "extend":
insideAtExtend = True
elif variableOrRule == "import":
insideAtImport = True
# might be a nesting at-rule
if variableOrRule in self.NESTED_AT_RULE:
printer.nestedLevel += 1
self._nestedLevel += 1
if variableOrRule in self.CONDITIONAL_GROUP_RULE:
enteringConditionalGroup = True
elif not insideRule and parenLevel == 0 and variableOrRule[-1] == ":":
elif not insideRule and parenLevel == 0 and \
variableOrRule[-1] == ":":
insidePropertyValue = True
elif self.ch == '#' and input.peek() == '{':
printer.preserveSingleSpace(isAfterSpace)
printer.print_string(self.ch + self.eatString('}'))
elif self.ch == '{':
if input.match(re.compile("[\t\n ]*}")) is not None:
output.space_before_token = True
printer.print_string("{}")
self.eatWhitespace(True)
output.add_new_line()
self.indent()
elif self._ch == '#' and self._input.peek() == '{':
self.preserveSingleSpace(isAfterSpace)
self.print_string(self._ch + self.eatString('}'))
elif self._ch == '{':
if insidePropertyValue:
insidePropertyValue = False
self.outdent()
self.indent()
self._output.space_before_token = True
self.print_string(self._ch)
if self.opts.newline_between_rules and printer.indentLevel == 0 and not output.just_added_blankline():
output.add_new_line(True)
# when entering conditional groups, only rulesets are
# allowed
if enteringConditionalGroup:
enteringConditionalGroup = False
insideRule = self._indentLevel > self._nestedLevel
else:
printer.indent()
output.space_before_token = True
printer.print_string(self.ch)
self.eatWhitespace(True)
output.add_new_line()
# otherwise, declarations are also allowed
insideRule = self._indentLevel >= self._nestedLevel
# when entering conditional groups, only rulesets are
# allowed
if enteringConditionalGroup:
enteringConditionalGroup = False
insideRule = printer.indentLevel > printer.nestedLevel
else:
# otherwise, declarations are also allowed
insideRule = printer.indentLevel >= printer.nestedLevel
elif self.ch == '}':
printer.outdent()
output.add_new_line()
printer.print_string(self.ch)
if self._options.newline_between_rules and insideRule:
if self._output.previous_line and \
not self._output.previous_line.is_empty() and \
self._output.previous_line.item(-1) != '{':
self._output.ensure_empty_line_above('/', ',')
self.eatWhitespace(True)
self._output.add_new_line()
elif self._ch == '}':
self.outdent()
self._output.add_new_line()
if previous_ch == '{':
self._output.trim(True)
insideAtExtend = False
insideAtImport = False
if insidePropertyValue:
self.outdent()
insidePropertyValue = False
self.print_string(self._ch)
insideRule = False
insidePropertyValue = False
if printer.nestedLevel:
printer.nestedLevel -= 1
if self._nestedLevel:
self._nestedLevel -= 1
self.eatWhitespace(True)
output.add_new_line()
self._output.add_new_line()
if self.opts.newline_between_rules and printer.indentLevel == 0 and not output.just_added_blankline():
output.add_new_line(True)
elif self.ch == ":":
if self._options.newline_between_rules and \
not self._output.just_added_blankline():
if self._input.peek() != '}':
self._output.add_new_line(True)
elif self._ch == ":":
if (insideRule or enteringConditionalGroup) and \
not (input.lookBack('&') or self.foundNestedPseudoClass()) and \
not input.lookBack('(') and not insideAtExtend:
not (self._input.lookBack('&') or
self.foundNestedPseudoClass()) and \
not self._input.lookBack('(') and not insideAtExtend:
# 'property: value' delimiter
# which could be in a conditional group query
printer.print_string(":")
self.print_string(":")
if not insidePropertyValue:
insidePropertyValue = True
output.space_before_token = True
self._output.space_before_token = True
self.eatWhitespace(True)
self.indent()
else:
# sass/less parent reference don't use a space
# sass nested pseudo-class don't use a space
# preserve space before pseudoclasses/pseudoelements, as it
# means "in any child"
if input.lookBack(' '):
output.space_before_token = True
if input.peek() == ":":
# preserve space before pseudoclasses/pseudoelements,
# as it means "in any child"
if self._input.lookBack(' '):
self._output.space_before_token = True
if self._input.peek() == ":":
# pseudo-element
self.ch = input.next()
printer.print_string("::")
self._ch = self._input.next()
self.print_string("::")
else:
# pseudo-element
printer.print_string(":")
elif self.ch == '"' or self.ch == '\'':
printer.preserveSingleSpace(isAfterSpace)
printer.print_string(self.ch + self.eatString(self.ch))
elif self.ch == ';':
insidePropertyValue = False
self.print_string(":")
elif self._ch == '"' or self._ch == '\'':
self.preserveSingleSpace(isAfterSpace)
self.print_string(self._ch + self.eatString(self._ch))
self.eatWhitespace(True)
elif self._ch == ';':
if insidePropertyValue:
self.outdent()
insidePropertyValue = False
insideAtExtend = False
printer.print_string(self.ch)
insideAtImport = False
self.print_string(self._ch)
self.eatWhitespace(True)
# This maintains single line comments on the same
# line. Block comments are also affected, but
# a new line is always output before one inside
# that section
if input.peek() is not '/':
output.add_new_line()
elif self.ch == '(':
if self._input.peek() is not '/':
self._output.add_new_line()
elif self._ch == '(':
# may be a url
if input.lookBack("url"):
printer.print_string(self.ch)
if self._input.lookBack("url"):
self.print_string(self._ch)
self.eatWhitespace()
self.ch = input.next()
if self.ch:
if self.ch is not ')' and self.ch is not '"' \
and self.ch is not '\'':
printer.print_string(self.ch + self.eatString(')'))
else:
input.back()
parenLevel += 1
self._ch = self._input.next()
if self._ch in {')', '"', '\''}:
self._input.back()
parenLevel += 1
elif self._ch is not None:
self.print_string(self._ch + self.eatString(')'))
else:
parenLevel += 1
printer.preserveSingleSpace(isAfterSpace)
printer.print_string(self.ch)
self.preserveSingleSpace(isAfterSpace)
self.print_string(self._ch)
self.eatWhitespace()
elif self.ch == ')':
printer.print_string(self.ch)
elif self._ch == ')':
self.print_string(self._ch)
parenLevel -= 1
elif self.ch == ',':
printer.print_string(self.ch)
elif self._ch == ',':
self.print_string(self._ch)
self.eatWhitespace(True)
if self.opts.selector_separator_newline and not insidePropertyValue and parenLevel < 1:
output.add_new_line()
if self._options.selector_separator_newline and \
not insidePropertyValue and parenLevel < 1 and \
not insideAtImport:
self._output.add_new_line()
else:
output.space_before_token = True
elif (self.ch == '>' or self.ch == '+' or self.ch == '~') and \
self._output.space_before_token = True
elif (self._ch == '>' or self._ch == '+' or self._ch == '~') and \
not insidePropertyValue and parenLevel < 1:
# handle combinator spacing
if self.opts.space_around_combinator:
output.space_before_token = True
printer.print_string(self.ch)
output.space_before_token = True
if self._options.space_around_combinator:
self._output.space_before_token = True
self.print_string(self._ch)
self._output.space_before_token = True
else:
printer.print_string(self.ch)
self.print_string(self._ch)
self.eatWhitespace()
# squash extra whitespace
if self.ch and bool(whitespaceChar.search(self.ch)):
self.ch = ''
elif self.ch == ']':
printer.print_string(self.ch)
elif self.ch == '[':
printer.preserveSingleSpace(isAfterSpace)
printer.print_string(self.ch)
elif self.ch == '=':
if self._ch and bool(whitespaceChar.search(self._ch)):
self._ch = ''
elif self._ch == ']':
self.print_string(self._ch)
elif self._ch == '[':
self.preserveSingleSpace(isAfterSpace)
self.print_string(self._ch)
elif self._ch == '=':
# no whitespace before or after
self.eatWhitespace()
printer.print_string('=')
if bool(whitespaceChar.search(self.ch)):
self.ch = ''
elif self.ch == '!': # !important
printer.print_string(' ')
printer.print_string(self.ch)
self.print_string('=')
if bool(whitespaceChar.search(self._ch)):
self._ch = ''
elif self._ch == '!': # !important
self.print_string(' ')
self.print_string(self._ch)
else:
printer.preserveSingleSpace(isAfterSpace)
printer.print_string(self.ch)
self.preserveSingleSpace(isAfterSpace)
self.print_string(self._ch)
sweet_code = output.get_code(self.opts.end_with_newline, self.opts.eol)
sweet_code = self._output.get_code(self._options.eol)
return sweet_code

View File

@ -23,40 +23,18 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from jsbeautifier.core.options import Options as BaseOptions
class BeautifierOptions:
def __init__(self):
self.indent_size = 4
self.indent_char = ' '
self.indent_with_tabs = False
self.preserve_newlines = False
self.selector_separator_newline = True
self.end_with_newline = False
self.newline_between_rules = True
self.space_around_combinator = False
self.eol = 'auto'
class BeautifierOptions(BaseOptions):
def __init__(self, options=None):
BaseOptions.__init__(self, options, 'css')
self.css = None
self.js = None
self.html = None
self.selector_separator_newline = self._get_boolean('selector_separator_newline', True)
self.newline_between_rules = self._get_boolean('newline_between_rules', True)
# deprecated
self.space_around_selector_separator = False
space_around_selector_separator = self._get_boolean('space_around_selector_separator')
def __repr__(self):
return """indent_size = %d
indent_char = [%s]
indent_with_tabs = [%s]
preserve_newlines = [%s]
separate_selectors_newline = [%s]
end_with_newline = [%s]
newline_between_rules = [%s]
space_around_combinator = [%s]
""" % (self.indent_size,
self.indent_char,
self.indent_with_tabs,
self.preserve_newlines,
self.selector_separator_newline,
self.end_with_newline,
self.newline_between_rules,
self.space_around_combinator)
# Continue to accept deprecated option
self.space_around_combinator = self._get_boolean('space_around_combinator') or \
space_around_selector_separator

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,34 @@
#! /usr/bin/env python
# To run this: ./js-beautify/tools/python-dev python ./python/js-beautify-profile
import sys
import unittest
import pstats
from pstats import SortKey
# Speedup things...
try:
import cProfile as profile
except ImportError:
import profile
import os
import copy
import jsbeautifier
options = jsbeautifier.default_options()
options.wrap_line_length = 80
def beautifier_test_github_min():
jsbeautifier.beautify(github_min, options)
def run():
sys.argv.append('discover')
unittest.main()
if __name__ == '__main__':
dirname = os.path.dirname(os.path.abspath(__file__))
github_min_file = os.path.join(
dirname, "../", "test/resources/github-min.js")
github_min = copy.copy(''.join(open( github_min_file).readlines()))
profile.run('beautifier_test_github_min()',
os.path.join(dirname, "../", 'build/jsbstats'))
profile.run('run()')
p = pstats.Stats(os.path.join(dirname, "../", 'build/jsbstats'))
p.strip_dirs().sort_stats(SortKey.CUMULATIVE).print_stats()

View File

@ -9,7 +9,7 @@ import errno
import copy
from jsbeautifier.__version__ import __version__
from jsbeautifier.javascript.options import BeautifierOptions
from jsbeautifier.javascript.beautifier import Beautifier, sanitizeOperatorPosition
from jsbeautifier.javascript.beautifier import Beautifier
#
# The MIT License (MIT)
@ -37,9 +37,9 @@ from jsbeautifier.javascript.beautifier import Beautifier, sanitizeOperatorPosit
# SOFTWARE.
#
# Originally written by Einar Lielmanis et al.,
# Conversion to python by Einar Lielmanis, einar@jsbeautifier.org,
# Conversion to python by Einar Lielmanis, einar@beautifier.io,
# Parsing improvement for brace-less and semicolon-less statements
# by Liam Newman <bitwiseman@gmail.com>
# by Liam Newman <bitwiseman@beautifier.io>
# Python is not my native language, feel free to push things around.
#
# Use either from command line (script displays its usage when run
@ -62,6 +62,9 @@ from jsbeautifier.javascript.beautifier import Beautifier, sanitizeOperatorPosit
# Here are the available options: (read source)
class MissingInputStreamError(Exception):
pass
def default_options():
return BeautifierOptions()
@ -108,22 +111,14 @@ def set_file_editorconfig_opts(filename, js_options):
# do not error on bad editor config
print("Error loading EditorConfig. Ignoring.", file=sys.stderr)
def beautify_file(file_name, opts=default_options()):
input_string = ''
if file_name == '-': # stdin
try:
if sys.stdin.isatty():
raise Exception()
if sys.stdin.isatty():
raise MissingInputStreamError()
stream = sys.stdin
input_string = ''.join(stream.readlines())
except Exception:
print(
"Must pipe input or define at least one file.\n",
file=sys.stderr)
usage(sys.stderr)
raise
stream = sys.stdin
input_string = ''.join(stream.readlines())
else:
stream = io.open(file_name, 'rt', newline='')
input_string = ''.join(stream.readlines())
@ -135,7 +130,7 @@ def usage(stream=sys.stdout):
print("jsbeautifier.py@" + __version__ + """
Javascript beautifier (http://jsbeautifier.org/)
Javascript beautifier (https://beautifier.io/)
Usage: jsbeautifier.py [options] <infile>
@ -157,6 +152,7 @@ Output options:
-E, --space-in-empty-paren Add a single space inside empty paren, ie. f( )
-j, --jslint-happy More jslint-compatible output
-a, --space-after-anon-function Add a space before an anonymous function's parens, ie. function ()
--space-after-named-function Add a space before a named function's parens, i.e. function example ()
-b, --brace-style=collapse Brace style (collapse, expand, end-expand, none)(,preserve-inline)
-k, --keep-array-indentation Keep array indentation.
-r, --replace Write output in-place, replacing input
@ -222,7 +218,7 @@ def main():
'space-in-paren', 'space-in-empty-paren', 'jslint-happy', 'space-after-anon-function',
'brace-style=', 'keep-array-indentation', 'indent-level=', 'unescape-strings',
'help', 'usage', 'stdin', 'eval-code', 'indent-with-tabs', 'keep-function-indentation', 'version',
'e4x', 'end-with-newline', 'comma-first', 'operator-position=', 'wrap-line-length', 'editorconfig'])
'e4x', 'end-with-newline', 'comma-first', 'operator-position=', 'wrap-line-length', 'editorconfig', 'space-after-named-function'])
except getopt.GetoptError as ex:
print(ex, file=sys.stderr)
return usage(sys.stderr)
@ -262,6 +258,8 @@ def main():
js_options.jslint_happy = True
elif opt in ('--space_after_anon_function', '-a'):
js_options.space_after_anon_function = True
elif opt in ('--space_after_named_function'):
js_options.space_after_named_function = True
elif opt in ('--eval-code'):
js_options.eval_code = True
elif opt in ('--brace-style', '-b'):
@ -275,7 +273,7 @@ def main():
elif opt in ('--comma-first', '-C'):
js_options.comma_first = True
elif opt in ('--operator-position', '-O'):
js_options.operator_position = sanitizeOperatorPosition(arg)
js_options.operator_position = arg
elif opt in ('--wrap-line-length ', '-w'):
js_options.wrap_line_length = int(arg)
elif opt in ('--stdin', '-i'):
@ -336,6 +334,19 @@ def main():
six = __import__("six")
f.write(six.u(pretty))
except MissingInputStreamError:
print(
"Must pipe input or define at least one file.\n",
file=sys.stderr)
usage(sys.stderr)
return 1
except UnicodeError as ex:
print("Error while decoding input or encoding output:",
file=sys.stderr)
print(ex, file=sys.stderr)
return 1
except Exception as ex:
print(ex, file=sys.stderr)
return 1

View File

@ -1 +1 @@
__version__ = '1.8.0-rc4'
__version__ = '1.8.6'

View File

@ -23,6 +23,122 @@
# SOFTWARE.
import copy
import re
from collections import namedtuple
class Options:
def __init__(self, options=None, merge_child_field=None):
self.css = None
self.js = None
self.html = None
options = _mergeOpts(options, merge_child_field)
self.raw_options = _normalizeOpts(options)
# Support passing the source text back with no change
self.disabled = self._get_boolean('disabled')
self.eol = self._get_characters('eol', 'auto')
self.end_with_newline = self._get_boolean('end_with_newline')
self.indent_size = self._get_number('indent_size', 4)
self.indent_char = self._get_characters('indent_char', ' ')
self.indent_level = self._get_number('indent_level')
self.preserve_newlines = self._get_boolean('preserve_newlines', True)
# TODO: fix difference in js and python
self.max_preserve_newlines = self.max_preserve_newlines = self._get_number('max_preserve_newlines', 10)
if not self.preserve_newlines:
self.max_preserve_newlines = 0
self.indent_with_tabs = self._get_boolean('indent_with_tabs')
if self.indent_with_tabs:
self.indent_char = '\t'
self.indent_size = 1
# Backwards compat with 1.3.x
self.wrap_line_length = self._get_number('wrap_line_length', self._get_number('max_char'))
def _get_array(self, name, default_value=[]):
option_value = getattr(self.raw_options, name, default_value)
result = []
if isinstance(option_value, list):
result = copy.copy(option_value)
elif isinstance(option_value, str):
result = re.compile(r"[^a-zA-Z0-9_/\-]+").split(option_value)
return result
def _get_boolean(self, name, default_value=False):
option_value = getattr(self.raw_options, name, default_value)
result = False
try:
result = bool(option_value)
except ValueError:
pass
return result
def _get_characters(self, name, default_value=''):
option_value = getattr(self.raw_options, name, default_value)
result = ''
if isinstance(option_value, str):
result = option_value.replace('\\r', '\r').replace('\\n', '\n').replace('\\t', '\t')
return result
def _get_number(self, name, default_value=0):
option_value = getattr(self.raw_options, name, default_value)
result = 0
try:
result = int(option_value)
except ValueError:
pass
return result
def _get_selection(self, name, selection_list, default_value=None):
result = self._get_selection_list(name, selection_list, default_value)
if len(result) != 1:
raise ValueError(
"Invalid Option Value: The option '" + name + "' can only be one of the following values:\n" +
str(selection_list) +
"\nYou passed in: '" +
str(getattr(self.raw_options, name, None)) +
"'")
return result[0]
def _get_selection_list(self, name, selection_list, default_value=None):
if not selection_list:
raise ValueError("Selection list cannot be empty.")
default_value = default_value or [selection_list[0]]
if not self._is_valid_selection(default_value, selection_list):
raise ValueError("Invalid Default Value!")
result = self._get_array(name, default_value)
if not self._is_valid_selection(result, selection_list):
raise ValueError(
"Invalid Option Value: The option '" + name + "' can contain only the following values:\n" +
str(selection_list) +
"\nYou passed in: '" +
str(getattr(self.raw_options, name, None)) +
"'")
return result
def _is_valid_selection(self, result, selection_list):
if len(result) == 0 or len(selection_list) == 0:
return False
for item in result:
if item not in selection_list:
return False
return True
# merges child options up with the parent options object
# Example: obj = {a: 1, b: {a: 2}}
@ -31,7 +147,17 @@ import copy
# Returns: {a: 2, b: {a: 2}}
def mergeOpts(options, childFieldName):
def _mergeOpts(options, childFieldName):
if options is None:
options = {}
if isinstance(options, tuple):
options = dict(options)
if isinstance(options, dict):
options = _normalizeOpts(options)
options = namedtuple("CustomOptions", options.keys())(*options.values())
finalOpts = copy.copy(options)
local = getattr(finalOpts, childFieldName, None)
@ -41,3 +167,20 @@ def mergeOpts(options, childFieldName):
setattr(finalOpts, key, local[key])
return finalOpts
def _normalizeOpts(options):
convertedOpts = copy.copy(options)
if isinstance(convertedOpts, dict):
option_keys = list(convertedOpts.keys())
for key in option_keys:
if '-' in key:
del convertedOpts[key]
convertedOpts[key.replace('-', '_')] = options[key]
else:
option_keys = list(getattr(convertedOpts, '__dict__', {}))
for key in option_keys:
if '-' in key:
delattr(convertedOpts, key)
setattr(convertedOpts, key.replace('-', '_'), getattr(options, key, None))
return convertedOpts

View File

@ -29,24 +29,31 @@ import re
__all__ = ["Output"]
class OutputLine:
def __init__(self, parent):
self.__parent = parent
self.__character_count = 0
self.__indent_count = -1
self.__alignment_count = 0
self.__items = []
def item(self, index):
return self.__items[index]
def get_character_count(self):
return self.__character_count
def is_empty(self):
return len(self.__items) == 0
def set_indent(self, level):
def set_indent(self, indent=0, alignment=0):
self.__indent_count = indent
self.__alignment_count = alignment
self.__character_count = self.__parent.baseIndentLength + \
level * self.__parent.indent_length
self.__indent_count = level
self.__alignment_count + \
self.__indent_count * self.__parent.indent_length
def last(self):
if not self.is_empty():
@ -79,50 +86,88 @@ class OutputLine:
result = ''
if not self.is_empty():
if self.__indent_count >= 0:
result = self.__parent.indent_cache[self.__indent_count]
result = self.__parent.get_indent_string(self.__indent_count)
if self.__alignment_count >= 0:
result += self.__parent.get_alignment_string(
self.__alignment_count)
result += ''.join(self.__items)
return result
class Output:
def __init__(self, indent_string, baseIndentString=''):
class IndentCache:
def __init__(self, base_string, level_string):
self.__cache = [base_string]
self.__level_string = level_string
self.indent_string = indent_string
self.baseIndentString = baseIndentString
self.indent_cache = [baseIndentString]
def __ensure_cache(self, level):
while level >= len(self.__cache):
self.__cache.append(
self.__cache[-1] + self.__level_string)
def get_level_string(self, level):
self.__ensure_cache(level)
return self.__cache[level]
class Output:
def __init__(self, options, baseIndentString=''):
indent_string = options.indent_char
if options.indent_size > 0:
indent_string = options.indent_char * options.indent_size
# Set to null to continue support for auto detection of base levelself.
if options.indent_level > 0:
baseIndentString = options.indent_level * indent_string
self.__indent_cache = IndentCache(baseIndentString, indent_string)
self.__alignment_cache = IndentCache('', ' ')
self.baseIndentLength = len(baseIndentString)
self.indent_length = len(indent_string)
self.raw = False
self.lines = []
self._end_with_newline = options.end_with_newline
self.__lines = []
self.previous_line = None
self.current_line = None
self.space_before_token = False
self.add_outputline()
def add_outputline(self):
self.__add_outputline()
def __add_outputline(self):
self.previous_line = self.current_line
self.current_line = OutputLine(self)
self.lines.append(self.current_line)
self.__lines.append(self.current_line)
def get_line_number(self):
return len(self.lines)
return len(self.__lines)
def get_indent_string(self, level):
return self.__indent_cache.get_level_string(level)
def get_alignment_string(self, level):
return self.__alignment_cache.get_level_string(level)
def is_empty(self):
return self.previous_line is None and self.current_line.is_empty()
def add_new_line(self, force_newline=False):
if len(self.lines) == 1 and self.just_added_newline():
# no newline on start of file
# never newline at the start of file
# otherwise, newline only if we didn't just add one or we're forced
if self.is_empty() or \
(not force_newline and self.just_added_newline()):
return False
if force_newline or not self.just_added_newline():
if not self.raw:
self.add_outputline()
return True
return False
# if raw output is enabled, don't print additional newlines,
# but still return True as though you had
if not self.raw:
self.__add_outputline()
return True
def get_code(self, end_with_newline, eol):
sweet_code = "\n".join(line.toString() for line in self.lines)
def get_code(self, eol):
sweet_code = "\n".join(line.toString() for line in self.__lines)
sweet_code = re.sub('[\r\n\t ]+$', '', sweet_code)
if end_with_newline:
if self._end_with_newline:
sweet_code += '\n'
if not eol == '\n':
@ -130,21 +175,17 @@ class Output:
return sweet_code
def set_indent(self, level):
def set_indent(self, indent=0, alignment=0):
# Never indent your first output indent at the start of the file
if len(self.lines) > 1:
while level >= len(self.indent_cache):
self.indent_cache.append(
self.indent_cache[-1] + self.indent_string)
self.current_line.set_indent(level)
if len(self.__lines) > 1:
self.current_line.set_indent(indent, alignment)
return True
self.current_line.set_indent(0)
self.current_line.set_indent()
return False
def add_raw_token(self, token):
for _ in range(token.newlines):
self.add_outputline()
self.__add_outputline()
self.current_line.push(token.whitespace_before)
self.current_line.push(token.text)
@ -159,17 +200,22 @@ class Output:
self.current_line.push(' ')
self.space_before_token = False
def remove_indent(self, index):
while index < len(self.__lines):
self.__lines[index].remove_indent()
index += 1
def trim(self, eat_newlines=False):
self.current_line.trim()
while eat_newlines and len(
self.lines) > 1 and self.current_line.is_empty():
self.lines.pop()
self.current_line = self.lines[-1]
self.__lines) > 1 and self.current_line.is_empty():
self.__lines.pop()
self.current_line = self.__lines[-1]
self.current_line.trim()
if len(self.lines) > 1:
self.previous_line = self.lines[-2]
if len(self.__lines) > 1:
self.previous_line = self.__lines[-2]
else:
self.previous_line = None
@ -177,11 +223,18 @@ class Output:
return self.current_line.is_empty()
def just_added_blankline(self):
if self.just_added_newline():
if len(self.lines) == 1:
return True
return self.is_empty() or \
(self.current_line.is_empty() and self.previous_line.is_empty())
line = self.lines[-2]
return line.is_empty()
return False
def ensure_empty_line_above(self, starts_with, ends_with):
index = len(self.__lines) - 2
while index >= 0:
potentialEmptyLine = self.__lines[index]
if potentialEmptyLine.is_empty():
break
elif not potentialEmptyLine.item(0).startswith(starts_with) and \
potentialEmptyLine.item(-1) != ends_with:
self.__lines.insert(index + 1, OutputLine(self))
self.previous_line = self.__lines[-2]
break
index -= 1

View File

@ -36,5 +36,8 @@ class Token:
self.newlines = newlines
self.whitespace_before = whitespace_before
self.parent = None
self.next = None
self.previous = None
self.opened = None
self.closed = None
self.directives = None

View File

@ -42,89 +42,100 @@ TOKEN = TokenTypes()
class Tokenizer:
def __init__(self, input_string):
import jsbeautifier.core.acorn as acorn
self.acorn = acorn
def __init__(self, input_string, options):
self._input = InputScanner(input_string)
self._tokens = None
self._newline_count = 0
self._whitespace_before_token = ''
self._options = options
self.__tokens = None
self.__newline_count = 0
self.__whitespace_before_token = ''
self._whitespace_pattern = re.compile(
self.acorn.six.u(r'[\n\r\u2028\u2029\t ]+'))
self._newline_pattern = re.compile(
self.acorn.six.u(r'([\t ]*)(\r\n|[\n\r\u2028\u2029])?'))
self._whitespace_pattern = re.compile(r'[\n\r\t ]+')
self._newline_pattern = re.compile(r'([^\n\r]*)(\r\n|[\n\r])?')
def tokenize(self):
self._input.restart()
self._tokens = TokenStream()
self.__tokens = TokenStream()
current = None
last = Token(TOKEN.START,'')
previous = Token(TOKEN.START,'')
open_token = None
open_stack = []
comments = TokenStream()
while last.type != TOKEN.EOF:
current = self.get_next_token(last)
while previous.type != TOKEN.EOF:
current = self.__get_next_token_with_comments(previous, open_token)
while self.is_comment(current):
if self._is_opening(current):
open_stack.append(open_token)
open_token = current
elif open_token is not None and \
self._is_closing(current, open_token):
current.opened = open_token
open_token.closed = current
open_token = open_stack.pop()
current.parent = open_token
self.__tokens.add(current)
previous = current
return self.__tokens
def __get_next_token_with_comments(self, previous, open_token):
current = self._get_next_token(previous, open_token)
if self._is_comment(current):
comments = TokenStream()
while self._is_comment(current):
comments.add(current)
current = self.get_next_token(last)
current = self._get_next_token(previous, open_token)
if not comments.isEmpty():
current.comments_before = comments
comments = TokenStream()
if self.is_opening(current):
current.parent = last
open_stack.append(open_token)
open_token = current
elif open_token is not None and self.is_closing(current, open_token):
current.parent = open_token.parent
current.opened = open_token
open_token = open_stack.pop()
current.parent = open_token
current.previous = previous
previous.next = current
self._tokens.add(current)
last = current
return self._tokens
return current
def reset(self):
def _is_first_token(self):
return self.__tokens.isEmpty()
def _reset(self):
pass
def get_next_token(self, last_token):
self.readWhitespace()
def _get_next_token(self, previous_token, open_token):
self._readWhitespace()
resulting_string = self._input.read(re.compile(r'.+'))
if resulting_string:
return self.create_token(TOKEN.RAW, resulting_string)
return self._create_token(TOKEN.RAW, resulting_string)
else:
return self.create_token(TOKEN.EOF, '')
return self._create_token(TOKEN.EOF, '')
def is_comment(self, current_token):
def _is_comment(self, current_token):
return False
def is_opening(self, current_token):
def _is_opening(self, current_token):
return False
def is_closing(self, current_token, open_token):
def _is_closing(self, current_token, open_token):
return False
def create_token(self, token_type, text):
token = Token(token_type, text, self._newline_count, self._whitespace_before_token)
self._newline_count = 0
self._whitespace_before_token = ''
def _create_token(self, token_type, text):
token = Token(token_type, text,
self.__newline_count, self.__whitespace_before_token)
self.__newline_count = 0
self.__whitespace_before_token = ''
return token
def readWhitespace(self):
def _readWhitespace(self):
resulting_string = self._input.read(self._whitespace_pattern)
if resulting_string != '':
if resulting_string == ' ':
self._whitespace_before_token = resulting_string
else:
for nextMatch in self._newline_pattern.findall(resulting_string):
if nextMatch[1] != '':
self._newline_count += 1
else:
self._whitespace_before_token = nextMatch[0]
break
if resulting_string == ' ':
self.__whitespace_before_token = resulting_string
elif resulting_string != '':
for nextMatch in self._newline_pattern.findall(resulting_string):
if nextMatch[1] == '':
self.__whitespace_before_token = nextMatch[0]
break
self.__newline_count += 1

View File

@ -29,42 +29,42 @@ from ..core.token import Token
class TokenStream:
def __init__(self, parent_token=None):
self._tokens = []
self._tokens_length = len(self._tokens)
self._position = 0
self._parent_token = parent_token
self.__tokens = []
self.__tokens_length = len(self.__tokens)
self.__position = 0
self.__parent_token = parent_token
def restart(self):
self._position = 0
self.__position = 0
def isEmpty(self):
return self._tokens_length == 0
return self.__tokens_length == 0
def hasNext(self):
return self._position < self._tokens_length
return self.__position < self.__tokens_length
def next(self):
if self.hasNext():
val = self._tokens[self._position]
self._position += 1
val = self.__tokens[self.__position]
self.__position += 1
return val
else:
raise StopIteration
def peek(self, index=0):
val = None
index += self._position
if index >= 0 and index < self._tokens_length:
val = self._tokens[index]
index += self.__position
if index >= 0 and index < self.__tokens_length:
val = self.__tokens[index]
return val
def add(self, token):
if self._parent_token:
token.parent = self._parent_token
if self.__parent_token:
token.parent = self.__parent_token
self._tokens.append(token)
self._tokens_length += 1
self.__tokens.append(token)
self.__tokens_length += 1
def __iter__(self):
self.restart()

View File

@ -18,42 +18,41 @@ six = __import__("six")
# ## Character categories
# acorn used char codes to squeeze the last bit of performance out
# Beautifier is okay without that, so we're using regex
# permit $ (36) and @ (64). @ is used in ES7 decorators.
# 65 through 91 are uppercase letters.
# permit _ (95).
# 97 through 123 are lowercase letters.
_baseASCIIidentifierStartChars = six.u(r"\x24\x40\x41-\x5a\x5f\x61-\x7a")
# inside an identifier @ is not allowed but 0-9 are.
_baseASCIIidentifierChars = six.u(r"\x24\x30-\x39\x41-\x5a\x5f\x61-\x7a")
# Big ugly regular expressions that match characters in the
# whitespace, identifier, and identifier-start categories. These
# are only applied when a character is found to actually have a
# code point above 128.
# IMPORTANT: These strings must be run through six to handle \u chars
_nonASCIIwhitespace = re.compile(
six.u(r"[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]"))
_baseASCIIidentifierStartChars = six.u(r"\x24\x40\x41-\x5a\x5f\x61-\x7a")
_nonASCIIidentifierStartChars = six.u(r"\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc")
_baseASCIIidentifierChars = six.u(r"\x24\x30-\x39\x41-\x5a\x5f\x61-\x7a")
_nonASCIIidentifierChars = six.u(r"\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f")
#_nonASCIIidentifierStart = re.compile("[" + _nonASCIIidentifierStartChars + "]")
#_nonASCIIidentifier = re.compile("[" + _nonASCIIidentifierStartChars + _nonASCIIidentifierChars + "]")
_identifierStart = re.compile(
"[" +
_baseASCIIidentifierStartChars +
_nonASCIIidentifierStartChars +
"]")
_identifierChars = re.compile(
"[" +
_baseASCIIidentifierChars +
_nonASCIIidentifierStartChars +
_nonASCIIidentifierChars +
"]")
_identifierStart = six.u("[") + \
_baseASCIIidentifierStartChars + \
_nonASCIIidentifierStartChars + \
six.u("]")
_identifierChars = six.u("[") + \
_baseASCIIidentifierChars + \
_nonASCIIidentifierStartChars + \
_nonASCIIidentifierChars + \
six.u("]*")
identifier = re.compile(
"[" +
_baseASCIIidentifierStartChars +
_nonASCIIidentifierStartChars +
"][" +
_baseASCIIidentifierChars +
_nonASCIIidentifierStartChars +
_nonASCIIidentifierChars +
"]*")
identifier = re.compile(_identifierStart + _identifierChars)
_nonASCIIwhitespace = re.compile(
six.u(r"[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]"))
# Whether a single character denotes a newline.
# IMPORTANT: This string must be run through six to handle \u chars
@ -66,39 +65,4 @@ newline = re.compile(six.u(r"[\n\r\u2028\u2029]"))
# in python they are the same, different methods are called on them
# IMPORTANT: This string must be run through six to handle \u chars
lineBreak = re.compile(six.u(r"\r\n|[\n\r\u2028\u2029]"))
allLineBreaks = lineBreak
# Test whether a given character code starts an identifier.
def isIdentifierStart(code):
# if code < 65:
# return code in [36, 64] # permit $ (36) and @ (64). @ is used in ES7 decorators.
# if code < 91:
# return True # 65 through 91 are uppercase letters
# if code < 97:
# return code == 95 # permit _ (95)
# if code < 123:
# return True # 97 through 123 are lowercase letters
# return code >= 0xaa and _nonASCIIidentifierStart.match(six.unichr(code))
# != None
return bool(_identifierStart.match(six.unichr(code)))
# Test whether a given character is part of an identifier.
def isIdentifierChar(code):
# if code < 48:
# return code == 36
# if code < 58:
# return True
# if code < 65:
# return False
# if code < 91:
# return True
# if code < 97:
# return code == 95
# if code < 123:
# return True
# return code >= 0xaa and _nonASCIIidentifier.match(six.unichr(code)) !=
# None
return bool(_identifierChars.match(six.unichr(code)))
allLineBreaks = lineBreak

File diff suppressed because it is too large Load Diff

View File

@ -23,66 +23,72 @@
# SOFTWARE.
class BeautifierOptions:
def __init__(self):
self.indent_size = 4
self.indent_char = ' '
self.indent_with_tabs = False
self.eol = 'auto'
self.preserve_newlines = True
self.max_preserve_newlines = 10
self.space_in_paren = False
self.space_in_empty_paren = False
self.e4x = False
self.jslint_happy = False
self.space_after_anon_function = False
self.brace_style = 'collapse'
self.keep_array_indentation = False
self.space_before_conditional = True
self.keep_function_indentation = False
self.eval_code = False
self.unescape_strings = False
self.wrap_line_length = 0
self.unindent_chained_methods = False
self.break_chained_methods = False
self.end_with_newline = False
self.comma_first = False
self.operator_position = 'before-newline'
from ..core.options import Options as BaseOptions
OPERATOR_POSITION = [
'before-newline',
'after-newline',
'preserve-newline'
]
class BeautifierOptions(BaseOptions):
def __init__(self, options=None):
BaseOptions.__init__(self, options, 'js')
self.css = None
self.js = None
self.html = None
# For testing of beautify ignore:start directive
# compatibility, re
raw_brace_style = getattr(self.raw_options, 'brace_style', None)
if raw_brace_style == "expand-strict": # graceful handling of deprecated option
setattr(self.raw_options, 'brace_style', "expand")
elif raw_brace_style == "collapse-preserve-inline": # graceful handling of deprecated option
setattr(self.raw_options, 'brace_style', "collapse,preserve-inline")
# elif bool(self.raw_options.braces_on_own_line): # graceful handling of deprecated option
# raw_brace_style = "expand": "collapse"
# elif raw_brace_style is None: # Nothing exists to set it
# setattr(self.raw_options, 'brace_style', "collapse")
# preserve-inline in delimited string will trigger brace_preserve_inline, everything
# else is considered a brace_style and the last one only will have an effect
brace_style_split = self._get_selection_list('brace_style', ['collapse', 'expand', 'end-expand', 'none', 'preserve-inline'])
# preserve-inline in delimited string will trigger brace_preserve_inline
# Everything else is considered a brace_style and the last one only will
# have an effect
# specify defaults in case one half of meta-option is missing
self.brace_preserve_inline = False
self.brace_style = "collapse"
for bs in brace_style_split:
if bs == "preserve-inline":
self.brace_preserve_inline = True
else:
self.brace_style = bs
self.unindent_chained_methods = self._get_boolean('unindent_chained_methods')
self.break_chained_methods = self._get_boolean('break_chained_methods')
self.space_in_paren = self._get_boolean('space_in_paren')
self.space_in_empty_paren = self._get_boolean('space_in_empty_paren')
self.jslint_happy = self._get_boolean('jslint_happy')
self.space_after_anon_function = self._get_boolean('space_after_anon_function')
self.space_after_named_function = self._get_boolean('space_after_named_function')
self.keep_array_indentation = self._get_boolean('keep_array_indentation')
self.space_before_conditional = self._get_boolean('space_before_conditional', True)
self.unescape_strings = self._get_boolean('unescape_strings')
self.e4x = self._get_boolean('e4x')
self.comma_first = self._get_boolean('comma_first')
self.operator_position = self._get_selection('operator_position', OPERATOR_POSITION)
# For testing of beautify preserve:start directive
self.test_output_raw = False
self.editorconfig = False
def __repr__(self):
return \
"""indent_size = %d
indent_char = [%s]
preserve_newlines = %s
max_preserve_newlines = %d
space_in_paren = %s
jslint_happy = %s
space_after_anon_function = %s
indent_with_tabs = %s
brace_style = %s
keep_array_indentation = %s
eval_code = %s
wrap_line_length = %s
unescape_strings = %s
""" % (self.indent_size,
self.indent_char,
self.preserve_newlines,
self.max_preserve_newlines,
self.space_in_paren,
self.jslint_happy,
self.space_after_anon_function,
self.indent_with_tabs,
self.brace_style,
self.keep_array_indentation,
self.eval_code,
self.wrap_line_length,
self.unescape_strings,
)
# force opts.space_after_anon_function to true if opts.jslint_happy
if self.jslint_happy:
self.space_after_anon_function = True
self.eval_code = False

View File

@ -114,54 +114,65 @@ class Tokenizer(BaseTokenizer):
positionable_operators = positionable_operators
line_starters = line_starters
def __init__(self, input_string, opts, indent_string):
BaseTokenizer.__init__(self, input_string)
def __init__(self, input_string, opts):
BaseTokenizer.__init__(self, input_string, opts)
# This is not pretty, but given how we did the version import
# it is the only way to do this without having setup.py fail on a missing
# six dependency.
self._six = __import__("six")
import jsbeautifier.javascript.acorn as acorn
self.acorn = acorn
self.opts = opts
self.indent_string = indent_string
self.in_html_comment = False
self.has_char_escapes = False
# comment ends just before nearest linefeed or end of file
# IMPORTANT: This string must be run through six to handle \u chars
self._whitespace_pattern = re.compile(
self._six.u(r'[\n\r\u2028\u2029\t\u000B\u00A0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff ]+'))
self._newline_pattern = re.compile(
self._six.u(r'([^\n\r\u2028\u2029]*)(\r\n|[\n\r\u2028\u2029])?'))
# // comment ends just before nearest linefeed or end of file
self.comment_pattern = re.compile(
self.acorn.six.u(r'//([^\n\r\u2028\u2029]*)'))
self._six.u(r'//([^\n\r\u2028\u2029]*)'))
def reset(self):
def _reset(self):
self.in_html_comment = False
def is_comment(self, current_token):
def _is_comment(self, current_token):
return current_token.type == TOKEN.COMMENT or \
current_token.type == TOKEN.BLOCK_COMMENT or \
current_token.type == TOKEN.UNKNOWN
def is_opening(self, current_token):
def _is_opening(self, current_token):
return current_token.type == TOKEN.START_BLOCK or current_token.type == TOKEN.START_EXPR
def is_closing(self, current_token, open_token):
def _is_closing(self, current_token, open_token):
return (current_token.type == TOKEN.END_BLOCK or current_token.type == TOKEN.END_EXPR) and \
(open_token is not None and (
(current_token.text == ']' and open_token.text == '[') or
(current_token.text == ')' and open_token.text == '(') or
(current_token.text == '}' and open_token.text == '{')))
def get_next_token(self, last_token):
self.readWhitespace()
def _get_next_token(self, previous_token, open_token):
self._readWhitespace()
token = None
c = self._input.peek()
token = token or self._read_singles(c)
token = token or self._read_word(last_token)
token = token or self._read_word(previous_token)
token = token or self._read_comment(c)
token = token or self._read_string(c)
token = token or self._read_regexp(c, last_token)
token = token or self._read_xml(c, last_token)
token = token or self._read_regexp(c, previous_token)
token = token or self._read_xml(c, previous_token)
token = token or self._read_non_javascript(c)
token = token or self._read_punctuation()
token = token or self.create_token(TOKEN.UNKNOWN, self._input.next())
token = token or self._create_token(TOKEN.UNKNOWN, self._input.next())
return token
@ -169,45 +180,45 @@ class Tokenizer(BaseTokenizer):
token = None
if c is None:
token = self.create_token(TOKEN.EOF, '')
token = self._create_token(TOKEN.EOF, '')
elif c == '(' or c == '[':
token = self.create_token(TOKEN.START_EXPR, c)
token = self._create_token(TOKEN.START_EXPR, c)
elif c == ')' or c == ']':
token = self.create_token(TOKEN.END_EXPR, c)
token = self._create_token(TOKEN.END_EXPR, c)
elif c == '{':
token = self.create_token(TOKEN.START_BLOCK, c)
token = self._create_token(TOKEN.START_BLOCK, c)
elif c == '}':
token = self.create_token(TOKEN.END_BLOCK, c)
token = self._create_token(TOKEN.END_BLOCK, c)
elif c == ';':
token = self.create_token(TOKEN.SEMICOLON, c)
token = self._create_token(TOKEN.SEMICOLON, c)
elif c == '.' and bool(dot_pattern.match(self._input.peek(1))):
token = self.create_token(TOKEN.DOT, c)
token = self._create_token(TOKEN.DOT, c)
elif c == ',':
token = self.create_token(TOKEN.COMMA, c)
token = self._create_token(TOKEN.COMMA, c)
if token is not None:
self._input.next()
return token
def _read_word(self, last_token):
def _read_word(self, previous_token):
resulting_string = self._input.read(self.acorn.identifier)
if resulting_string != '':
if not (last_token.type == TOKEN.DOT or (
last_token.type == TOKEN.RESERVED and (
last_token.text == 'set' or last_token.text == 'get')
if not (previous_token.type == TOKEN.DOT or (
previous_token.type == TOKEN.RESERVED and (
previous_token.text == 'set' or previous_token.text == 'get')
)) and reserved_word_pattern.match(resulting_string):
if resulting_string == 'in' or resulting_string == 'of':
# in and of are operators, need to hack
return self.create_token(TOKEN.OPERATOR, resulting_string)
return self._create_token(TOKEN.OPERATOR, resulting_string)
return self.create_token(TOKEN.RESERVED, resulting_string)
return self._create_token(TOKEN.RESERVED, resulting_string)
return self.create_token(TOKEN.WORD, resulting_string)
return self._create_token(TOKEN.WORD, resulting_string)
resulting_string = self._input.read(number_pattern)
if resulting_string != '':
return self.create_token(TOKEN.WORD, resulting_string)
return self._create_token(TOKEN.WORD, resulting_string)
def _read_comment(self, c):
token = None
@ -220,12 +231,12 @@ class Tokenizer(BaseTokenizer):
if directives and directives.get('ignore') == 'start':
comment += directives_core.readIgnored(self._input)
comment = re.sub(self.acorn.allLineBreaks, '\n', comment)
token = self.create_token(TOKEN.BLOCK_COMMENT, comment)
token = self._create_token(TOKEN.BLOCK_COMMENT, comment)
token.directives = directives
elif self._input.peek(1) == '/': # peek // comment
comment = self._input.read(self.comment_pattern)
token = self.create_token(TOKEN.COMMENT, comment)
token = self._create_token(TOKEN.COMMENT, comment)
return token
@ -241,7 +252,7 @@ class Tokenizer(BaseTokenizer):
else:
resulting_string = self.parse_string(resulting_string, c)
if self.has_char_escapes and self.opts.unescape_strings:
if self.has_char_escapes and self._options.unescape_strings:
resulting_string = self.unescape_string(resulting_string)
if self._input.peek() == c :
@ -250,13 +261,13 @@ class Tokenizer(BaseTokenizer):
resulting_string = re.sub(
self.acorn.allLineBreaks, '\n', resulting_string)
return self.create_token(TOKEN.STRING, resulting_string)
return self._create_token(TOKEN.STRING, resulting_string)
return None
def _read_regexp(self, c, last_token):
def _read_regexp(self, c, previous_token):
if c == '/' and self.allowRegExOrXML(last_token):
if c == '/' and self.allowRegExOrXML(previous_token):
# handle regexp
resulting_string = self._input.next()
esc = False
@ -286,14 +297,14 @@ class Tokenizer(BaseTokenizer):
resulting_string += self._input.read(
self.acorn.identifier)
return self.create_token(TOKEN.STRING, resulting_string)
return self._create_token(TOKEN.STRING, resulting_string)
return None
def _read_xml(self, c, last_token):
if self.opts.e4x and c == "<" and self._input.test(
startXmlRegExp) and self.allowRegExOrXML(last_token):
def _read_xml(self, c, previous_token):
if self._options.e4x and c == "<" and self._input.test(
startXmlRegExp) and self.allowRegExOrXML(previous_token):
# handle e4x xml literals
xmlStr = ""
match = self._input.match(xmlRegExp)
@ -325,7 +336,7 @@ class Tokenizer(BaseTokenizer):
xmlStr += self._input.match(re.compile(r'[\s\S]*')).group(0)
xmlStr = re.sub(self.acorn.allLineBreaks, '\n', xmlStr)
return self.create_token(TOKEN.STRING, xmlStr)
return self._create_token(TOKEN.STRING, xmlStr)
return None
@ -335,12 +346,12 @@ class Tokenizer(BaseTokenizer):
if c == '#':
c = self._input.next()
# she-bang
if self._tokens.isEmpty() and self._input.peek() == '!':
if self._is_first_token() and self._input.peek() == '!':
resulting_string = c
while self._input.hasNext() and c != '\n':
c = self._input.next()
resulting_string += c
return self.create_token(TOKEN.UNKNOWN, resulting_string.strip() + '\n')
return self._create_token(TOKEN.UNKNOWN, resulting_string.strip() + '\n')
# Spidermonkey-specific sharp variables for circular references
# https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
@ -364,7 +375,7 @@ class Tokenizer(BaseTokenizer):
self._input.next()
self._input.next()
return self.create_token(TOKEN.WORD, sharp)
return self._create_token(TOKEN.WORD, sharp)
self._input.back()
@ -373,7 +384,7 @@ class Tokenizer(BaseTokenizer):
resulting_string = self._input.read(template_pattern)
if resulting_string:
resulting_string = re.sub(self.acorn.allLineBreaks, '\n', resulting_string)
return self.create_token(TOKEN.STRING, resulting_string)
return self._create_token(TOKEN.STRING, resulting_string)
elif self._input.match(re.compile(r'<\!--')):
c = '<!--'
@ -381,12 +392,12 @@ class Tokenizer(BaseTokenizer):
c += self._input.next()
self.in_html_comment = True
return self.create_token(TOKEN.COMMENT, c)
return self._create_token(TOKEN.COMMENT, c)
elif c == '-' and self.in_html_comment and self._input.match(
re.compile('-->')):
self.in_html_comment = False
return self.create_token(TOKEN.COMMENT, '-->')
return self._create_token(TOKEN.COMMENT, '-->')
return None
@ -395,19 +406,20 @@ class Tokenizer(BaseTokenizer):
resulting_string = self._input.read(punct_pattern)
if resulting_string != '':
if resulting_string == '=':
token = self.create_token(TOKEN.EQUALS, resulting_string)
token = self._create_token(TOKEN.EQUALS, resulting_string)
else:
token = self.create_token(TOKEN.OPERATOR, resulting_string)
token = self._create_token(TOKEN.OPERATOR, resulting_string)
return token
def allowRegExOrXML(self, last_token):
return (last_token.type == TOKEN.RESERVED and last_token.text in ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield']) or \
(last_token.type == TOKEN.END_EXPR and last_token.text == ')' and
last_token.parent and last_token.parent.type == TOKEN.RESERVED and last_token.parent.text in ['if', 'while', 'for']) or \
(last_token.type in [TOKEN.COMMENT, TOKEN.START_EXPR, TOKEN.START_BLOCK, TOKEN.START, TOKEN.END_BLOCK, TOKEN.OPERATOR,
TOKEN.EQUALS, TOKEN.EOF, TOKEN.SEMICOLON, TOKEN.COMMA])
__regexTokens = { TOKEN.COMMENT, TOKEN.START_EXPR, TOKEN.START_BLOCK,
TOKEN.START, TOKEN.END_BLOCK, TOKEN.OPERATOR,
TOKEN.EQUALS, TOKEN.EOF, TOKEN.SEMICOLON, TOKEN.COMMA }
def allowRegExOrXML(self, previous_token):
return (previous_token.type == TOKEN.RESERVED and previous_token.text in {'return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'}) or \
(previous_token.type == TOKEN.END_EXPR and previous_token.text == ')' and
previous_token.opened.previous.type == TOKEN.RESERVED and previous_token.opened.previous.text in {'if', 'while', 'for'}) or \
(previous_token.type in self.__regexTokens )
def parse_string(
self,

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,14 @@ test_cli_common()
exit 1
}
# unicode error - only happens in python
# Note: different exceptions are thrown on different platforms.
$CLI_SCRIPT ../test/resources/unicode-error.js 2>&1 | grep -q "Error while decoding input or encoding output:" || {
$CLI_SCRIPT ../test/resources/unicode-error.js 2>&1
echo "[$CLI_SCRIPT_NAME] Output should be unicode error message."
exit 1
}
$CLI_SCRIPT 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME (with no parameters)] Return code should be error."
exit 1

View File

@ -1,6 +1,6 @@
#
# Unpacker for eval() based packers, a part of javascript beautifier
# by Einar Lielmanis <einar@jsbeautifier.org>
# by Einar Lielmanis <einar@beautifier.io>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#

View File

@ -2,7 +2,7 @@
# simple unpacker/deobfuscator for scripts messed up with
# javascriptobfuscator.com
#
# written by Einar Lielmanis <einar@jsbeautifier.org>
# written by Einar Lielmanis <einar@beautifier.io>
# rewritten in Python by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# Will always return valid javascript: if `detect()` is false, `code` is

View File

@ -1,6 +1,6 @@
#
# deobfuscator for scripts messed up with myobfuscate.com
# by Einar Lielmanis <einar@jsbeautifier.org>
# by Einar Lielmanis <einar@beautifier.io>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#

View File

@ -1,6 +1,6 @@
#
# Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
# by Einar Lielmanis <einar@jsbeautifier.org>
# by Einar Lielmanis <einar@beautifier.io>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#

View File

@ -1,6 +1,6 @@
#
# Trivial bookmarklet/escaped script detector for the javascript beautifier
# written by Einar Lielmanis <einar@jsbeautifier.org>
# written by Einar Lielmanis <einar@beautifier.io>
# rewritten in Python by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# Will always return valid javascript: if `detect()` is false, `code` is

View File

@ -32,8 +32,8 @@ setup(name='jsbeautifier',
long_description=('Beautify, unpack or deobfuscate JavaScript. '
'Handles popular online obfuscators.'),
author='Liam Newman, Einar Lielmanis, et al.',
author_email='team@jsbeautifier.org',
url='http://jsbeautifier.org',
author_email='team@beautifier.io',
url='https://beautifier.io',
entry_points={
'console_scripts': [
'js-beautify = jsbeautifier:main'

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import copy
import cssbeautifier
options = cssbeautifier.default_options()
options.wrap_line_length = 80
data = ''
def beautifier_test_github_css():
cssbeautifier.beautify(data, options)
def report_perf(fn):
import timeit
iter = 5
time = timeit.timeit(
fn +
"()",
setup="from __main__ import " +
fn +
"; gc.enable()",
number=iter)
print(fn + ": " + str(iter / time) + " cycles/sec")
if __name__ == '__main__':
dirname = os.path.dirname(os.path.abspath(__file__))
github_file = os.path.join(
dirname, "../", "test/resources/github.css")
data = copy.copy(''.join(open(github_file).readlines()))
# warm up
beautifier_test_github_css()
report_perf("beautifier_test_github_css")

View File

@ -17,10 +17,13 @@ def beautifier_test_underscore():
def beautifier_test_underscore_min():
jsbeautifier.beautify(data_min, options)
def beautifier_test_github_min():
jsbeautifier.beautify(github_min, options)
def report_perf(fn):
import timeit
iter = 50
iter = 5
time = timeit.timeit(
fn +
"()",
@ -37,12 +40,17 @@ if __name__ == '__main__':
dirname, "../", "test/resources/underscore.js")
underscore_min_file = os.path.join(
dirname, "../", "test/resources/underscore-min.js")
github_min_file = os.path.join(
dirname, "../", "test/resources/github-min.js")
data = copy.copy(''.join(open(underscore_file).readlines()))
data_min = copy.copy(''.join(open(underscore_min_file).readlines()))
github_min = copy.copy(''.join(open( github_min_file).readlines()))
# warm up
beautifier_test_underscore()
beautifier_test_underscore_min()
beautifier_test_github_min()
report_perf("beautifier_test_underscore")
report_perf("beautifier_test_underscore_min")
report_perf("beautifier_test_github_min")

View File

@ -26,6 +26,7 @@
SOFTWARE.
*/
/*jshint unused:false */
/*jshint strict:false */
function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_beautify)
{
@ -50,49 +51,70 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
function reset_options()
{
opts = JSON.parse(JSON.stringify(default_opts));
test_name = 'css-beautify';
}
function test_css_beautifier(input)
function test_beautifier(input)
{
return css_beautify(input, opts);
}
var sanitytest;
var test_name = '';
function set_name(name)
{
name = (name || '').trim();
if (name) {
test_name = name.replace(/\r/g, '\\r').replace(/\n/g, '\\n');
}
}
// test the input on beautifier with the current flag settings
// does not check the indentation / surroundings as bt() does
function test_fragment(input, expected)
{
var success = true;
sanitytest.test_function(test_beautifier, test_name);
expected = expected || expected === '' ? expected : input;
sanitytest.expect(input, expected);
success = success && sanitytest.expect(input, expected);
// if the expected is different from input, run it again
// expected output should be unchanged when run twice.
if (expected !== input) {
sanitytest.expect(expected, expected);
if (success && expected !== input) {
success = success && sanitytest.expect(expected, expected);
}
// Everywhere we do newlines, they should be replaced with opts.eol
sanitytest.test_function(test_beautifier, 'eol ' + test_name);
opts.eol = '\r\\n';
expected = expected.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
if (input && input.indexOf('\n') !== -1) {
opts.disabled = true;
success = success && sanitytest.expect(input, input || '');
success = success && sanitytest.expect('\n\n' + expected, '\n\n' + expected);
opts.disabled = false;
success = success && sanitytest.expect(input, expected);
if (success && input && input.indexOf('\n') !== -1) {
input = input.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
// Ensure support for auto eol detection
opts.eol = 'auto';
sanitytest.expect(input, expected);
success = success && sanitytest.expect(input, expected);
}
opts.eol = '\n';
return success;
}
// test css
function t(input, expectation)
{
var success = true;
var wrapped_input, wrapped_expectation;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_css_beautifier, 'css_beautify');
test_fragment(input, expectation);
success = success && test_fragment(input, expectation);
return success;
}
function unicode_char(value) {
@ -112,6 +134,7 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
{{^matrix}}
// {{&name}}
reset_options();
set_name('{{&name}}');
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
@ -123,6 +146,7 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
{{#matrix}}
// {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
reset_options();
set_name('{{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})');
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
@ -142,6 +166,12 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
//============================================================
test_fragment(null, '');
reset_options();
//============================================================
// Test user pebkac protection, converts dash names to underscored names
opts["end-with-newline"] = true;
test_fragment(null, '\n');
reset_options();
//============================================================
// test basic css beautifier

View File

@ -109,6 +109,15 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
t(None, "")
self.reset_options()
#============================================================
# Test user pebkac protection, converts dash names to underscored names
setattr(self.options, 'end-with-newline', True)
t(None, '\n')
self.reset_options()
#============================================================
t("", "")
t("\n", "")
t(".tabs{}\n", ".tabs {}")
@ -258,6 +267,12 @@ class CSSBeautifierTest(unittest.TestCase):
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n'
expectation = expectation.replace('\n', '\r\n')
self.options.disabled = True
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), input or '')
self.assertMultiLineEqual(
cssbeautifier.beautify('\n\n' + expectation, self.options), '\n\n' + expectation)
self.options.disabled = False;
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
if input and input.find('\n') != -1:

View File

@ -56,6 +56,99 @@ exports.test_data = {
{ fragment: true, input: ' \n\n.tabs{}\n\n\n\n', output: ' .tabs {}{{eof}}' },
{ fragment: true, input: '\n', output: '{{eof}}' }
]
}, {
name: "Support Indent Level Options and Base Indent Autodetection",
description: "If user specifies indent level, use it. If not, autodetect indent level from starting whitespace.",
matrix: [{
options: [
{ name: "indent_size", value: "4" },
{ name: "indent_char", value: "' '" },
{ name: "indent_with_tabs", value: "false" }
],
input_start_indent: ' ',
output_start_of_base: ' ',
i: ' '
}, {
options: [
{ name: "indent_size", value: "4" },
{ name: "indent_char", value: "' '" },
{ name: "indent_with_tabs", value: "false" },
{ name: "indent_level", value: "0" }
],
input_start_indent: ' ',
output_start_of_base: ' ',
i: ' '
}, {
options: [
{ name: "indent_size", value: "4" },
{ name: "indent_char", value: "' '" },
{ name: "indent_with_tabs", value: "false" },
{ name: "indent_level", value: "1" }
],
input_start_indent: ' ',
output_start_of_base: ' ',
i: ' '
}, {
options: [
{ name: "indent_size", value: "4" },
{ name: "indent_char", value: "' '" },
{ name: "indent_with_tabs", value: "false" },
{ name: "indent_level", value: "2" }
],
input_start_indent: '',
output_start_of_base: ' ',
i: ' '
}, {
options: [
{ name: "indent_size", value: "4" },
{ name: "indent_char", value: "' '" },
{ name: "indent_with_tabs", value: "true" },
{ name: "indent_level", value: "2" }
],
input_start_indent: '',
output_start_of_base: '\t\t',
i: '\t'
}, {
options: [
{ name: "indent_size", value: "4" },
{ name: "indent_char", value: "' '" },
{ name: "indent_with_tabs", value: "false" },
{ name: "indent_level", value: "0" }
],
input_start_indent: '\t ',
output_start_of_base: '\t ',
i: ' '
}],
tests: [
{ fragment: true, input: '{{input_start_indent}}a', output: '{{output_start_of_base}}a' },
{
fragment: true,
input: [
'{{input_start_indent}}.a {',
' text-align: right;',
'}'
],
output: [
'{{output_start_of_base}}.a {',
'{{output_start_of_base}}{{i}}text-align: right;',
'{{output_start_of_base}}}'
]
}, {
fragment: true,
input: [
'{{input_start_indent}}// This is a random comment',
'.a {',
' text-align: right;',
'}'
],
output: [
'{{output_start_of_base}}// This is a random comment',
'{{output_start_of_base}}.a {',
'{{output_start_of_base}}{{i}}text-align: right;',
'{{output_start_of_base}}}'
]
}
]
}, {
name: "Empty braces",
description: "",
@ -72,6 +165,18 @@ exports.test_data = {
tests: [{
input: '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity = 90);\n}',
output: '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity=90);\n}'
}, {
comment: 'simple data uri base64 test',
input: 'a { background: url(data:image/gif;base64,R0lGODlhCwALAJEAAAAAAP///xUVFf///yH5BAEAAAMALAAAAAALAAsAAAIPnI+py+0/hJzz0IruwjsVADs=); }',
output: 'a {\n\tbackground: url(data:image/gif;base64,R0lGODlhCwALAJEAAAAAAP///xUVFf///yH5BAEAAAMALAAAAAALAAsAAAIPnI+py+0/hJzz0IruwjsVADs=);\n}'
}, {
comment: 'non-base64 data',
input: 'a { background: url(data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E); }',
output: 'a {\n\tbackground: url(data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E);\n}'
}, {
comment: 'Beautifier does not fix or mitigate bad data uri',
input: 'a { background: url(data: image/gif base64,R0lGODlhCwALAJEAAAAAAP///xUVFf///yH5BAEAAAMALAAAAAALAAsAAAIPnI+py+0/hJzz0IruwjsVADs=); }',
output: 'a {\n\tbackground: url(data: image/gif base64,R0lGODlhCwALAJEAAAAAAP///xUVFf///yH5BAEAAAMALAAAAAALAAsAAAIPnI+py+0/hJzz0IruwjsVADs=);\n}'
}]
}, {
name: "Support simple language specific option inheritance/overriding",
@ -167,46 +272,95 @@ exports.test_data = {
]
}
]
}, {
name: "Issue 1373 -- Correct spacing around [attribute~=value]",
description: "",
tests: [{
unchanged: 'header>div[class~="div-all"]'
}]
}, {
name: 'Selector Separator',
description: '',
matrix: [{
options: [
{ name: 'selector_separator_newline', value: 'false' },
{ name: 'selector_separator', value: '" "' }
{ name: 'selector_separator', value: '" "' },
{ name: "newline_between_rules", value: "true" }
],
separator: ' ',
separator1: ' '
separator1: ' ',
new_rule: '\n',
first_nested_rule: ''
}, {
options: [
{ name: 'selector_separator_newline', value: 'false' },
{ name: 'selector_separator', value: '" "' }
{ name: 'selector_separator', value: '" "' },
{ name: "newline_between_rules", value: "false" }
],
separator: ' ',
separator1: ' ',
new_rule: '',
first_nested_rule: ''
}, {
options: [
{ name: 'selector_separator_newline', value: 'false' },
{ name: 'selector_separator', value: '" "' },
{ name: "newline_between_rules", value: "false" }
],
// BUG: #713
separator: ' ',
separator1: ' '
separator1: ' ',
new_rule: '',
first_nested_rule: ''
}, {
options: [
{ name: 'selector_separator_newline', value: 'true' },
{ name: 'selector_separator', value: '" "' }
{ name: 'selector_separator', value: '" "' },
{ name: "newline_between_rules", value: "true" }
],
separator: '\\n',
separator1: '\\n\\t'
separator1: '\\n\\t',
new_rule: '\n',
first_nested_rule: '\n' // bug #1489
}, {
options: [
{ name: 'selector_separator_newline', value: 'true' },
{ name: 'selector_separator', value: '" "' }
{ name: 'selector_separator', value: '" "' },
{ name: "newline_between_rules", value: "false" }
],
separator: '\\n',
separator1: '\\n\\t'
separator1: '\\n\\t',
new_rule: '',
first_nested_rule: ''
}, {
options: [
{ name: 'selector_separator_newline', value: 'true' },
{ name: 'selector_separator', value: '" "' },
{ name: "newline_between_rules", value: "false" }
],
separator: '\\n',
separator1: '\\n\\t',
new_rule: '',
new_rule_bug: ''
}],
tests: [
{ input: '#bla, #foo{color:green}', output: '#bla,{{separator}}#foo {\n\tcolor: green\n}' },
{ input: '#bla, #foo{color:green}\n#bla, #foo{color:green}', output: '#bla,{{separator}}#foo {\n\tcolor: green\n}{{new_rule}}\n#bla,{{separator}}#foo {\n\tcolor: green\n}' },
{ input: '@media print {.tab{}}', output: '@media print {\n\t.tab {}\n}' },
{ input: '@media print {.tab,.bat{}}', output: '@media print {\n\t.tab,{{separator1}}.bat {}\n}' },
{
comment: 'This is bug #1489',
input: '@media print {.tab,.bat{}}',
output: '@media print {\n{{first_nested_rule}}\t.tab,{{separator1}}.bat {}\n}'
},
{
comment: 'This is bug #1489',
input: '@media print {// comment\n//comment 2\n.bat{}}',
output: '@media print {\n{{new_rule}}\t// comment\n\t//comment 2\n\t.bat {}\n}'
},
{ input: '#bla, #foo{color:black}', output: '#bla,{{separator}}#foo {\n\tcolor: black\n}' }, {
input: 'a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}',
output: 'a:first-child,{{separator}}a:first-child {\n\tcolor: red;\n\tdiv:first-child,{{separator1}}div:hover {\n\t\tcolor: black;\n\t}\n}'
input: 'a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}\na:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}',
output: 'a:first-child,{{separator}}a:first-child {\n\tcolor: red;{{new_rule}}\n\tdiv:first-child,{{separator1}}div:hover {\n\t\tcolor: black;\n\t}\n}\n{{new_rule}}a:first-child,{{separator}}a:first-child {\n\tcolor: red;{{new_rule}}\n\tdiv:first-child,{{separator1}}div:hover {\n\t\tcolor: black;\n\t}\n}'
}
]
}, {
@ -257,6 +411,13 @@ exports.test_data = {
input: '.tool-tip {\n\tposition: relative;\n\n\t\t\n\t.tool-tip-content {\n\t\t&>* {\n\t\t\tmargin-top: 0;\n\t\t}\n\t\t\n\n\t\t.mixin-box-shadow(.2rem .2rem .5rem rgba(0, 0, 0, .15));\n\t\tpadding: 1rem;\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t}\n}',
output: '.tool-tip {\n\tposition: relative;\n\n\n\t.tool-tip-content {\n\t\t&>* {\n\t\t\tmargin-top: 0;\n\t\t}\n\\n\\n\t\t.mixin-box-shadow(.2rem .2rem .5rem rgba(0, 0, 0, .15));\n\t\tpadding: 1rem;\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t}\n}'
}]
}, {
name: "Issue #1338 -- Preserve Newlines within CSS rules",
options: [{ name: "preserve_newlines", value: "true" }],
description: "",
tests: [{
unchanged: 'body {\n\tgrid-template-areas:\n\t\t"header header"\n\t\t"main sidebar"\n\t\t"footer footer";\n}'
}]
}, {
name: "Newline Between Rules",
description: "",
@ -264,28 +425,238 @@ exports.test_data = {
options: [
{ name: "newline_between_rules", value: "true" }
],
new_rule: '\n\n'
new_rule: '\n'
}, {
options: [
{ name: "newline_between_rules", value: "false" }
],
new_rule: '\n'
new_rule: ''
}],
tests: [
{ input: '.div {}\n.span {}', output: '.div {}{{new_rule}}.span {}' },
{ input: '.div{}\n \n.span{}', output: '.div {}{{new_rule}}.span {}' },
{ input: '.div {} \n \n.span { } \n', output: '.div {}{{new_rule}}.span {}' },
{ input: '.div {\n \n} \n .span {\n } ', output: '.div {}{{new_rule}}.span {}' },
{ input: '.div {}\n.span {}', output: '.div {}\n{{new_rule}}.span {}' },
{ input: '.div{}\n \n.span{}', output: '.div {}\n{{new_rule}}.span {}' },
{ input: '.div {} \n \n.span { } \n', output: '.div {}\n{{new_rule}}.span {}' },
{ input: '.div {\n \n} \n .span {\n } ', output: '.div {}\n{{new_rule}}.span {}' },
{
input: '.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div{height:15px;}',
output: '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}{{new_rule}}.div {\n\theight: 15px;\n}'
output: '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n{{new_rule}}.div {\n\theight: 15px;\n}'
},
{ input: '.tabs{width:10px;//end of line comment\nheight:10px;//another\n}\n.div{height:15px;}', output: '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}{{new_rule}}.div {\n\theight: 15px;\n}' },
{ input: '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', output: '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}{{new_rule}}.div {\n\theight: 15px;\n}' },
{ input: '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', output: '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}{{new_rule}}.div {\n\theight: 15px;\n}' },
{ input: '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}', output: '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}{{new_rule}}@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}' },
{ input: 'a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}{{new_rule}}.div {\n\theight: 15px;\n}' },
{ input: 'a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}{{new_rule}}.div {\n\theight: 15px;\n}' }
{ input: '.tabs{width:10px;//end of line comment\nheight:10px;//another\n}\n.div{height:15px;}', output: '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}\n{{new_rule}}.div {\n\theight: 15px;\n}' },
{ input: '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', output: '#foo {\n\tbackground-image: url(foo@2x.png);\n{{new_rule}}\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n{{new_rule}}.div {\n\theight: 15px;\n}' },
{ input: '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', output: '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n{{new_rule}}\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n{{new_rule}}.div {\n\theight: 15px;\n}' },
{ input: '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}', output: '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n{{new_rule}}@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n{{new_rule}}\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n{{new_rule}}\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}' },
{ input: 'a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n{{new_rule}}\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}\n{{new_rule}}.div {\n\theight: 15px;\n}' },
{ input: 'a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n{{new_rule}}\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}\n{{new_rule}}.div {\n\theight: 15px;\n}' },
{
input_: [
'.list-group {',
'\t.list-group-item {',
'\t}',
'',
'\t.list-group-icon {',
'\t}',
'}',
'',
'.list-group-condensed {',
'}'
],
output: [
'.list-group {',
'\t.list-group-item {}',
'{{new_rule}}\t.list-group-icon {}',
'}',
'{{new_rule}}.list-group-condensed {}'
]
},
{
input_: [
'.list-group {',
'\t.list-group-item {',
'\t\ta:1',
'\t}',
'\t.list-group-item {',
'\t\ta:1',
'\t}',
'\t.list-group-icon {',
'\t}',
'\t.list-group-icon {',
'\t}',
'}',
'.list-group-condensed {',
'}'
],
output: [
'.list-group {',
'\t.list-group-item {',
'\t\ta: 1',
'\t}',
'{{new_rule}}\t.list-group-item {',
'\t\ta: 1',
'\t}',
'{{new_rule}}\t.list-group-icon {}',
'{{new_rule}}\t.list-group-icon {}',
'}',
'{{new_rule}}.list-group-condensed {}'
]
},
{
input_: [
'.list-group {',
'\t.list-group-item {',
'\t\ta:1',
'\t}',
'\t//this is my pre-comment',
'\t.list-group-item {',
'\t\ta:1',
'\t}',
'\t//this is a comment',
'\t.list-group-icon {',
'\t}',
'\t//this is also a comment',
'\t.list-group-icon {',
'\t}',
'}',
'.list-group-condensed {',
'}'
],
output: [
'.list-group {',
'\t.list-group-item {',
'\t\ta: 1',
'\t}',
'{{new_rule}}\t//this is my pre-comment',
'\t.list-group-item {',
'\t\ta: 1',
'\t}',
'{{new_rule}}\t//this is a comment',
'\t.list-group-icon {}',
'{{new_rule}}\t//this is also a comment',
'\t.list-group-icon {}',
'}',
'{{new_rule}}.list-group-condensed {}'
]
},
{
input_: [
'.list-group {',
'\tcolor: #38a0e5;',
'\t.list-group-item {',
'\t\ta:1',
'\t}',
'\tcolor: #38a0e5;',
'\t.list-group-item {',
'\t\ta:1',
'\t}',
'color: #38a0e5;',
'\t.list-group-icon {',
'\t}',
'\tcolor: #38a0e5;',
'\t.list-group-icon {',
'\t}',
'}',
'color: #38a0e5;',
'.list-group-condensed {',
'}'
],
output: [
'.list-group {',
'\tcolor: #38a0e5;',
'{{new_rule}}\t.list-group-item {',
'\t\ta: 1',
'\t}',
'{{new_rule}}\tcolor: #38a0e5;',
'{{new_rule}}\t.list-group-item {',
'\t\ta: 1',
'\t}',
'{{new_rule}}\tcolor: #38a0e5;',
'{{new_rule}}\t.list-group-icon {}',
'{{new_rule}}\tcolor: #38a0e5;',
'{{new_rule}}\t.list-group-icon {}',
'}',
'{{new_rule}}color: #38a0e5;',
'{{new_rule}}.list-group-condensed {}'
]
},
{
input_: [
'@media only screen and (max-width: 40em) {',
'header {',
' margin: 0 auto;',
' padding: 10px;',
' background: red;',
' }',
'main {',
' margin: 20px auto;',
' padding: 4px;',
' background: blue;',
' }',
'}'
],
output: [
'@media only screen and (max-width: 40em) {',
'\theader {',
'\t\tmargin: 0 auto;',
'\t\tpadding: 10px;',
'\t\tbackground: red;',
'\t}',
'{{new_rule}}\tmain {',
'\t\tmargin: 20px auto;',
'\t\tpadding: 4px;',
'\t\tbackground: blue;',
'\t}',
'}'
]
},
{
input_: [
'.preloader {',
'\theight: 20px;',
'\t.line {',
'\t\twidth: 1px;',
'\t\theight: 12px;',
'\t\tbackground: #38a0e5;',
'\t\tmargin: 0 1px;',
'\t\tdisplay: inline-block;',
'\t\t&.line-1 {',
'\t\t\tanimation-delay: 800ms;',
'\t\t}',
'\t\t&.line-2 {',
'\t\t\tanimation-delay: 600ms;',
'\t\t}',
'\t}',
'\tdiv {',
'\t\tcolor: #38a0e5;',
'\t\tfont-family: "Arial", sans-serif;',
'\t\tfont-size: 10px;',
'\t\tmargin: 5px 0;',
'\t}',
'}'
],
output: [
'.preloader {',
'\theight: 20px;',
'{{new_rule}}\t.line {',
'\t\twidth: 1px;',
'\t\theight: 12px;',
'\t\tbackground: #38a0e5;',
'\t\tmargin: 0 1px;',
'\t\tdisplay: inline-block;',
'{{new_rule}}\t\t&.line-1 {',
'\t\t\tanimation-delay: 800ms;',
'\t\t}',
'{{new_rule}}\t\t&.line-2 {',
'\t\t\tanimation-delay: 600ms;',
'\t\t}',
'\t}',
'{{new_rule}}\tdiv {',
'\t\tcolor: #38a0e5;',
'\t\tfont-family: "Arial", sans-serif;',
'\t\tfont-size: 10px;',
'\t\tmargin: 5px 0;',
'\t}',
'}'
]
}
]
}, {
name: "Functions braces",
@ -434,6 +805,14 @@ exports.test_data = {
}],
tests: [
{ unchanged: '/* header comment newlines on */' },
{
input: [
'@import "custom.css";<i>.rule{}'
],
output: [
'@import "custom.css";<new_rule>.rule {}'
]
},
{ input: '.tabs{<i>/* test */<i>}', output: '.tabs {<o>\t/* test */<o>}' },
{
comment: '#1185',
@ -525,23 +904,23 @@ exports.test_data = {
},
{
input: '#foo {<i>background-image: url(foo@2x.png);<i>\t@font-face {<i>\t\tfont-family: "Bitstream Vera Serif Bold";<i>\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");<i>\t}<i>}<i>.div{<i>height:15px;<i>}',
output: '#foo {<o>\tbackground-image: url(foo@2x.png);<o>\t@font-face {<o>\t\tfont-family: "Bitstream Vera Serif Bold";<o>\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");<o>\t}<o>}<new_rule>.div {<o>\theight: 15px;<o>}'
output: '#foo {<o>\tbackground-image: url(foo@2x.png);<new_rule>\t@font-face {<o>\t\tfont-family: "Bitstream Vera Serif Bold";<o>\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");<o>\t}<o>}<new_rule>.div {<o>\theight: 15px;<o>}'
},
{
input: '@media screen {<i>\t#foo:hover {<i>\t\tbackground-image: url(foo@2x.png);<i>\t}<i>\t@font-face {<i>\t\tfont-family: "Bitstream Vera Serif Bold";<i>\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");<i>\t}<i>}<i>.div{<i>height:15px;<i>}',
output: '@media screen {<o>\t#foo:hover {<o>\t\tbackground-image: url(foo@2x.png);<o>\t}<o>\t@font-face {<o>\t\tfont-family: "Bitstream Vera Serif Bold";<o>\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");<o>\t}<o>}<new_rule>.div {<o>\theight: 15px;<o>}'
output: '@media screen {<o>\t#foo:hover {<o>\t\tbackground-image: url(foo@2x.png);<o>\t}<new_rule>\t@font-face {<o>\t\tfont-family: "Bitstream Vera Serif Bold";<o>\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");<o>\t}<o>}<new_rule>.div {<o>\theight: 15px;<o>}'
},
{
input: '@font-face {<i>\tfont-family: "Bitstream Vera Serif Bold";<i>\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");<i>}<i1>@media screen {<i>\t#foo:hover {<i>\t\tbackground-image: url(foo.png);<i>\t}<i>\t@media screen and (min-device-pixel-ratio: 2) {<i>\t\t@font-face {<i>\t\t\tfont-family: "Helvetica Neue";<i>\t\t}<i>\t\t#foo:hover {<i>\t\t\tbackground-image: url(foo@2x.png);<i>\t\t}<i>\t}<i>}',
output: '@font-face {<o>\tfont-family: "Bitstream Vera Serif Bold";<o>\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");<o>}<new_rule>@media screen {<o>\t#foo:hover {<o>\t\tbackground-image: url(foo.png);<o>\t}<o>\t@media screen and (min-device-pixel-ratio: 2) {<o>\t\t@font-face {<o>\t\t\tfont-family: "Helvetica Neue";<o>\t\t}<o>\t\t#foo:hover {<o>\t\t\tbackground-image: url(foo@2x.png);<o>\t\t}<o>\t}<o>}'
output: '@font-face {<o>\tfont-family: "Bitstream Vera Serif Bold";<o>\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");<o>}<new_rule>@media screen {<o>\t#foo:hover {<o>\t\tbackground-image: url(foo.png);<o>\t}<new_rule>\t@media screen and (min-device-pixel-ratio: 2) {<o>\t\t@font-face {<o>\t\t\tfont-family: "Helvetica Neue";<o>\t\t}<new_rule>\t\t#foo:hover {<o>\t\t\tbackground-image: url(foo@2x.png);<o>\t\t}<o>\t}<o>}'
},
{
input: 'a:first-child{<i>color:red;<i>div:first-child{<i>color:black;<i>}<i>}<i>.div{<i>height:15px;<i>}',
output: 'a:first-child {<o>\tcolor: red;<o>\tdiv:first-child {<o>\t\tcolor: black;<o>\t}<o>}<new_rule>.div {<o>\theight: 15px;<o>}'
output: 'a:first-child {<o>\tcolor: red;<new_rule>\tdiv:first-child {<o>\t\tcolor: black;<o>\t}<o>}<new_rule>.div {<o>\theight: 15px;<o>}'
},
{
input: 'a:first-child{<i>color:red;<i>div:not(.peq){<i>color:black;<i>}<i>}<i>.div{<i>height:15px;<i>}',
output: 'a:first-child {<o>\tcolor: red;<o>\tdiv:not(.peq) {<o>\t\tcolor: black;<o>\t}<o>}<new_rule>.div {<o>\theight: 15px;<o>}'
output: 'a:first-child {<o>\tcolor: red;<new_rule>\tdiv:not(.peq) {<o>\t\tcolor: black;<o>\t}<o>}<new_rule>.div {<o>\theight: 15px;<o>}'
}
]
},
@ -689,6 +1068,36 @@ exports.test_data = {
'}'
]
}]
}, {
name: "Issue #645",
description: "",
options: [
{ name: "selector_separator_newline", value: "true" },
{ name: "preserve_newlines", value: "true" },
{ name: "newline_between_rules", value: "true" }
],
tests: [{
unchanged: [
'/* Comment above first rule */',
'',
'body {',
'\tdisplay: none;',
'}',
'',
'/* Comment between rules */',
'',
'ul,',
'',
'/* Comment between selectors */',
'',
'li {',
'\tdisplay: none;',
'}',
'',
'/* Comment after last rule */'
]
}]
}, {
name: "Extend Tests",
description: "Test for '@extend'",
@ -714,6 +1123,33 @@ exports.test_data = {
'}'
]
}]
}, {
name: "Import Tests",
description: "Test for '@import'",
options: [],
tests: [{
input: [
'@import "custom.css";.rule{}',
'a, p {}'
],
output: [
'@import "custom.css";',
'.rule {}',
'a,',
'p {}'
]
}, {
input: [
'@import url("bluish.css") projection,tv;.rule{}',
'a, p {}'
],
output: [
'@import url("bluish.css") projection, tv;',
'.rule {}',
'a,',
'p {}'
]
}]
}, {
name: "Important ",
description: "Spacing of !important",

View File

@ -26,6 +26,7 @@
SOFTWARE.
*/
/*jshint unused:false */
/*jshint strict:false */
function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_beautify)
{
@ -50,55 +51,75 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be
function reset_options()
{
opts = JSON.parse(JSON.stringify(default_opts));
test_name = 'html-beautify';
}
function test_html_beautifier(input)
function test_beautifier(input)
{
return html_beautify(input, opts);
}
var sanitytest;
var test_name = '';
function set_name(name)
{
name = (name || '').trim();
if (name) {
test_name = name.replace(/\r/g, '\\r').replace(/\n/g, '\\n');
}
}
// test the input on beautifier with the current flag settings
// does not check the indentation / surroundings as bt() does
function test_fragment(input, expected)
{
var success = true;
sanitytest.test_function(test_beautifier, test_name);
expected = expected || expected === '' ? expected : input;
sanitytest.expect(input, expected);
success = success && sanitytest.expect(input, expected);
// if the expected is different from input, run it again
// expected output should be unchanged when run twice.
if (expected !== input) {
sanitytest.expect(expected, expected);
if (success && expected !== input) {
success = success && sanitytest.expect(expected, expected);
}
// Everywhere we do newlines, they should be replaced with opts.eol
opts.eol = '\r\n';
sanitytest.test_function(test_beautifier, 'eol ' + test_name);
opts.eol = '\r\\n';
expected = expected.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
if (input && input.indexOf('\n') !== -1) {
opts.disabled = true;
success = success && sanitytest.expect(input, input || '');
success = success && sanitytest.expect('\n\n' + expected, '\n\n' + expected);
opts.disabled = false;
success = success && sanitytest.expect(input, expected);
if (success && input && input.indexOf('\n') !== -1) {
input = input.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
// Ensure support for auto eol detection
opts.eol = 'auto';
sanitytest.expect(input, expected);
success = success && sanitytest.expect(input, expected);
}
opts.eol = '\n';
return success;
}
// test html
function bth(input, expectation)
{
var success = true;
var wrapped_input, wrapped_expectation, field_input, field_expectation;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_html_beautifier, 'html_beautify');
test_fragment(input, expectation);
success = success && test_fragment(input, expectation);
if (opts.indent_size === 4 && input) {
wrapped_input = '<div>\n' + input.replace(/^(.+)$/mg, ' $1') + '\n <span>inline</span>\n</div>';
wrapped_expectation = '<div>\n' + expectation.replace(/^(.+)$/mg, ' $1') + '\n <span>inline</span>\n</div>';
test_fragment(wrapped_input, wrapped_expectation);
success = success && test_fragment(wrapped_input, wrapped_expectation);
}
return success;
}
function unicode_char(value) {
@ -118,6 +139,7 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be
{{^matrix}}
// {{&name}}
reset_options();
set_name('{{&name}}');
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
@ -129,6 +151,7 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be
{{#matrix}}
// {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
reset_options();
set_name('{{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})');
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
@ -150,7 +173,15 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be
reset_options();
//============================================================
// Test user pebkac protection, converts dash names to underscored names
opts["end-with-newline"] = true;
test_fragment(null, '\n');
reset_options();
//============================================================
set_name('end_with_newline = true');
opts.end_with_newline = true;
test_fragment('', '\n');
test_fragment('<div></div>\n');
test_fragment('<div></div>\n\n\n', '<div></div>\n');
@ -162,11 +193,17 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be
'</head>\n');
opts.end_with_newline = false;
reset_options();
//============================================================
set_name('Error cases');
// error cases need love too
bth('<img title="Bad food!" src="foo.jpg" alt="Evil" ">');
bth("<!-- don't blow up if a comment is not complete"); // -->
reset_options();
//============================================================
set_name('Basic beautify');
test_fragment(
'<head>\n' +
' <script>\n' +
@ -262,6 +299,9 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be
'<div> content <img> content </div>');
bth('Text <a href="#">Link</a> Text');
reset_options();
//============================================================
set_name('content_unformatted = ["script", "style"]');
var content_unformatted = opts.content_unformatted;
opts.content_unformatted = ['script', 'style'];
bth('<script id="javascriptTemplate" type="text/x-kendo-template">\n' +
@ -275,7 +315,10 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be
' body {background-color:lightgrey}\n' +
' h1 {color:blue}\n' +
'</style>');
opts.content_unformatted = content_unformatted;
reset_options();
//============================================================
set_name('inline = ["custom-element"]');
inline_tags = opts.inline;
opts.inline = ['custom-element'];
@ -285,6 +328,11 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be
' insert newlines</div>');
opts.inline = inline_tags;
reset_options();
//============================================================
set_name('line wrap tests');
bth('<div><span>content</span></div>');
// Handlebars tests
@ -324,52 +372,49 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some test text that should wrap_inside_this section here.</div>',
/* expected */
'<div>Some test text that should wrap_inside_this\n' +
' section here.</div>');
'<div>Some test text that should\n' +
' wrap_inside_this section here.</div>');
// Support passing string of number
opts.wrap_line_length = "40";
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some test text that should wrap_inside_this section here.</div>',
/* expected */
'<div>Some test text that should wrap_inside_this\n' +
' section here.</div>');
'<div>Some test text that should\n' +
' wrap_inside_this section here.</div>');
opts.indent_size = 1;
opts.indent_char = '\t';
opts.preserve_newlines = false;
bth('<div>\n\tfoo\n</div>', '<div> foo </div>');
opts.wrap_line_length = 80;
// BUGBUG #1238 This is still wrong but is also a good regression test
//...---------1---------2---------3---------4---------5---------6---------7---------8---------9--------10--------11--------12--------13--------14--------15--------16--------17--------18--------19--------20--------21--------22--------23--------24--------25--------26--------27--------28--------29
bth('<span uib-tooltip="[[firstName]] [[lastName]]" tooltip-enable="showToolTip">\n' +
' <ng-letter-avatar charCount="2" data="[[data]]"\n' +
' shape="round" fontsize="[[font]]" height="[[height]]" width="[[width]]"\n' +
' avatarcustombgcolor="[[bgColor]]" dynamic="true"></ng-letter-avatar>\n' +
' </span>',
/* expected */
'<span uib-tooltip="[[firstName]] [[lastName]]" tooltip-enable="showToolTip">\n' +
' <ng-letter-avatar charCount="2" data="[[data]]" shape="round" fontsize="[[font]]"\n' +
' height="[[height]]" width="[[width]]" avatarcustombgcolor="[[bgColor]]"\n' +
' dynamic="true"></ng-letter-avatar>\n' +
'</span>');
opts.preserve_newlines = true;
bth('<div>\n\tfoo\n</div>');
// ISSUE #607 - preserve-newlines makes this look a bit odd now, but it much better
test_fragment(
'<p>В РАБОЧЕМ РЕЖИМЕ, после ввода параметров опыта (номер, шаг отсчетов и глубина зондирования), текущие\n' +
' отсчеты сохраняются в контроллере при нажатии кнопки «ПУСК». Одновременно, они распечатываются\n' +
' на минипринтере. Управлять контроллером для записи данных зондирования можно при помощи <link_row to="РК.05.01.01">Радиокнопки РК-11</link_row>.</p>',
/* expected */
'<p>В РАБОЧЕМ РЕЖИМЕ, после ввода параметров опыта (номер, шаг отсчетов и\n' +
' глубина зондирования), текущие\n' +
' отсчеты сохраняются в контроллере при нажатии кнопки «ПУСК». Одновременно,\n' +
' они распечатываются\n' +
' на минипринтере. Управлять контроллером для записи данных зондирования\n' +
' можно при помощи <link_row to="РК.05.01.01">Радиокнопки РК-11</link_row>.</p>');
reset_options();
//============================================================
// test preserve_newlines and max_preserve_newlines
opts.preserve_newlines = false;
bth('<div>Should not</div>\n\n\n' +
'<div>preserve newlines</div>',
'<div>Should not</div>\n' +
'<div>preserve newlines</div>');
opts.preserve_newlines = true;
opts.max_preserve_newlines = 0;
bth('<div>Should</div>\n\n\n' +
'<div>preserve zero newlines</div>',
'<div>Should</div>\n' +
'<div>preserve zero newlines</div>');
opts.max_preserve_newlines = 1;
bth('<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>',
'<div>Should</div>\n\n' +
'<div>preserve one newline</div>');
opts.max_preserve_newlines = null;
bth('<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>',
'<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>');
}
beautifier_tests();

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@
SOFTWARE.
*/
/*jshint unused:false */
/*jshint strict:false */
function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_beautify)
{
@ -35,8 +36,6 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
indent_char: ' ',
preserve_newlines: true,
jslint_happy: false,
keep_array_indentation: false,
brace_style: 'collapse',
space_before_conditional: true,
break_chained_methods: false,
selector_separator: '\n',
@ -50,57 +49,75 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
function reset_options()
{
opts = JSON.parse(JSON.stringify(default_opts));
test_name = 'js-beautify';
}
function test_js_beautifier(input)
function test_beautifier(input)
{
return js_beautify(input, opts);
}
var sanitytest;
var test_name = '';
function set_name(name)
{
name = (name || '').trim();
if (name) {
test_name = name.replace(/\r/g, '\\r').replace(/\n/g, '\\n');
}
}
// test the input on beautifier with the current flag settings
// does not check the indentation / surroundings as bt() does
function test_fragment(input, expected)
{
var success = true;
sanitytest.test_function(test_beautifier, test_name);
expected = expected || expected === '' ? expected : input;
sanitytest.expect(input, expected);
success = success && sanitytest.expect(input, expected);
// if the expected is different from input, run it again
// expected output should be unchanged when run twice.
if (expected !== input) {
sanitytest.expect(expected, expected);
if (success && expected !== input) {
success = success && sanitytest.expect(expected, expected);
}
// Everywhere we do newlines, they should be replaced with opts.eol
sanitytest.test_function(test_beautifier, 'eol ' + test_name);
opts.eol = '\r\\n';
expected = expected.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
if (input && input.indexOf('\n') !== -1) {
opts.disabled = true;
success = success && sanitytest.expect(input, input || '');
success = success && sanitytest.expect('\n\n' + expected, '\n\n' + expected);
opts.disabled = false;
success = success && sanitytest.expect(input, expected);
if (success && input && input.indexOf('\n') !== -1) {
input = input.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
// Ensure support for auto eol detection
opts.eol = 'auto';
sanitytest.expect(input, expected);
success = success && sanitytest.expect(input, expected);
}
opts.eol = '\n';
return success;
}
// test the input on beautifier with the current flag settings
// test both the input as well as { input } wrapping
function bt(input, expectation)
{
var success = true;
var wrapped_input, wrapped_expectation;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_js_beautifier, 'js_beautify');
test_fragment(input, expectation);
success = success && test_fragment(input, expectation);
// If we set raw, input should be unchanged
opts.test_output_raw = true;
if (!opts.end_with_newline) {
test_fragment(input, input);
success = success && test_fragment(input, input);
}
opts.test_output_raw = false;
@ -117,16 +134,16 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
if (current_indent_size === 4 && input) {
wrapped_input = '{\n' + input.replace(/^(.+)$/mg, ' $1') + '\n foo = bar;\n}';
wrapped_expectation = '{\n' + expectation.replace(/^(.+)$/mg, ' $1') + '\n foo = bar;\n}';
test_fragment(wrapped_input, wrapped_expectation);
success = success && test_fragment(wrapped_input, wrapped_expectation);
// If we set raw, input should be unchanged
opts.test_output_raw = true;
if (!opts.end_with_newline) {
test_fragment(wrapped_input, wrapped_input);
success = success && test_fragment(wrapped_input, wrapped_input);
}
opts.test_output_raw = false;
}
return success;
}
// run all tests for the given brace style ("collapse", "expand", "end-expand", or "none").
@ -289,6 +306,7 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
{{^matrix}}
// {{&name}}
reset_options();
set_name('{{&name}}');
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
@ -300,6 +318,7 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
{{#matrix}}
// {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
reset_options();
set_name('{{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})');
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
@ -319,6 +338,12 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
//============================================================
test_fragment(null, '');
reset_options();
//============================================================
// Test user pebkac protection, converts dash names to underscored names
opts["end-with-newline"] = true;
test_fragment(null, '\n');
reset_options();
//============================================================
opts.indent_size = 1;

View File

@ -52,8 +52,6 @@ class TestJSBeautifier(unittest.TestCase):
default_options.indent_char = ' '
default_options.preserve_newlines = true
default_options.jslint_happy = false
default_options.keep_array_indentation = true
default_options.brace_style = 'collapse'
default_options.indent_level = 0
default_options.break_chained_methods = false
default_options.eol = '\n'
@ -146,6 +144,27 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
bt(None, "")
self.reset_options()
#============================================================
# Test user pebkac protection, converts dash names to underscored names
setattr(self.options, 'end-with-newline', True)
test_fragment(None, '\n')
self.reset_options()
#============================================================
# Test passing dictionary or tuple
self.options = {'end_with_newline': True, 'eol': '\r\n' }
test_fragment(None, '\r\n')
self.options = {'end-with-newline': True}
test_fragment(None, '\n')
self.options = {'end-with-newline': False}
test_fragment(None, '')
self.options = ( ('end-with-newline', True), ('eol', '\r') )
test_fragment(None, '\r')
self.reset_options()
#============================================================
self.options.indent_size = 1
@ -1274,20 +1293,27 @@ class TestJSBeautifier(unittest.TestCase):
self.assertMultiLineEqual(
jsbeautifier.beautify(expectation, self.options), expectation)
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n'
expectation = expectation.replace('\n', '\r\n')
self.assertMultiLineEqual(
jsbeautifier.beautify(input, self.options), expectation)
if input and input.find('\n') != -1:
input = input.replace('\n', '\r\n')
if self.options is None or not isinstance(self.options, (dict, tuple)):
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n'
expectation = expectation.replace('\n', '\r\n')
self.options.disabled = True
self.assertMultiLineEqual(
jsbeautifier.beautify(input, self.options), input or '')
self.assertMultiLineEqual(
jsbeautifier.beautify('\n\n' + expectation, self.options), '\n\n' + expectation)
self.options.disabled = False;
self.assertMultiLineEqual(
jsbeautifier.beautify(input, self.options), expectation)
# Ensure support for auto eol detection
self.options.eol = 'auto'
self.assertMultiLineEqual(
jsbeautifier.beautify(input, self.options), expectation)
self.options.eol = '\n'
if input and input.find('\n') != -1:
input = input.replace('\n', '\r\n')
self.assertMultiLineEqual(
jsbeautifier.beautify(input, self.options), expectation)
# Ensure support for auto eol detection
self.options.eol = 'auto'
self.assertMultiLineEqual(
jsbeautifier.beautify(input, self.options), expectation)
self.options.eol = '\n'
def wrap(self, text):
return self.wrapregex.sub(' \\1', text)

View File

@ -31,10 +31,7 @@ exports.test_data = {
{ name: "indent_size", value: "4" },
{ name: "indent_char", value: "' '" },
{ name: "preserve_newlines", value: "true" },
{ name: "jslint_happy", value: "false" },
{ name: "keep_array_indentation", value: "false" },
{ name: "brace_style", value: "'collapse'" },
{ name: "operator_position", value: "'before-newline'" }
{ name: "jslint_happy", value: "false" }
],
groups: [{
name: "Unicode Support",
@ -47,6 +44,9 @@ exports.test_data = {
" ' + unicode_char(228) + 'rgerlich: true",
"};"
]
}, {
input_: "var' + unicode_char(160) + unicode_char(3232) + '_' + unicode_char(3232) + ' = \"hi\";",
output: "var ' + unicode_char(3232) + '_' + unicode_char(3232) + ' = \"hi\";"
}]
}, {
name: "Test template and continuation strings",
@ -187,6 +187,81 @@ exports.test_data = {
{ fragment: true, input: ' \n\nreturn .5\n\n\n\n', output: ' return .5{{eof}}' },
{ fragment: true, input: '\n', output: '{{eof}}' }
]
}, {
name: "Support Indent Level Options and Base Indent Autodetection",
description: "If user specifies indent level, use it. If not, autodetect indent level from starting whitespace.",
matrix: [{
options: [],
input_start_indent: ' ',
output_start_of_base: ' ',
i: ' '
}, {
options: [
{ name: "indent_level", value: "0" }
],
input_start_indent: ' ',
output_start_of_base: ' ',
i: ' '
}, {
options: [
{ name: "indent_level", value: "1" }
],
input_start_indent: ' ',
output_start_of_base: ' ',
i: ' '
}, {
options: [
{ name: "indent_level", value: "2" }
],
input_start_indent: '',
output_start_of_base: ' ',
i: ' '
}, {
options: [
{ name: "indent_with_tabs", value: "true" },
{ name: "indent_level", value: "2" }
],
input_start_indent: '',
output_start_of_base: '\t\t',
i: '\t'
}, {
options: [
{ name: "indent_level", value: "0" }
],
input_start_indent: '\t ',
output_start_of_base: '\t ',
i: ' '
}],
tests: [
{ fragment: true, input: '{{input_start_indent}}a', output: '{{output_start_of_base}}a' },
{
fragment: true,
input: [
'{{input_start_indent}}function test(){',
' console.log("this is a test");',
'}'
],
output: [
'{{output_start_of_base}}function test() {',
'{{output_start_of_base}}{{i}}console.log("this is a test");',
'{{output_start_of_base}}}'
]
}, {
fragment: true,
input: [
'{{input_start_indent}}// This is a random comment',
'function test(){',
' console.log("this is a test");',
'}'
],
output: [
'{{output_start_of_base}}// This is a random comment',
'{{output_start_of_base}}function test() {',
'{{output_start_of_base}}{{i}}console.log("this is a test");',
'{{output_start_of_base}}}'
]
}
]
}, {
name: "Support simple language specific option inheritance/overriding",
description: "Support simple language specific option inheritance/overriding",
@ -254,6 +329,17 @@ exports.test_data = {
},
// brace_style collapse - Shouldn't preserve if no newlines (uses collapse styling)
{
options: [],
ibo: '',
iao: '',
ibc: '',
iac: '',
obo: ' ',
oao: '\n ',
obc: '\n',
oac: ' '
},
{
options: [
{ name: "brace_style", value: "'collapse'" }
@ -298,16 +384,10 @@ exports.test_data = {
output: 'if (1)<obo>{<oao>2<obc>}<oac>else<obo>{<oao>3<obc>}'
},
{
input: 'try<ibo>{<iao>a();<ibc>}<iac>' +
'catch(b)<ibo>{<iao>c();<ibc>}<iac>' +
'catch(d)<ibo>{}<iac>' +
'finally<ibo>{<iao>e();<ibc>}',
input: 'try<ibo>{<iao>a();<ibc>}<iac>' + 'catch(b)<ibo>{<iao>c();<ibc>}<iac>' + 'catch(d)<ibo>{}<iac>' + 'finally<ibo>{<iao>e();<ibc>}',
output:
// expected
'try<obo>{<oao>a();<obc>}<oac>' +
'catch (b)<obo>{<oao>c();<obc>}<oac>' +
'catch (d)<obo>{}<oac>' +
'finally<obo>{<oao>e();<obc>}'
'try<obo>{<oao>a();<obc>}<oac>' + 'catch (b)<obo>{<oao>c();<obc>}<oac>' + 'catch (d)<obo>{}<oac>' + 'finally<obo>{<oao>e();<obc>}'
}
]
}, {
@ -477,8 +557,24 @@ exports.test_data = {
' .f();',
'});'
]
},
{
comment: 'regression test for fix #1533',
unchanged: [
'angular.module("test").controller("testCtrl", function($scope) {',
' $scope.tnew;',
' $scope.toggle_tnew = function() {',
' $scope.mode = 0;',
' if (!$scope.tnew) {',
' $scope.tnew = {};',
' } else $scope.tnew = null;',
' }',
' $scope.fn = function() {',
' return null;',
' }',
'});'
]
}
]
}, {
name: "Space in parens tests",
@ -570,6 +666,12 @@ exports.test_data = {
}, {
name: "operator_position option - ensure no neswlines if preserve_newlines is false",
matrix: [{
options: [
// test for default
// { name: "operator_position", value: "'before-newline'" },
{ name: "preserve_newlines", value: "false" }
]
}, {
options: [
{ name: "operator_position", value: "'before-newline'" },
{ name: "preserve_newlines", value: "false" }
@ -592,7 +694,17 @@ exports.test_data = {
output: inputlib.operator_position.sanity
}]
}, {
name: "operator_position option - set to 'before-newline' (default value)",
name: 'operator_position option - set to "before-newline" (default value)',
matrix: [{
options: [
// test for default
// { name: "operator_position", value: "'before-newline'" }
]
}, {
options: [
{ name: "operator_position", value: "'before-newline'" }
]
}],
tests: [{
comment: 'comprehensive, various newlines',
input: inputlib.operator_position.comprehensive,
@ -660,7 +772,7 @@ exports.test_data = {
]
}]
}, {
name: "operator_position option - set to 'after_newline'",
name: 'operator_position option - set to "after_newline"',
options: [{
name: "operator_position",
value: "'after-newline'"
@ -731,7 +843,7 @@ exports.test_data = {
]
}]
}, {
name: "operator_position option - set to 'preserve-newline'",
name: 'operator_position option - set to "preserve-newline"',
options: [{
name: "operator_position",
value: "'preserve-newline'"
@ -858,6 +970,27 @@ exports.test_data = {
},
{
unchanged: "async x => x * 2;"
},
{
unchanged: [
'async function() {',
' const obj = {',
' a: 1,',
' b: await fn(),',
' c: 2',
' };',
'}'
]
},
{
unchanged: [
'const a = 1,',
' b = a ? await foo() : b,',
' c = await foo(),',
' d = 3,',
' e = (await foo()),',
' f = 4;'
]
}
]
}, {
@ -1456,12 +1589,16 @@ exports.test_data = {
comment: 'Directive: ignore',
unchanged: "/* beautify ignore:start */\n/* beautify ignore:end */"
},
{ unchanged: "/* beautify ignore:start */\n var a,,,{ 1;\n/* beautify ignore:end */" },
{ unchanged: "/* beautify ignore:start */\n var a,,,{ 1;\n /* beautify ignore:end */" },
{ unchanged: "var a = 1;\n/* beautify ignore:start */\n var a = 1;\n/* beautify ignore:end */" },
{ unchanged: "/* beautify ignore:start */ {asdklgh;y;+++;dd2d}/* beautify ignore:end */" },
{
input_: "var a = 1;\n/* beautify ignore:start */\n var a,,,{ 1;\n/* beautify ignore:end */",
output: "var a = 1;\n/* beautify ignore:start */\n var a,,,{ 1;\n/* beautify ignore:end */"
comment: 'ignore starts _after_ the start comment, ends after the end comment',
unchanged: "/* beautify ignore:start */ {asdklgh;y;+++;dd2d}/* beautify ignore:end */"
},
{ unchanged: "/* beautify ignore:start */ {asdklgh;y;+++;dd2d} /* beautify ignore:end */" },
{
input_: "var a = 1;\n/* beautify ignore:start */\n var a,,,{ 1;\n/*beautify ignore:end*/",
output: "var a = 1;\n/* beautify ignore:start */\n var a,,,{ 1;\n/*beautify ignore:end*/"
},
{
input_: "var a = 1;\n /* beautify ignore:start */\n var a,,,{ 1;\n/* beautify ignore:end */",
@ -1773,28 +1910,39 @@ exports.test_data = {
{ name: "space_after_anon_function", value: "true" }
],
f: ' ',
c: ''
c: '',
nf: ''
}, {
options: [
{ name: "jslint_happy", value: "true" },
{ name: "space_after_anon_function", value: "false" }
],
f: ' ',
c: ''
c: '',
nf: ''
}, {
options: [
{ name: "jslint_happy", value: "false" },
{ name: "space_after_anon_function", value: "true" }
],
f: ' ',
c: ' '
c: ' ',
nf: ''
}, {
options: [
{ name: "jslint_happy", value: "false" },
{ name: "space_after_anon_function", value: "false" }
],
f: '',
c: ' '
c: ' ',
nf: ''
}, {
options: [
{ name: "space_after_named_function", value: "true" }
],
f: '',
c: ' ',
nf: ' '
}
@ -1807,10 +1955,18 @@ exports.test_data = {
input_: 'x();\n\nfunction(){}',
output: 'x();\n\nfunction{{f}}() {}'
},
{
input_: 'x();\n\nfunction y(){}',
output: 'x();\n\nfunction y{{nf}}() {}'
},
{
input_: 'x();\n\nvar x = {\nx: function(){}\n}',
output: 'x();\n\nvar x = {\n x: function{{f}}() {}\n}'
},
{
input_: 'x();\n\nvar x = {\nx: function y(){}\n}',
output: 'x();\n\nvar x = {\n x: function y{{nf}}() {}\n}'
},
{
input_: 'function () {\n var a, b, c, d, e = [],\n f;\n}',
output: 'function{{f}}() {\n var a, b, c, d, e = [],\n f;\n}'
@ -1834,6 +1990,10 @@ exports.test_data = {
input_: 'var a2, b2, c2, d2 = 0, c = function() {}, d = \\\'\\\';',
output: 'var a2, b2, c2, d2 = 0,\n c = function{{f}}() {},\n d = \\\'\\\';'
},
{
input_: 'var a2, b2, c2, d2 = 0, c = function yoohoo() {}, d = \\\'\\\';',
output: 'var a2, b2, c2, d2 = 0,\n c = function yoohoo{{nf}}() {},\n d = \\\'\\\';'
},
{
input_: 'var a2, b2, c2, d2 = 0, c = function() {},\nd = \\\'\\\';',
output: 'var a2, b2, c2, d2 = 0,\n c = function{{f}}() {},\n d = \\\'\\\';'
@ -1842,8 +2002,26 @@ exports.test_data = {
input_: 'var o2=$.extend(a);function(){alert(x);}',
output: 'var o2 = $.extend(a);\n\nfunction{{f}}() {\n alert(x);\n}'
},
{ input: 'function*() {\n yield 1;\n}', output: 'function*{{f}}() {\n yield 1;\n}' },
{ unchanged: 'function* x() {\n yield 1;\n}' }
{
input_: 'var o2=$.extend(a);function yoohoo(){alert(x);}',
output: 'var o2 = $.extend(a);\n\nfunction yoohoo{{nf}}() {\n alert(x);\n}'
},
{
input: 'function*() {\n yield 1;\n}',
output: 'function*{{f}}() {\n yield 1;\n}'
},
{
input: 'function* yoohoo() {\n yield 1;\n}',
output: 'function* yoohoo{{nf}}() {\n yield 1;\n}'
},
{
input: 'function* x() {\n yield 1;\n}',
output: 'function* x{{nf}}() {\n yield 1;\n}'
},
{
input: 'async x() {\n yield 1;\n}',
output: 'async x{{nf}}() {\n yield 1;\n}'
}
]
}, {
name: "Regression tests",
@ -2150,6 +2328,19 @@ exports.test_data = {
'}'
]
},
{
comment: "Issue 1544 - Typescript declare formatting (no newline).",
unchanged: [
'declare const require: any;',
'declare function greet(greeting: string): void;',
'declare var foo: number;',
'declare namespace myLib {',
' function makeGreeting(s: string): string;',
' let numberOfGreetings: number;',
'}',
'declare let test: any;'
]
},
{
unchanged: [
'interface Test {',
@ -2742,8 +2933,7 @@ exports.test_data = {
' var obj = {<oao>' + //NL in templates
'<oaot><oaot>a: function() { console.log("test"); },',
' b()<obo><obot><obot>{<oao>' + //NL in templates
'<oaot><oaot><oaot>console.log("test2");' +
'<obc> }' + //NL in templates
'<oaot><oaot><oaot>console.log("test2");' + '<obc> }' + //NL in templates
'<obc> };' + //NL in templates
'<obc>}'
]
@ -2787,6 +2977,11 @@ exports.test_data = {
'}'
]
},
{
comment: "Issue #1197 - dynamic import() arrow syntax",
input: 'frontend = Async(() => import("../frontend").then(m => m.default ))',
output: 'frontend = Async(() => import("../frontend").then(m => m.default))'
},
{
comment: "Issue 858 - from is a keyword only after import",
unchanged: [
@ -3004,6 +3199,9 @@ exports.test_data = {
{ unchanged: 'a[1]()', comment: 'another magic function call' },
{ input: 'if(a){b();}else if(c) foo();', output: "if (a) {\n b();\n} else if (c) foo();" },
{ input: 'switch(x) {case 0: case 1: a(); break; default: break}', output: "switch (x) {\n case 0:\n case 1:\n a();\n break;\n default:\n break\n}" },
{ input: 'switch(x) {default: case 1: a(); break; case 0: break}', output: "switch (x) {\n default:\n case 1:\n a();\n break;\n case 0:\n break\n}" },
{ input: 'switch(x){case -1:break;case !y:break;}', output: 'switch (x) {\n case -1:\n break;\n case !y:\n break;\n}' },
{ unchanged: 'a !== b' },
{ input: 'if (a) b(); else c();', output: "if (a) b();\nelse c();" },

View File

@ -26,6 +26,8 @@
SOFTWARE.
*/
'use strict';
var fs = require('fs');
var mustache = require('mustache');
var path = require('path');
@ -100,13 +102,11 @@ function set_formatters(data, test_method, comment_mark) {
return function(text, render) {
var outputs = [];
// text is ignored for this
for (var name in context) {
if (name === 'options') {
continue;
}
if (context.hasOwnProperty(name)) {
outputs.push(name + ' = "' + context[name].replace(/\n/g, '\\n').replace(/\t/g, '\\t') + '"');
if (context.options) {
var item;
for (var x = 0; x < context.options.length; x++) {
item = context.options[x];
outputs.push(item.name + ' = "' + item.value.replace(/\n/g, '\\n').replace(/\t/g, '\\t').replace(/[']/g, "\"") + '"');
}
}
return render(outputs.join(', '));

3
test/resources/github-min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3634
test/resources/github.html Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
T(e){var C,T,E,x,A,M,P,D,N,z,B=[],H={},j={},R={},L={},q=!1,I=null,F=null,O=null,U=0,X=[],Y=null,V=null,W=null,G=!1,Q=!1,$=10,K=30,J=!0,Z=!1,et=!1,tt=!1,nt=0,it=0,at=!1;function ot(e){e=e.replace(/https?:\/\/(m\.)?vk\.com\/([^#]+#\/)?/,"");for(var r,s,l,c=Q,u=[e,s=t(e,c)||e,l=n(e,c)||e,i(s),a(l)],d={},p=[],f=0,h=u.length;h>f;f++)u[f]&&!d[u[f]]&&(d[u[f]]=!0,r=o(u[f]),r=r.replace(/[å¸]/gi,"[å¸]").replace(/(e|yo)/gi,"(?:e|yo)"),p.push(new RegExp("(^|\\s|\\(|>)("+r+")","gi")));return p}function rt(e,t,n){if(clearTimeout(F),!n)return F=setTimeout(function(){rt(e,t,!0)},10),!1;var i=r(e)||"";if(I==i)return!1;var a,o="_"+i,l=j[o],c=ot(i);if(!l&&i.length>2&&j["_"+i.slice(0,-2)]){var u="_"+i.slice(0,-2);j[u]&&L[u]&&!j[u].length&&(!tt||tt&&!R[u])&&(L[o]=!0,l=j[o]=[],tt&&(R[o]=""))}if(!l){l=[];var d=0;if(!i&&B.length)for(var p=U,f=U+Math.min($,B.length);f>p;p++)l.push([B[p]]),d++;else{for(var h={},_=0,p=0,f=B.length;f>p;p++){var v=B[p];if(H[v]){if(h[v]=!0,(a=lt(c,H[v][0]))&&(l.push([v,a]),++_>=K))

View File

@ -21,18 +21,9 @@ build_js()
# Wrap webkit output into an non-breaking form.
# In an upcoming verion these will be replaced with standard webpack umd
cat ./tools/template/beautify.begin.js > ./js/lib/beautify.js
cat ./dist/legacy_beautify_js.js >> ./js/lib/beautify.js
cat ./tools/template/beautify.end.js >> ./js/lib/beautify.js
cat ./tools/template/beautify-css.begin.js > ./js/lib/beautify-css.js
cat ./dist/legacy_beautify_css.js >> ./js/lib/beautify-css.js
cat ./tools/template/beautify-css.end.js >> ./js/lib/beautify-css.js
cat ./tools/template/beautify-html.begin.js > ./js/lib/beautify-html.js
cat ./dist/legacy_beautify_html.js >> ./js/lib/beautify-html.js
cat ./tools/template/beautify-html.end.js >> ./js/lib/beautify-html.js
sed '/GENERATED_BUILD_OUTPUT/ r ./build/legacy/legacy_beautify_js.js' <./tools/template/beautify.wrapper.js >./js/lib/beautify.js || exit 1
sed '/GENERATED_BUILD_OUTPUT/ r ./build/legacy/legacy_beautify_css.js' <./tools/template/beautify-css.wrapper.js >./js/lib/beautify-css.js || exit 1
sed '/GENERATED_BUILD_OUTPUT/ r ./build/legacy/legacy_beautify_html.js' <./tools/template/beautify-html.wrapper.js >./js/lib/beautify-html.js || exit 1
}
build_beautify()
@ -62,8 +53,7 @@ build_beautify()
$PROJECT_DIR/js/bin/css-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/web/common-style.css || exit 1
# html not ready yet
# $PROJECT_DIR/js/bin/html-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r index.html
$PROJECT_DIR/js/bin/html-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r index.html
build_js
}

View File

@ -13,45 +13,35 @@ esac
release_python()
{
cd $SCRIPT_DIR/..
git checkout -B release origin/release
git clean -xfd || exit 1
echo "__version__ = '$NEW_VERSION'" > python/jsbeautifier/__version__.py
git commit -am "Python $NEW_VERSION"
cd python
# python setup.py register -r pypi
python setup.py sdist || exit 1
python -m twine upload dist/* || exit 1
git push
}
release_node()
{
cd $SCRIPT_DIR/..
git checkout -B release origin/release
git clean -xfd || exit 1
make js || exit 1
npm version $NEW_VERSION
unset NPM_TAG
if [[ $NEW_VERSION =~ .*(rc|beta).* ]]; then
NPM_TAG='--tag next'
fi
npm publish . $NPM_TAG || exit 1
git push
git push --tags
}
release_web()
{
cd $SCRIPT_DIR/..
local ORIGINAL_BRANCH
ORIGINAL_BRANCH=$(git branch | grep '[*] .*' | awk '{print $2}')
git clean -xfd || exit 1
git fetch || exit 1
git checkout -B gh-pages origin/gh-pages || exit 1
git merge origin/master --no-edit || exit 1
make js || exit 1
git add -f js/lib/ || exit 1
git commit -m "Built files for $NEW_VERSION"
git merge origin/release --no-edit || exit 1
git push || exit 1
git checkout $ORIGINAL_BRANCH
git checkout master
}
sedi() {
@ -64,17 +54,39 @@ sedi() {
fi
}
update_readme_versions()
update_versions()
{
git fetch --all || exit 1
git checkout master || exit 1
git reset --hard origin/master || exit 1
git clean -xfd || exit 1
#sedi -E 's@(cdn.rawgit.+beautify/v)[^/]+@\1'$NEW_VERSION'@' README.md
npm version --no-git-tag-version $NEW_VERSION
sedi -E 's@(cdn.rawgit.+beautify/v)[^/]+@\1'$NEW_VERSION'@' README.md
sedi -E 's@(cdnjs.cloudflare.+beautify/)[^/]+@\1'$NEW_VERSION'@' README.md
sedi -E 's/\((README\.md:.js-beautify@).+\)/(\1'$NEW_VERSION')/' README.md
git add README.md
git commit -m "Bump version numbers in README.md"
echo "__version__ = '$NEW_VERSION'" > python/jsbeautifier/__version__.py
git add .
git commit -am "Bump version numbers for $NEW_VERSION"
git push
}
update_release_branch()
{
git reset --hard
git clean -xfd
git checkout -B release origin/release || exit 1
git merge origin/master --no-edit || exit 1
make js || exit 1
git add -f js/lib/ || exit 1
git commit -m "Release: $NEW_VERSION"
git tag "v$NEW_VERSION" || exit 1
git push || exit 1
git push --tags
}
main()
{
cd $SCRIPT_DIR/..
@ -82,11 +94,9 @@ main()
local NEW_VERSION=$1
NEW_VERSION=$1
git checkout master
git reset --hard
git clean -xfd
update_versions
update_release_branch
update_readme_versions
release_python
release_node
release_web

View File

@ -1,23 +0,0 @@
var css_beautify = legacy_beautify_css;
/* Footer */
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define([], function() {
return {
css_beautify: css_beautify
};
});
} else if (typeof exports !== "undefined") {
// Add support for CommonJS. Just put this file somewhere on your require.paths
// and you will be able to `var html_beautify = require("beautify").html_beautify`.
exports.css_beautify = css_beautify;
} else if (typeof window !== "undefined") {
// If we're running a web page and don't have either of the above, add our one global
window.css_beautify = css_beautify;
} else if (typeof global !== "undefined") {
// If we don't even have window, try global.
global.css_beautify = css_beautify;
}
}());

View File

@ -1,4 +1,3 @@
/*jshint curly:false, eqeqeq:true, laxbreak:true, noempty:false */
/* AUTO-GENERATED. DO NOT MODIFY. */
/*
@ -32,8 +31,8 @@
Written by Harutyun Amirjanyan, (amirjanyan@gmail.com)
Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
http://jsbeautifier.org/
Based on code initially developed by: Einar Lielmanis, <einar@beautifier.io>
https://beautifier.io/
Usage:
css_beautify(source_text);
@ -64,3 +63,28 @@
// http://www.w3.org/TR/css3-syntax/
(function() {
/* GENERATED_BUILD_OUTPUT */
var css_beautify = legacy_beautify_css;
/* Footer */
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define([], function() {
return {
css_beautify: css_beautify
};
});
} else if (typeof exports !== "undefined") {
// Add support for CommonJS. Just put this file somewhere on your require.paths
// and you will be able to `var html_beautify = require("beautify").html_beautify`.
exports.css_beautify = css_beautify;
} else if (typeof window !== "undefined") {
// If we're running a web page and don't have either of the above, add our one global
window.css_beautify = css_beautify;
} else if (typeof global !== "undefined") {
// If we don't even have window, try global.
global.css_beautify = css_beautify;
}
}());

View File

@ -1,37 +0,0 @@
var style_html = legacy_beautify_html;
/* Footer */
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define(["require", "./beautify", "./beautify-css"], function(requireamd) {
var js_beautify = requireamd("./beautify");
var css_beautify = requireamd("./beautify-css");
return {
html_beautify: function(html_source, options) {
return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
}
};
});
} else if (typeof exports !== "undefined") {
// Add support for CommonJS. Just put this file somewhere on your require.paths
// and you will be able to `var html_beautify = require("beautify").html_beautify`.
var js_beautify = require('./beautify.js');
var css_beautify = require('./beautify-css.js');
exports.html_beautify = function(html_source, options) {
return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
};
} else if (typeof window !== "undefined") {
// If we're running a web page and don't have either of the above, add our one global
window.html_beautify = function(html_source, options) {
return style_html(html_source, options, window.js_beautify, window.css_beautify);
};
} else if (typeof global !== "undefined") {
// If we don't even have window, try global.
global.html_beautify = function(html_source, options) {
return style_html(html_source, options, global.js_beautify, global.css_beautify);
};
}
}());

View File

@ -1,4 +1,3 @@
/*jshint curly:false, eqeqeq:true, laxbreak:true, noempty:false */
/* AUTO-GENERATED. DO NOT MODIFY. */
/*
@ -32,8 +31,8 @@
Written by Nochum Sossonko, (nsossonko@hotmail.com)
Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
http://jsbeautifier.org/
Based on code initially developed by: Einar Lielmanis, <einar@beautifier.io>
https://beautifier.io/
Usage:
style_html(html_source);
@ -74,3 +73,42 @@
*/
(function() {
/* GENERATED_BUILD_OUTPUT */
var style_html = legacy_beautify_html;
/* Footer */
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define(["require", "./beautify", "./beautify-css"], function(requireamd) {
var js_beautify = requireamd("./beautify");
var css_beautify = requireamd("./beautify-css");
return {
html_beautify: function(html_source, options) {
return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
}
};
});
} else if (typeof exports !== "undefined") {
// Add support for CommonJS. Just put this file somewhere on your require.paths
// and you will be able to `var html_beautify = require("beautify").html_beautify`.
var js_beautify = require('./beautify.js');
var css_beautify = require('./beautify-css.js');
exports.html_beautify = function(html_source, options) {
return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
};
} else if (typeof window !== "undefined") {
// If we're running a web page and don't have either of the above, add our one global
window.html_beautify = function(html_source, options) {
return style_html(html_source, options, window.js_beautify, window.css_beautify);
};
} else if (typeof global !== "undefined") {
// If we don't even have window, try global.
global.html_beautify = function(html_source, options) {
return style_html(html_source, options, global.js_beautify, global.css_beautify);
};
}
}());

View File

@ -1,21 +0,0 @@
var js_beautify = legacy_beautify_js;
/* Footer */
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define([], function() {
return { js_beautify: js_beautify };
});
} else if (typeof exports !== "undefined") {
// Add support for CommonJS. Just put this file somewhere on your require.paths
// and you will be able to `var js_beautify = require("beautify").js_beautify`.
exports.js_beautify = js_beautify;
} else if (typeof window !== "undefined") {
// If we're running a web page and don't have either of the above, add our one global
window.js_beautify = js_beautify;
} else if (typeof global !== "undefined") {
// If we don't even have window, try global.
global.js_beautify = js_beautify;
}
}());

View File

@ -1,6 +1,4 @@
/*jshint curly:false, eqeqeq:true, laxbreak:true, noempty:false */
/* AUTO-GENERATED. DO NOT MODIFY. */
/* see js/src/javascript/index.js */
/*
The MIT License (MIT)
@ -31,12 +29,12 @@
---------------
Written by Einar Lielmanis, <einar@jsbeautifier.org>
http://jsbeautifier.org/
Written by Einar Lielmanis, <einar@beautifier.io>
https://beautifier.io/
Originally converted to javascript by Vital, <vital76@gmail.com>
"End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
Parsing improvements for brace-less statements by Liam Newman <bitwiseman@gmail.com>
Parsing improvements for brace-less statements by Liam Newman <bitwiseman@beautifier.io>
Usage:
@ -88,3 +86,27 @@
*/
(function() {
/* GENERATED_BUILD_OUTPUT */
var js_beautify = legacy_beautify_js;
/* Footer */
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define([], function() {
return { js_beautify: js_beautify };
});
} else if (typeof exports !== "undefined") {
// Add support for CommonJS. Just put this file somewhere on your require.paths
// and you will be able to `var js_beautify = require("beautify").js_beautify`.
exports.js_beautify = js_beautify;
} else if (typeof window !== "undefined") {
// If we're running a web page and don't have either of the above, add our one global
window.js_beautify = js_beautify;
} else if (typeof global !== "undefined") {
// If we don't even have window, try global.
global.js_beautify = js_beautify;
}
}());

View File

@ -1,3 +1,4 @@
/*jshint strict:false, node:false */
/*exported run_tests, read_settings_from_cookie, beautify, submitIssue */
var the = {
use_codemirror: !window.location.href.match(/without-codemirror/),
@ -20,6 +21,7 @@ requirejs(['beautifier'],
the.beautifier = beautifier;
});
function any(a, b) {
return a || b;
}
@ -95,26 +97,28 @@ function store_settings_to_cookie() {
}
function unpacker_filter(source) {
var trailing_comments = '',
var leading_comments = '',
comment = '',
unpacked = '',
found = false;
// cut trailing comments
// cuts leading comments
do {
found = false;
if (/^\s*\/\*/.test(source)) {
found = true;
comment = source.substr(0, source.indexOf('*/') + 2);
source = source.substr(comment.length).replace(/^\s+/, '');
trailing_comments += comment + "\n";
source = source.substr(comment.length);
leading_comments += comment;
} else if (/^\s*\/\//.test(source)) {
found = true;
comment = source.match(/^\s*\/\/.*/)[0];
source = source.substr(comment.length).replace(/^\s+/, '');
trailing_comments += comment + "\n";
source = source.substr(comment.length);
leading_comments += comment;
}
} while (found);
leading_comments += '\n';
source = source.replace(/^\s+/, '');
var unpackers = [P_A_C_K_E_R, Urlencoded, JavascriptObfuscator /*, MyObfuscate*/ ];
for (var i = 0; i < unpackers.length; i++) {
@ -126,7 +130,7 @@ function unpacker_filter(source) {
}
}
return trailing_comments + source;
return leading_comments + source;
}

View File

@ -1,3 +1,4 @@
/*jshint node:false, jquery:true, strict:false */
$(function() {
read_settings_from_cookie();

View File

@ -13,7 +13,7 @@ var legacy = {
output: {
library: 'legacy_[name]',
filename: 'legacy_[name].js',
path: path.resolve(__dirname, 'dist')
path: path.resolve(__dirname, 'build/legacy')
}
};