Merge remote-tracking branch 'beautify-web/master'

This commit is contained in:
Liam Newman 2019-05-01 17:54:41 -07:00
commit 6e710e1413
128 changed files with 40776 additions and 18574 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/*"

19
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,19 @@
# Description
- [ ] Source branch in your fork has meaningful name (not `master`)
Fixes Issue:
# Before Merge Checklist
These items can be completed after PR is created.
(Check any items that are not applicable (NA) for this PR)
- [ ] JavaScript implementation
- [ ] Python implementation (NA if HTML beautifier)
- [ ] Added Tests to data file(s)
- [ ] Added command-line option(s) (NA if
- [ ] README.md documents new feature/option(s)

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,19 +0,0 @@
language: python
python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
node_js:
- 'node'
env:
- TRAVIS_NODE_VERSION="6"
install:
- rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION
- npm install
script: "make ci"

View File

@ -1,42 +1,292 @@
# Changelog
## v1.8.0-rc2
## v1.10.0
### Description
# Description
* Added `templating` setting to control when template languages are recognized. All languages are off by default in Javascript and on by default in HTML.
* Thanks to @HanabishiRecca, @averydev, @kalbasit, @asteinha for contributions
https://github.com/beautify-web/js-beautify/compare/v1.9.1...v1.10.0
### Closed Issues
* beautifying scss selector with colon in it adds space ([#1667](https://github.com/beautify-web/js-beautify/issues/1667))
* Javascript multiline comments duplicates ([#1663](https://github.com/beautify-web/js-beautify/issues/1663))
* Tokenizer crashes if the input terminates with a dot character. ([#1658](https://github.com/beautify-web/js-beautify/issues/1658))
* stop reformatting valid css \\! into invalid \\ ! ([#1656](https://github.com/beautify-web/js-beautify/pull/1656))
* wrong indent for unclosed <? - need to support disabling templating ([#1647](https://github.com/beautify-web/js-beautify/issues/1647))
* Beautify inserts space before exclamation mark in comment <!-- in css <style> ([#1641](https://github.com/beautify-web/js-beautify/issues/1641))
* 'less' mixins parameter formatting problem ([#1582](https://github.com/beautify-web/js-beautify/issues/1582))
* Change css tests to use 4 space indenting instead of tabs ([#1527](https://github.com/beautify-web/js-beautify/issues/1527))
* Braces after case get pushed onto new line ([#1357](https://github.com/beautify-web/js-beautify/issues/1357))
* Extra space in pseudo-elements and pseudo-classes selectors ([#1233](https://github.com/beautify-web/js-beautify/issues/1233))
* LESS formatting - mixins with multiple variables ([#1018](https://github.com/beautify-web/js-beautify/issues/1018))
* Bug in less format ([#842](https://github.com/beautify-web/js-beautify/issues/842))
## v1.9.1
### Description
### Closed Issues
* nested table not beautified correctly ([#1649](https://github.com/beautify-web/js-beautify/issues/1649))
* Add an option to preserve indentation on empty lines ([#1322](https://github.com/beautify-web/js-beautify/issues/1322))
## v1.9.0
### Description
# Description
* Fixed Tab indenting - when tabs indenting enabled, they are used universally. Also, tab size customizable: 8-space tabs would mean each tab is treated as 8 spaces. (#1294, #1551)
* Accurate line wrapping - Layout always wraps when line length exceed specified column, unless wrapping would not reduce line length. (#284, #1238)
* Improved Template handling in HTML - Go, Django, Handlebars, ERB/EJS/ASP, PHP (still only handlebars indenting) (#881, #1602, #1620)
* Improved Template handling in Javascript - ERB/EJS/ASP, PHP (no indenting, no Django or Handlebars due to potential syntax conflicts for curly braces) (#1377)
* Fixed indenting of mustache inverted conditionals (#1623 @e2tha-e)
* Fixed indenting for HTML tags with option end tags (#1213)
https://github.com/beautify-web/js-beautify/compare/v1.8.9...v1.9.0
### Closed Issues
* Incorrect indentation of `^` inverted section tags in Handlebars/Mustache code ([#1623](https://github.com/beautify-web/js-beautify/issues/1623))
* PHP In HTML Attributes ([#1620](https://github.com/beautify-web/js-beautify/issues/1620))
* DeanEdward python unpacker offset problem ([#1616](https://github.com/beautify-web/js-beautify/issues/1616))
* CLI on Windows doesn't accept -f - for stdin? ([#1609](https://github.com/beautify-web/js-beautify/issues/1609))
* HTML type attribute breaks JavaScript beautification? ([#1606](https://github.com/beautify-web/js-beautify/issues/1606))
* Use of global MODE before declaration caused uglify problem ([#1604](https://github.com/beautify-web/js-beautify/issues/1604))
* When building html tags using Mustache variables, extra whitespace is added after opening arrow ([#1602](https://github.com/beautify-web/js-beautify/issues/1602))
* <script type="text/html">isnot abled to be beautified ([#1591](https://github.com/beautify-web/js-beautify/issues/1591))
* _get_full_indent undefined ([#1590](https://github.com/beautify-web/js-beautify/issues/1590))
* Website "autodetect" setting doesn't distinguish css vs javascript ([#1565](https://github.com/beautify-web/js-beautify/issues/1565))
* Add setting to keep HTML tag text content unformatted or ignore custom delimiters ([#1560](https://github.com/beautify-web/js-beautify/issues/1560))
* HTML auto formatting using spaces instead of tabs ([#1551](https://github.com/beautify-web/js-beautify/issues/1551))
* Unclosed single quote in php tag causes formatting changes which break php code ([#1377](https://github.com/beautify-web/js-beautify/issues/1377))
* Using tabs when wrapping attributes and wrapping if needed ([#1294](https://github.com/beautify-web/js-beautify/issues/1294))
* HTML --wrap-attributes doesn't respect --wrap-line-length ([#1238](https://github.com/beautify-web/js-beautify/issues/1238))
* Bad indent level(HTML) ([#1213](https://github.com/beautify-web/js-beautify/issues/1213))
* js-beautify produces invalid code for variables with Unicode escape sequences ([#1211](https://github.com/beautify-web/js-beautify/issues/1211))
* support vuejs ([#1154](https://github.com/beautify-web/js-beautify/issues/1154))
* Go templates in HTML ([#881](https://github.com/beautify-web/js-beautify/issues/881))
* Better behavior for javascript --wrap-line-length ([#284](https://github.com/beautify-web/js-beautify/issues/284))
## v1.8.9
### Description
### Closed Issues
* Won't run from CLI - bad option name `files` ([#1583](https://github.com/beautify-web/js-beautify/issues/1583))
* in the .vue file `space_after_anon_function` is invalid ([#1425](https://github.com/beautify-web/js-beautify/issues/1425))
* Add function default_options() to beautifier.js ([#1364](https://github.com/beautify-web/js-beautify/issues/1364))
* fix: Missing space before function parentheses ? ([#1077](https://github.com/beautify-web/js-beautify/issues/1077))
* Support globs in CLI ([#787](https://github.com/beautify-web/js-beautify/issues/787))
## v1.8.8
### Description
### Closed Issues
* async function in object wrong indentation ([#1573](https://github.com/beautify-web/js-beautify/issues/1573))
## v1.8.7
### Description
### Closed Issues
* Add tests for html `indent_scripts` option ([#1518](https://github.com/beautify-web/js-beautify/issues/1518))
* Support dynamic import ([#1197](https://github.com/beautify-web/js-beautify/issues/1197))
* HTML: add an option to preserve manual wrapping of attributes ([#1125](https://github.com/beautify-web/js-beautify/issues/1125))
* js-beautify adds a space between # and include ([#1114](https://github.com/beautify-web/js-beautify/issues/1114))
* space_after_anon_function doesn't work with anon async functions ([#1034](https://github.com/beautify-web/js-beautify/issues/1034))
* Space before function arguments (space-after-function) (space-after-named-function) ([#608](https://github.com/beautify-web/js-beautify/issues/608))
## 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))
* preserve-newline doesn't work for text blocks inside tags ([#1352](https://github.com/beautify-web/js-beautify/issues/1352))
* 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))
* <button> with force-expand-multiline formatting bug ([#1335](https://github.com/beautify-web/js-beautify/issues/1335))
* js-beautify 1.7.5 breaks some correct JS code when run with -X ([#1334](https://github.com/beautify-web/js-beautify/issues/1334))
* 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))
* Negative numbers removes newlines in arrays ([#1288](https://github.com/beautify-web/js-beautify/issues/1288))
* 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))
* Generate js-beautify executable properly on windows when installed from PIP ([#1266](https://github.com/beautify-web/js-beautify/issues/1266))
* Add or preserve empty line between nested SCSS rules ([#1258](https://github.com/beautify-web/js-beautify/issues/1258))
* Create beta channel for releases ([#1255](https://github.com/beautify-web/js-beautify/issues/1255))
* Add install tests for packages ([#1254](https://github.com/beautify-web/js-beautify/issues/1254))
* Formatting slow when line wrap is set ([#1231](https://github.com/beautify-web/js-beautify/issues/1231))
* [!true && ...] Negated expressions in an array get collapsed into a single line ([#1229](https://github.com/beautify-web/js-beautify/issues/1229))
* 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))
* html.format.wrapAttributes on handlebars template ([#1199](https://github.com/beautify-web/js-beautify/issues/1199))
* Don't indent unclosed HTML tags containing server directives "<@" ([#1193](https://github.com/beautify-web/js-beautify/issues/1193))
* `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))
* Content deleted when formatting with indent_handlebars: true ([#1174](https://github.com/beautify-web/js-beautify/issues/1174))
* 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))
* Support underscore templates ([#1130](https://github.com/beautify-web/js-beautify/issues/1130))
* html-bar/handlebar {{else if}} block is indented ([#1123](https://github.com/beautify-web/js-beautify/issues/1123))
* Wrap line length, first line not correct ([#1122](https://github.com/beautify-web/js-beautify/issues/1122))
* TypeError: Cannot read property 'replace' of undefined ([#1120](https://github.com/beautify-web/js-beautify/issues/1120))
* Strange behaviours of formatting for double spans ([#1113](https://github.com/beautify-web/js-beautify/issues/1113))
* Missing space between "else" and "if". ([#1107](https://github.com/beautify-web/js-beautify/issues/1107))
* invalid indentation for html code ([#1098](https://github.com/beautify-web/js-beautify/issues/1098))
* HTML "select" tags have too much indentation ([#1097](https://github.com/beautify-web/js-beautify/issues/1097))
* Formatting breaks apart unquoted attribute ([#1094](https://github.com/beautify-web/js-beautify/issues/1094))
* HTML formatting wraps ending block tag for no reason with nested inline elements ([#1041](https://github.com/beautify-web/js-beautify/issues/1041))
* Ignore expressions in handlebars tags. ([#1040](https://github.com/beautify-web/js-beautify/issues/1040))
* not correctly joining lines for HTML ([#1033](https://github.com/beautify-web/js-beautify/issues/1033))
* Fails to format SVG files properly ([#1027](https://github.com/beautify-web/js-beautify/issues/1027))
* Template tags with new lines in them ([#1016](https://github.com/beautify-web/js-beautify/issues/1016))
* Span tags do not re-indent correctly ([#1010](https://github.com/beautify-web/js-beautify/issues/1010))
* Error in --eol processing in python ([#987](https://github.com/beautify-web/js-beautify/issues/987))
* Extra space added when quote is present ([#943](https://github.com/beautify-web/js-beautify/issues/943))
* weird formatting for HTML5 <tr> ([#882](https://github.com/beautify-web/js-beautify/issues/882))
* Respect non-breaking spaces ([#869](https://github.com/beautify-web/js-beautify/issues/869))
* Media Queries style issue ([#863](https://github.com/beautify-web/js-beautify/issues/863))
* Weird Beautify Style? ([#857](https://github.com/beautify-web/js-beautify/issues/857))
* "unformatted" paradigm broken, "unformatted" and "inline" are not the same ([#841](https://github.com/beautify-web/js-beautify/issues/841))
* Increment/Decrement Operator on object property extra indent on subsequent line ([#814](https://github.com/beautify-web/js-beautify/issues/814))
* Inconsistence of "newline_between_rules" with @import or @media ([#769](https://github.com/beautify-web/js-beautify/issues/769))
* Unexpected line break in "-1" ([#740](https://github.com/beautify-web/js-beautify/issues/740))
* Blank line before and after CSS / JS comments ([#736](https://github.com/beautify-web/js-beautify/issues/736))
* newline_between_rules support for Sass (enhancement) ([#657](https://github.com/beautify-web/js-beautify/issues/657))
* CSS comment spacing disregards `newline_between_rules`, `selector_separator_newline` ([#645](https://github.com/beautify-web/js-beautify/issues/645))
* HTML: wrap_line_length may produce buggy spaces ([#607](https://github.com/beautify-web/js-beautify/issues/607))
* Wrong code formatting using Handlebars ([#576](https://github.com/beautify-web/js-beautify/issues/576))
* option to ignore section or line in html ([#575](https://github.com/beautify-web/js-beautify/issues/575))
* Tokenize html before beautifying ([#546](https://github.com/beautify-web/js-beautify/issues/546))
* Extra newline is inserted after the comment line instead of before it ([#531](https://github.com/beautify-web/js-beautify/issues/531))
* html-beautify's max_preserve_newlines preserves one new line too much ([#517](https://github.com/beautify-web/js-beautify/issues/517))
* Disable/Skip HTML single-line comment ([#426](https://github.com/beautify-web/js-beautify/issues/426))
* Add tests for various javascript dependency loading libraries ([#360](https://github.com/beautify-web/js-beautify/issues/360))
* Formatting of @import broken ([#358](https://github.com/beautify-web/js-beautify/issues/358))
* newline removal seems not to work properly (in sublime text 3 on xp pro sp3) ([#348](https://github.com/beautify-web/js-beautify/issues/348))
## v1.7.5
@ -204,7 +454,7 @@ Added `content_unformatted` option (Thanks @arai-a)
## v1.6.4
### Description
* Fixed JSX multi-line root element handling
* Fixed JSX multi-line root element handling
* Fixed CSS Combinator spacing (NOTE: use `space_around_combinator` option)
* Fixed (more) CSS pseudo-class and pseudo-element selectors (Thanks @Konamiman!)
* Fixed Shorthand generator functions and `yield*` (Thanks @jgeurts!)
@ -331,6 +581,29 @@ Fixes for regressions found in 1.6.0
* Cannot copy more than 1000 characters out of CodeMirror buffer ([#768](https://github.com/beautify-web/js-beautify/issues/768))
* Missing 'var' in beautify-html.js; breaks strict mode ([#763](https://github.com/beautify-web/js-beautify/issues/763))
* Fix typo in the example javascript code of index.html ([#753](https://github.com/beautify-web/js-beautify/pull/753))
* Prevent incorrect wrapping of return statements. ([#751](https://github.com/beautify-web/js-beautify/pull/751))
* `js-beautify -X` breaks code with jsx spread attributes ([#734](https://github.com/beautify-web/js-beautify/issues/734))
* Unhelpful error when .jsbeautifyrc has invalid json ([#728](https://github.com/beautify-web/js-beautify/issues/728))
* Support for ES7 decorators ([#685](https://github.com/beautify-web/js-beautify/issues/685))
* Don't wrap XML declaration (or processing instructions) ([#683](https://github.com/beautify-web/js-beautify/pull/683))
* JSX/JS: Single field objects should not expand ([#674](https://github.com/beautify-web/js-beautify/issues/674))
* ES6 Module Loading Object Destructuring newlines ([#668](https://github.com/beautify-web/js-beautify/issues/668))
* Beautifying css tears @media in two lines, adds spaces in names with hyphens ([#656](https://github.com/beautify-web/js-beautify/issues/656))
* ES6 concise method not propely indented ([#647](https://github.com/beautify-web/js-beautify/issues/647))
* Extra newline inserted with `wrap_attributes` set to "force" ([#641](https://github.com/beautify-web/js-beautify/issues/641))
* `wrap_attributes` ignores `wrap_attributes_indent_size` and `wrap_line_length` when set to "auto" ([#640](https://github.com/beautify-web/js-beautify/issues/640))
* Error building on Windows ([#638](https://github.com/beautify-web/js-beautify/issues/638))
* Mixed shorthand function with arrow function in object literal is mis-formatted ([#602](https://github.com/beautify-web/js-beautify/issues/602))
* Space does not let save using "&:" ([#594](https://github.com/beautify-web/js-beautify/issues/594))
* Indenting is broken in some cases ([#578](https://github.com/beautify-web/js-beautify/issues/578))
* modified cli.js so that it can read from piped input by default ([#558](https://github.com/beautify-web/js-beautify/pull/558))
* Update release process instructions ([#543](https://github.com/beautify-web/js-beautify/issues/543))
* Publish v1.6.0 ([#542](https://github.com/beautify-web/js-beautify/issues/542))
* es6 destructuring ([#511](https://github.com/beautify-web/js-beautify/issues/511))
* NPM js-beautify: different treatment of "-" in command line ([#390](https://github.com/beautify-web/js-beautify/issues/390))
* Newline inserted after ES6 module import/export ([#382](https://github.com/beautify-web/js-beautify/issues/382))
* Option to preserve or inline "short objects" on a single line ([#315](https://github.com/beautify-web/js-beautify/issues/315))
* Format json in line ([#114](https://github.com/beautify-web/js-beautify/issues/114))
## v1.5.10
@ -371,7 +644,7 @@ Version jump due to release script tweaks
### Description
* JSX support!
* Alternative Newline Characters
* CSS and JS comment formatting fixes
* CSS and JS comment formatting fixes
* General bug fixing
@ -474,12 +747,12 @@ https://github.com/beautify-web/js-beautify/compare/v1.5.2...v1.5.3
* Do not break "x++ + y"
* function declaration inside array behaves the same as in expression
* Close String literals at newline
* Support handlebar syntax
* Support handlebar syntax
* Check `<script>` "type"-attribute
* Allow `<style>` and `<script>` tags to be unformatted
* Port css nesting fix to python
* Fix python six dependency
* Initial very cursory support for ES6 module, export, and import
* Initial very cursory support for ES6 module, export, and import
https://github.com/beautify-web/js-beautify/compare/v1.5.1...v1.5.2
@ -514,53 +787,12 @@ https://github.com/beautify-web/js-beautify/compare/v1.5.1...v1.5.2
* Cannot declare object literal properties with unquoted reserved words ([#440](https://github.com/beautify-web/js-beautify/issues/440))
* Do not put a space within `function*` generator functions. ([#428](https://github.com/beautify-web/js-beautify/issues/428))
* beautification of "nth-child" css fails csslint ([#418](https://github.com/beautify-web/js-beautify/issues/418))
## v1.5.1
### Description
Highlights:
* Fixes var declaration of objects and arrays to indent correctly (#256, #430)
* Support keywords as IdentifierNames such as foo.catch() (#309, #351,#368, #378)
* Improved indenting for statements (#289)
* Improved ES6 support - let, const, template strings, and "fat arrow"
* Support for non-ASCII characters in variable names (#305)
* Multiple fixes to requirejs support and added tests to protect in future
* Improved LESS support (still plenty of room for improvement in this area)
* Do not add space after !!
https://github.com/einars/js-beautify/compare/v1.4.2...v1.5.1
### Closed Issues
* Nested if statements not displayed correctly ([#450](https://github.com/beautify-web/js-beautify/issues/450))
* preserve_newlines always true ([#449](https://github.com/beautify-web/js-beautify/issues/449))
* line wrapping breaks in weird places ([#438](https://github.com/beautify-web/js-beautify/issues/438))
* Update dependencies to current versions ([#437](https://github.com/beautify-web/js-beautify/pull/437))
* Add support for ES6 template strings ([#434](https://github.com/beautify-web/js-beautify/pull/434))
* Fix #402: support ES6 fat arrow ([#433](https://github.com/beautify-web/js-beautify/pull/433))
* Ending brace missaligned when part of first definition in var line ([#430](https://github.com/beautify-web/js-beautify/issues/430))
* fixing disabled line wrapping for HTML ([#429](https://github.com/beautify-web/js-beautify/pull/429))
* Missing semi colon ([#420](https://github.com/beautify-web/js-beautify/issues/420))
* Fixed require.js support ([#416](https://github.com/beautify-web/js-beautify/pull/416))
* should not split the es6 operator '=>' ([#402](https://github.com/beautify-web/js-beautify/issues/402))
* fixed relative paths for require.js ([#387](https://github.com/beautify-web/js-beautify/pull/387))
* Support reserved words as property names ([#378](https://github.com/beautify-web/js-beautify/issues/378))
* Make the AMD API match the rest of the APIs ([#376](https://github.com/beautify-web/js-beautify/pull/376))
* Preserve newlines in html related to issue #307 ([#375](https://github.com/beautify-web/js-beautify/pull/375))
* Multi-line statements ([#374](https://github.com/beautify-web/js-beautify/issues/374))
* Reserved words used as property/function/variable identifiers are formatted incorrectly ([#368](https://github.com/beautify-web/js-beautify/issues/368))
* fixed problems with colon character ([#363](https://github.com/beautify-web/js-beautify/pull/363))
* require.JS paths are hardcoded in beautify-html.js ([#359](https://github.com/beautify-web/js-beautify/issues/359))
* Regression in p.a.c.ked file detection ([#357](https://github.com/beautify-web/js-beautify/issues/357))
* Fix Issue #339 ([#354](https://github.com/beautify-web/js-beautify/pull/354))
* Added single line comment support in less/sass for javascript parser ([#353](https://github.com/beautify-web/js-beautify/pull/353))
* Function named 'in' not formatting correctly ([#351](https://github.com/beautify-web/js-beautify/issues/351))
* CSS Pseudo element ([#346](https://github.com/beautify-web/js-beautify/issues/346))
* array closing brace error for return statements with keep_array_indentation ([#340](https://github.com/beautify-web/js-beautify/issues/340))
* CSS Beautifier: breaks :before and :after (regression) ([#339](https://github.com/beautify-web/js-beautify/issues/339))
* Publish v1.5.0 ([#335](https://github.com/beautify-web/js-beautify/issues/335))
* "keep array indentation" not working ([#333](https://github.com/beautify-web/js-beautify/issues/333))
* CSS Beautifier: support LESS/SASS line comments ([#326](https://github.com/beautify-web/js-beautify/issues/326))
* Incorrect formating with semicolon-less code ([#323](https://github.com/beautify-web/js-beautify/issues/323))
* comment breaks indent ([#413](https://github.com/beautify-web/js-beautify/issues/413))
* AngularJS inline templates are being corrupted! ([#385](https://github.com/beautify-web/js-beautify/issues/385))
* Beautify HTML: Setting inline JS and CSS to stay unformatted ([#383](https://github.com/beautify-web/js-beautify/issues/383))
* Spaces in function definition ([#372](https://github.com/beautify-web/js-beautify/issues/372))
* Chained code indents break at comment lines ([#314](https://github.com/beautify-web/js-beautify/issues/314))
* Handling of newlines around if/else/if statements ([#311](https://github.com/beautify-web/js-beautify/issues/311))
* Tags in javascript are being destroyed ([#117](https://github.com/beautify-web/js-beautify/issues/117))

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

@ -7,6 +7,15 @@ If you find a bug, please report it, including environment and examples of curre
## How to Make Changes (Implement Fixes and New Features)
Fixes and enhancements are totally welcome. We prefer you to file an issue before filing a PR, as this gives us chance to discuss design details, but feel free to dive right in.
### 0. Prereqisites for development
* 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.
### 1. Build and Test Locally
This repository holds two mostly identical implementations of the beautifiers: a JavaScript implementation and a Python implementation.
While developing, you may locally build and test the JavaScript or Python (or both). The HTML beautifier is only implemented in JavaScript.
@ -14,10 +23,11 @@ While developing, you may locally build and test the JavaScript or Python (or bo
* Familiarize yourself with the folder structure and code style before you dive in.
* Make changes to the implementation of your choice.
* If working in the JavaScript implementation:
* Run `make static` to see changes served locally at `http://localhost:8080`
* To load a debug (human readable) version of the beautifier, open `http://localhost:8080/?debug`
* Run `make js` to build and run unit tests
* Run `make static` to manually test changes locally at `http://localhost:8080`
* To load a debug (human readable) version of the beautifier source, open `http://localhost:8080/?debug`
* If working in the Python implementation:
* Run `make py`
* Run `make py` to build and run unit tests
* Add tests to `/test/data/*/test.js`.
* Run `make jstest` or `make pytest` to run style checks, and to generate and run tests.
* Include all changed files in your commit - The generated test files are checked in along with changes to the test data files.
@ -53,7 +63,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.
@ -64,8 +74,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
@ -110,6 +123,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,30 +32,36 @@ 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 && \
./jsbeautifier/tests/shell-test.sh
alltest: jstest pytest
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 +73,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 js/src/**/*) $(wildcard web/*.js) js/index.js tools/template/* webpack.config.js
$(SCRIPT_DIR)/build.sh js
@ -111,6 +117,7 @@ update: depends
$(BUILD_DIR)/node: package.json package-lock.json | $(BUILD_DIR)
@$(NODE) --version
$(NPM) install
$(NPM) --version
@touch $(BUILD_DIR)/node
$(BUILD_DIR)/python: python/setup.py | $(BUILD_DIR) $(BUILD_DIR)/virtualenv

View File

@ -1,6 +1,5 @@
# JS Beautifier
[![Build Status](https://api.travis-ci.org/beautify-web/js-beautify.svg?branch=master)](http://travis-ci.org/beautify-web/js-beautify)
[![Build status](https://ci.appveyor.com/api/projects/status/5bxmpvew5n3e58te/branch/master?svg=true)](https://ci.appveyor.com/project/beautify-web/js-beautify/branch/master)
[![Build Status](https://dev.azure.com/beautifier-io/js-beautify/_apis/build/status/beautify-web.js-beautify)](https://dev.azure.com/beautifier-io/js-beautify/_build/latest?definitionId=1)
[![PyPI version](https://img.shields.io/pypi/v/jsbeautifier.svg)](https://pypi.python.org/pypi/jsbeautifier)
[![CDNJS version](https://img.shields.io/cdnjs/v/js-beautify.svg)](https://cdnjs.com/libraries/js-beautify)
@ -15,10 +14,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 +61,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.10.0/beautify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.0/beautify-css.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.0/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.10.0/beautify.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.0/beautify-css.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.0/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.10.0/js/lib/beautify.js"></script>
<script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.10.0/js/lib/beautify-css.js"></script>
<script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.10.0/js/lib/beautify-html.js"></script>
```
Older versions are available by changing the version number.
@ -92,7 +91,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`.
@ -160,7 +159,7 @@ CLI Options:
-r, --replace Write output in-place, replacing input
-o, --outfile Write output to file (default stdout)
--config Path to config file
--type [js|css|html] ["js"]
--type [js|css|html] ["js"] Select beautifier type (NOTE: Does *not* filter files, only defines which beautifier type to run)
-q, --quiet Suppress logging to stdout
-h, --help Show this help
-v, --version Show the version
@ -180,16 +179,19 @@ 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
-k, --keep-array-indentation Preserve array indentation
-x, --unescape-strings Decode printable characters encoded in xNN notation
-w, --wrap-line-length Wrap lines at next opportunity after N characters [0]
-w, --wrap-line-length Wrap lines that exceed N characters [0]
-X, --e4x Pass E4X xml literals through untouched
--good-stuff Warm the cockles of Crockford's heart
-C, --comma-first Put commas at the beginning of new line instead of end
-O, --operator-position Set operator position (before-newline|after-newline|preserve-newline) [before-newline]
--indent-empty-lines Keep indentation on empty lines
--templating List of templating languages (auto,django,erb,handlebars,php) ["auto"] auto = none in JavaScript, all in html
```
Which correspond to the underscored option keys for both library interfaces
@ -200,6 +202,7 @@ Which correspond to the underscored option keys for both library interfaces
"indent_size": 4,
"indent_char": " ",
"indent_with_tabs": false,
"editorconfig": false,
"eol": "\n",
"end_with_newline": false,
"indent_level": 0,
@ -209,6 +212,7 @@ Which correspond to the underscored option keys for both library interfaces
"space_in_empty_paren": false,
"jslint_happy": false,
"space_after_anon_function": false,
"space_after_named_function": false,
"brace_style": "collapse",
"unindent_chained_methods": false,
"break_chained_methods": false,
@ -217,7 +221,9 @@ Which correspond to the underscored option keys for both library interfaces
"wrap_line_length": 0,
"e4x": false,
"comma_first": false,
"operator_position": "before-newline"
"operator_position": "before-newline",
"indent_empty_lines": false,
"templating": ["auto"]
}
```
@ -314,6 +320,7 @@ CSS Beautifier Options:
-n, --end-with-newline End output with newline
-L, --selector-separator-newline Add a newline between multiple selectors
-N, --newline-between-rules Add a newline between CSS rules
--indent-empty-lines Keep indentation on empty lines
HTML Beautifier Options:
-s, --indent-size Indentation size [4]
@ -327,23 +334,47 @@ HTML Beautifier Options:
-b, --brace-style [collapse-preserve-inline|collapse|expand|end-expand|none] ["collapse"]
-S, --indent-scripts [keep|separate|normal] ["normal"]
-w, --wrap-line-length Maximum characters per line (0 disables) [250]
-A, --wrap-attributes Wrap attributes to new lines [auto|force|force-aligned|force-expand-multiline|aligned-multiple] ["auto"]
-A, --wrap-attributes Wrap attributes to new lines [auto|force|force-aligned|force-expand-multiline|aligned-multiple|preserve|preserve-aligned] ["auto"]
-i, --wrap-attributes-indent-size Indent wrapped attributes to after N characters [indent-size] (ignored if wrap-attributes is "aligned")
-d, --inline List of tags to be considered inline tags
-U, --unformatted List of tags (defaults to inline) that should not be reformatted
-T, --content_unformatted List of tags (defaults to pre) whose content should not be reformatted
-E, --extra_liners List of tags (defaults to [head,body,/html] that should have an extra newline before them.
--editorconfig Use EditorConfig to set up the options
--indent_scripts Sets indent level inside script tags ("normal", "keep", "separate")
--unformatted_content_delimiter Keep text content together between this string [""]
--indent-empty-lines Keep indentation on empty lines
--templating List of templating languages (auto,none,django,erb,handlebars,php) ["auto"] auto = none in JavaScript, all in html
```
## Directives to Ignore or Preserve sections (Javascript beautifier only)
## Directives
Beautifier for supports directives in comments inside the file.
This allows you to tell the beautifier to preserve the formatting of or completely ignore part of a file.
The example input below will remain changed after beautification
Directives let you control the behavior of the Beautifier from within your source files. Directives are placed in comments inside the file. Directives are in the format `/* beautify {name}:{value} */` in CSS and JavaScript. In HTML they are formatted as `<!-- beautify {name}:{value} -->`.
### Ignore directive
The `ignore` directive makes the beautifier completely ignore part of a file, treating it as literal text that is not parsed.
The input below will remain unchanged after beautification:
```js
// Use preserve when the content is not javascript, but you don't want it reformatted.
// Use ignore when the content is not parsable in the current language, JavaScript in this case.
var a = 1;
/* beautify ignore:start */
{This is some strange{template language{using open-braces?
/* beautify ignore:end */
```
### Preserve directive
NOTE: this directive only works in HTML and JavaScript, not CSS.
The `preserve` directive makes the Beautifier parse and then keep the existing formatting of a section of code.
The input below will remain unchanged after beautification:
```js
// Use preserve when the content is valid syntax in the current language, JavaScript in this case.
// This will parse the code and preserve the existing formatting.
/* beautify preserve:start */
{
browserName: 'internet explorer',
@ -351,12 +382,6 @@ The example input below will remain changed after beautification
version: '8'
}
/* beautify preserve:end */
// Use ignore when the content is not parsable as javascript.
var a = 1;
/* beautify ignore:start */
{This is some strange{template language{using open-braces?
/* beautify ignore:end */
```
# License
@ -365,13 +390,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.10.0)

View File

@ -1,37 +0,0 @@
version: 1.6+{build}
nuget:
disable_publish_on_pr: true
deploy: off
# Test against this version of Node.js
environment:
global:
nodejs_version: "6"
matrix:
- PYTHON: "C:\\Python27"
PYTHON_VERSION: "2.7.x"
PYTHON_ARCH: "32"
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;c:\\MinGW\\bin;%PATH%"
- pip --version
- copy c:\MinGW\bin\mingw32-make.exe c:\MinGW\bin\make.exe
- make --version
# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
- python --version
# run tests
- bash -c "make ci"
# Don't actually build.
build: off

96
azure-pipelines.yml Normal file
View File

@ -0,0 +1,96 @@
# https://aka.ms/yaml
jobs:
- job: 'Windows'
pool:
vmImage: 'vs2017-win2016'
strategy:
matrix:
Python34_Node8:
python.version: '3.4'
python.architecture: 'x86'
node_version: 8.x
Python36_Node10:
python.version: '3.6'
python.architecture: 'x64'
node_version: 10.x
Python37_Node11:
python.version: '3.7'
python.architecture: 'x64'
node_version: 11.x
maxParallel: 6
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(python.version)'
architecture: '$(python.architecture)'
- task: NodeTool@0
inputs:
versionSpec: $(node_version)
# change this to make ci after addressing git CRLF issue
- task: Bash@3
inputs:
targetType: 'inline'
script: 'make all'
- job: 'Linux'
pool:
vmImage: 'ubuntu-16.04' # other options: 'macOS-10.13', 'vs2017-win2016'
strategy:
matrix:
Python27_Node6:
python.version: '2.7'
node_version: 6.x
Python34_Node8:
python.version: '3.4'
node_version: 8.x
Python36_Node10:
python.version: '3.6'
node_version: 10.x
Python37_Node11:
python.version: '3.7'
node_version: 11.x
maxParallel: 6
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(python.version)'
architecture: 'x64'
- task: NodeTool@0
inputs:
versionSpec: $(node_version)
- task: Bash@3
inputs:
targetType: 'inline'
script: 'make ci'
- job: 'MacOS'
pool:
vmImage: 'macOS-10.13'
strategy:
matrix:
Python27_Node6:
python.version: '2.7'
node_version: 6.x
Python37_Node11:
python.version: '3.7'
node_version: 11.x
maxParallel: 6
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(python.version)'
architecture: 'x64'
- task: NodeTool@0
inputs:
versionSpec: $(node_version)
- task: Bash@3
inputs:
targetType: 'inline'
script: 'make ci'

View File

@ -1,6 +1,10 @@
<!doctype html>
<html lang="en-US" dir="ltr" prefix="og:http://ogp.me/ns# fb:http://ogp.me/ns/fb#">
<!--
<html lang="en-US" dir="ltr" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
<head>
<meta charset="utf-8" /><!-- The <meta> element must be within the first 1024 bytes of the HTML -->
<!--
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
@ -25,252 +29,254 @@
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>
<!-- 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="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://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="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>
<h1>Online JavaScript Beautifier <span id="version-number"></span></h1>
<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>
<p>
<select name="language" id="language">
<option value="css">Beautify CSS</option>
<option value="html">Beautify HTML</option>
<option value="js">Beautify JavaScript</option>
</select>
</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>
<input class="checkbox" type="checkbox" id="indent-empty-lines">
<label for="indent-empty-lines">Keep indentation on empty lines?</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">
<button class="submit"><strong>Beautify Code</strong> <em>(ctrl-enter)</em>
</button>
<textarea id="source" rows="30" cols="160"></textarea>
<button class="submit"><strong>Beautify Code</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,</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>

View File

@ -6,6 +6,7 @@
"preserve_newlines": true,
"max_preserve_newlines": 10,
"jslint_happy": false,
"space_after_named_function": false,
"space_after_anon_function": false,
"brace_style": "collapse",
"keep_array_indentation": false,
@ -14,5 +15,7 @@
"break_chained_methods": false,
"eval_code": false,
"unescape_strings": false,
"wrap_line_length": 0
"wrap_line_length": 0,
"indent_empty_lines": false,
"templating": ["auto"]
}

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:

File diff suppressed because it is too large Load Diff

View File

@ -1,71 +0,0 @@
/* jshint curly: false */
// 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
// taken from [Esprima](http://esprima.org) by Ariya Hidayat.
//
// Git repositories for Acorn are available at
//
// http://marijnhaverbeke.nl/git/acorn
// https://github.com/marijnh/acorn.git
// ## Character categories
// 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 + "]");
exports.identifier = new RegExp("[" + baseASCIIidentifierStartChars + nonASCIIidentifierStartChars + "][" + baseASCIIidentifierChars + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]*", 'g');
// Whether a single character denotes a newline.
exports.newline = /[\n\r\u2028\u2029]/;
// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.
// 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));
};

62
js/src/core/directives.js Normal file
View File

@ -0,0 +1,62 @@
/*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';
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_end_ignore_pattern = new RegExp(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)) {
return null;
}
var directives = {};
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);
}
return directives;
};
Directives.prototype.readIgnored = function(input) {
return input.readUntilAfter(this.__directives_end_ignore_pattern);
};
module.exports.Directives = Directives;

View File

@ -1,4 +1,4 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
@ -26,127 +26,167 @@
SOFTWARE.
*/
'use strict';
var regexp_has_sticky = RegExp.prototype.hasOwnProperty('sticky');
function InputScanner(input_string) {
var _input = input_string || '';
var _input_length = _input.length;
var _position = 0;
this.back = function() {
if (_position > 0) {
_position -= 1;
}
};
this.hasNext = function() {
return _position < _input_length;
};
this.next = function() {
var val = null;
if (this.hasNext()) {
val = _input.charAt(_position);
_position += 1;
}
return val;
};
this.peek = function(index) {
var val = null;
index = index || 0;
index += _position;
if (index >= 0 && index < _input_length) {
val = _input.charAt(index);
}
return val;
};
this.test = function(pattern, index) {
index = index || 0;
index += _position;
pattern.lastIndex = index;
if (index >= 0 && index < _input_length) {
var pattern_match = pattern.exec(_input);
return pattern_match && pattern_match.index === index;
} else {
return false;
}
};
this.testChar = function(pattern, index) {
// test one character regex match
var val = this.peek(index);
return val !== null && pattern.test(val);
};
this.match = function(pattern) {
pattern.lastIndex = _position;
var pattern_match = pattern.exec(_input);
if (pattern_match && pattern_match.index === _position) {
_position += pattern_match[0].length;
} else {
pattern_match = null;
}
return pattern_match;
};
this.readWhile = function(pattern) {
var val = '';
var match = this.match(pattern);
if (match) {
val = match[0];
}
return val;
};
this.readUntil = function(pattern) {
var val = '';
var match_index = _position;
pattern.lastIndex = _position;
var pattern_match = pattern.exec(_input);
if (pattern_match) {
match_index = pattern_match.index;
} else {
match_index = _input_length;
}
val = _input.substring(_position, match_index);
_position = match_index;
return val;
};
this.readUntilAfter = function(pattern) {
var val = '';
var match_index = _position;
pattern.lastIndex = _position;
var pattern_match = pattern.exec(_input);
if (pattern_match) {
match_index = pattern_match.index + pattern_match[0].length;
} else {
match_index = _input_length;
}
val = _input.substring(_position, match_index);
_position = match_index;
return val;
};
/* css beautifier legacy helpers */
this.peekUntilAfter = function(pattern) {
var start = _position;
var val = this.readUntilAfter(pattern);
_position = start;
return val;
};
this.lookBack = function(testVal) {
var start = _position - 1;
return start >= testVal.length && _input.substring(start - testVal.length, start)
.toLowerCase() === testVal;
};
this.__input = input_string || '';
this.__input_length = this.__input.length;
this.__position = 0;
}
InputScanner.prototype.restart = function() {
this.__position = 0;
};
module.exports.InputScanner = InputScanner;
InputScanner.prototype.back = function() {
if (this.__position > 0) {
this.__position -= 1;
}
};
InputScanner.prototype.hasNext = function() {
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;
}
return val;
};
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);
}
return val;
};
// This is a JavaScript only helper function (not in python)
// Javascript doesn't have a match method
// and not all implementation support "sticky" flag.
// If they do not support sticky then both this.match() and this.test() method
// must get the match and check the index of the match.
// If sticky is supported and set, this method will use it.
// Otherwise it will check that global is set, and fall back to the slower method.
InputScanner.prototype.__match = function(pattern, index) {
pattern.lastIndex = index;
var pattern_match = pattern.exec(this.__input);
if (pattern_match && !(regexp_has_sticky && pattern.sticky)) {
if (pattern_match.index !== index) {
pattern_match = null;
}
}
return pattern_match;
};
InputScanner.prototype.test = function(pattern, index) {
index = index || 0;
index += this.__position;
if (index >= 0 && index < this.__input_length) {
return !!this.__match(pattern, index);
} else {
return false;
}
};
InputScanner.prototype.testChar = function(pattern, index) {
// test one character regex match
var val = this.peek(index);
pattern.lastIndex = 0;
return val !== null && pattern.test(val);
};
InputScanner.prototype.match = function(pattern) {
var pattern_match = this.__match(pattern, this.__position);
if (pattern_match) {
this.__position += pattern_match[0].length;
} else {
pattern_match = null;
}
return pattern_match;
};
InputScanner.prototype.read = function(starting_pattern, until_pattern, until_after) {
var val = '';
var match;
if (starting_pattern) {
match = this.match(starting_pattern);
if (match) {
val += match[0];
}
}
if (until_pattern && (match || !starting_pattern)) {
val += this.readUntil(until_pattern, until_after);
}
return val;
};
InputScanner.prototype.readUntil = function(pattern, until_after) {
var val = '';
var match_index = this.__position;
pattern.lastIndex = this.__position;
var pattern_match = pattern.exec(this.__input);
if (pattern_match) {
match_index = pattern_match.index;
if (until_after) {
match_index += pattern_match[0].length;
}
} else {
match_index = this.__input_length;
}
val = this.__input.substring(this.__position, match_index);
this.__position = match_index;
return val;
};
InputScanner.prototype.readUntilAfter = function(pattern) {
return this.readUntil(pattern, true);
};
InputScanner.prototype.get_regexp = function(pattern, match_from) {
var result = null;
var flags = 'g';
if (match_from && regexp_has_sticky) {
flags = 'y';
}
// strings are converted to regexp
if (typeof pattern === "string" && pattern !== '') {
// result = new RegExp(pattern.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), flags);
result = new RegExp(pattern, flags);
} else if (pattern) {
result = new RegExp(pattern.source, flags);
}
return result;
};
InputScanner.prototype.get_literal_regexp = function(literal_string) {
return RegExp(literal_string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'));
};
/* css beautifier legacy helpers */
InputScanner.prototype.peekUntilAfter = function(pattern) {
var start = this.__position;
var val = this.readUntilAfter(pattern);
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)
.toLowerCase() === testVal;
};
module.exports.InputScanner = InputScanner;

View File

@ -1,38 +1,165 @@
/*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) {
this.raw_options = _mergeOpts(options, merge_child_field);
// 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', this.indent_char === '\t');
if (this.indent_with_tabs) {
this.indent_char = '\t';
// indent_size behavior changed after 1.8.6
// It used to be that indent_size would be
// set to 1 for indent_with_tabs. That is no longer needed and
// actually doesn't make sense - why not use spaces? Further,
// that might produce unexpected behavior - tabs being used
// for single-column alignment. So, when indent_with_tabs is true
// and indent_size is 1, reset indent_size to 4.
if (this.indent_size === 1) {
this.indent_size = 4;
}
}
// Backwards compat with 1.3.x
this.wrap_line_length = this._get_number('wrap_line_length', this._get_number('max_char'));
this.indent_empty_lines = this._get_boolean('indent_empty_lines');
// valid templating languages ['django', 'erb', 'handlebars', 'php']
// For now, 'auto' = all off for javascript, all on for html (and inline javascript).
// other values ignored
this.templating = this._get_selection_list('templating', ['auto', 'none', 'django', 'erb', 'handlebars', 'php'], ['auto']);
}
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) {
// Returns: {a: 2}
function _mergeOpts(allOptions, childFieldName) {
var finalOpts = {};
allOptions = _normalizeOpts(allOptions);
var name;
for (name in allOptions) {
@ -42,7 +169,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 +177,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,6 +1,5 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
@ -26,209 +25,395 @@
SOFTWARE.
*/
'use strict';
function OutputLine(parent) {
this._character_count = 0;
// use indent_count as a marker for this._lines that have preserved indentation
this._indent_count = -1;
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.__wrap_point_index = 0;
this.__wrap_point_character_count = 0;
this.__wrap_point_indent_count = -1;
this.__wrap_point_alignment_count = 0;
this._items = [];
var _empty = true;
this.set_indent = function(level) {
this._character_count = parent.baseIndentLength + level * parent.indent_length;
this._indent_count = level;
};
this.get_character_count = function() {
return this._character_count;
};
this.is_empty = function() {
return _empty;
};
this.last = function() {
if (!this._empty) {
return this._items[this._items.length - 1];
} else {
return null;
}
};
this.push = function(item) {
this._items.push(item);
this._character_count += item.length;
_empty = false;
};
this.pop = function() {
var item = null;
if (!_empty) {
item = this._items.pop();
this._character_count -= item.length;
_empty = this._items.length === 0;
}
return item;
};
this.remove_indent = function() {
if (this._indent_count > 0) {
this._indent_count -= 1;
this._character_count -= parent.indent_length;
}
};
this.trim = function() {
while (this.last() === ' ') {
this._items.pop();
this._character_count -= 1;
}
_empty = this._items.length === 0;
};
this.toString = function() {
var result = '';
if (!this._empty) {
if (this._indent_count >= 0) {
result = parent.indent_cache[this._indent_count];
}
result += this._items.join('');
}
return result;
};
this.__items = [];
}
function Output(indent_string, baseIndentString) {
baseIndentString = baseIndentString || '';
this.indent_cache = [baseIndentString];
this.baseIndentLength = baseIndentString.length;
this.indent_length = indent_string.length;
this.raw = false;
OutputLine.prototype.clone_empty = function() {
var line = new OutputLine(this.__parent);
line.set_indent(this.__indent_count, this.__alignment_count);
return line;
};
this._lines = [];
this.baseIndentString = baseIndentString;
this.indent_string = indent_string;
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) {
if (this.is_empty()) {
this.__indent_count = indent || 0;
this.__alignment_count = alignment || 0;
this.__character_count = this.__parent.get_indent_size(this.__indent_count, this.__alignment_count);
}
};
OutputLine.prototype._set_wrap_point = function() {
if (this.__parent.wrap_line_length) {
this.__wrap_point_index = this.__items.length;
this.__wrap_point_character_count = this.__character_count;
this.__wrap_point_indent_count = this.__parent.next_line.__indent_count;
this.__wrap_point_alignment_count = this.__parent.next_line.__alignment_count;
}
};
OutputLine.prototype._should_wrap = function() {
return this.__wrap_point_index &&
this.__character_count > this.__parent.wrap_line_length &&
this.__wrap_point_character_count > this.__parent.next_line.__character_count;
};
OutputLine.prototype._allow_wrap = function() {
if (this._should_wrap()) {
this.__parent.add_new_line();
var next = this.__parent.current_line;
next.set_indent(this.__wrap_point_indent_count, this.__wrap_point_alignment_count);
next.__items = this.__items.slice(this.__wrap_point_index);
this.__items = this.__items.slice(0, this.__wrap_point_index);
next.__character_count += this.__character_count - this.__wrap_point_character_count;
this.__character_count = this.__wrap_point_character_count;
if (next.__items[0] === " ") {
next.__items.splice(0, 1);
next.__character_count -= 1;
}
return true;
}
return false;
};
OutputLine.prototype.is_empty = function() {
return this.__items.length === 0;
};
OutputLine.prototype.last = function() {
if (!this.is_empty()) {
return this.__items[this.__items.length - 1];
} else {
return null;
}
};
OutputLine.prototype.push = function(item) {
this.__items.push(item);
var last_newline_index = item.lastIndexOf('\n');
if (last_newline_index !== -1) {
this.__character_count = item.length - last_newline_index;
} else {
this.__character_count += item.length;
}
};
OutputLine.prototype.pop = function() {
var item = null;
if (!this.is_empty()) {
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_size;
}
};
OutputLine.prototype._remove_wrap_indent = function() {
if (this.__wrap_point_indent_count > 0) {
this.__wrap_point_indent_count -= 1;
}
};
OutputLine.prototype.trim = function() {
while (this.last() === ' ') {
this.__items.pop();
this.__character_count -= 1;
}
};
OutputLine.prototype.toString = function() {
var result = '';
if (this.is_empty()) {
if (this.__parent.indent_empty_lines) {
result = this.__parent.get_indent_string(this.__indent_count);
}
} else {
result = this.__parent.get_indent_string(this.__indent_count, this.__alignment_count);
result += this.__items.join('');
}
return result;
};
function IndentStringCache(options, baseIndentString) {
this.__cache = [''];
this.__indent_size = options.indent_size;
this.__indent_string = options.indent_char;
if (!options.indent_with_tabs) {
this.__indent_string = new Array(options.indent_size + 1).join(options.indent_char);
}
// Set to null to continue support for auto detection of base indent
baseIndentString = baseIndentString || '';
if (options.indent_level > 0) {
baseIndentString = new Array(options.indent_level + 1).join(this.__indent_string);
}
this.__base_string = baseIndentString;
this.__base_string_length = baseIndentString.length;
}
IndentStringCache.prototype.get_indent_size = function(indent, column) {
var result = this.__base_string_length;
column = column || 0;
if (indent < 0) {
result = 0;
}
result += indent * this.__indent_size;
result += column;
return result;
};
IndentStringCache.prototype.get_indent_string = function(indent_level, column) {
var result = this.__base_string;
column = column || 0;
if (indent_level < 0) {
indent_level = 0;
result = '';
}
column += indent_level * this.__indent_size;
this.__ensure_cache(column);
result += this.__cache[column];
return result;
};
IndentStringCache.prototype.__ensure_cache = function(column) {
while (column >= this.__cache.length) {
this.__add_column();
}
};
IndentStringCache.prototype.__add_column = function() {
var column = this.__cache.length;
var indent = 0;
var result = '';
if (this.__indent_size && column >= this.__indent_size) {
indent = Math.floor(column / this.__indent_size);
column -= indent * this.__indent_size;
result = new Array(indent + 1).join(this.__indent_string);
}
if (column) {
result += new Array(column + 1).join(' ');
}
this.__cache.push(result);
};
function Output(options, baseIndentString) {
this.__indent_cache = new IndentStringCache(options, baseIndentString);
this.raw = false;
this._end_with_newline = options.end_with_newline;
this.indent_size = options.indent_size;
this.wrap_line_length = options.wrap_line_length;
this.indent_empty_lines = options.indent_empty_lines;
this.__lines = [];
this.previous_line = null;
this.current_line = null;
this.next_line = new OutputLine(this);
this.space_before_token = false;
this.add_outputline = function() {
this.previous_line = this.current_line;
this.current_line = new OutputLine(this);
this._lines.push(this.current_line);
};
this.non_breaking_space = false;
this.previous_token_wrapped = false;
// initialize
this.add_outputline();
this.get_line_number = function() {
return this._lines.length;
};
// Using object instead of string to allow for later expansion of info about each line
this.add_new_line = function(force_newline) {
if (this.get_line_number() === 1 && this.just_added_newline()) {
return false; // no newline on start of file
}
if (force_newline || !this.just_added_newline()) {
if (!this.raw) {
this.add_outputline();
}
return true;
}
return false;
};
this.get_code = function(end_with_newline, eol) {
var sweet_code = this._lines.join('\n').replace(/[\r\n\t ]+$/, '');
if (end_with_newline) {
sweet_code += '\n';
}
if (eol !== '\n') {
sweet_code = sweet_code.replace(/[\n]/g, eol);
}
return sweet_code;
};
this.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);
}
this.current_line.set_indent(level);
return true;
}
this.current_line.set_indent(0);
return false;
};
this.add_raw_token = function(token) {
for (var x = 0; x < token.newlines; x++) {
this.add_outputline();
}
this.current_line.push(token.whitespace_before);
this.current_line.push(token.text);
this.space_before_token = false;
};
this.add_token = function(printable_token) {
this.add_space_before_token();
this.current_line.push(printable_token);
};
this.add_space_before_token = function() {
if (this.space_before_token && !this.just_added_newline()) {
this.current_line.push(' ');
}
this.space_before_token = false;
};
this.remove_indent = function(index) {
var output_length = this._lines.length;
while (index < output_length) {
this._lines[index].remove_indent();
index++;
}
};
this.trim = function(eat_newlines) {
eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
this.current_line.trim(indent_string, baseIndentString);
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.current_line.trim();
}
this.previous_line = this._lines.length > 1 ? this._lines[this._lines.length - 2] : null;
};
this.just_added_newline = function() {
return this.current_line.is_empty();
};
this.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;
};
this.__add_outputline();
}
module.exports.Output = Output;
Output.prototype.__add_outputline = function() {
this.previous_line = this.current_line;
this.current_line = this.next_line.clone_empty();
this.__lines.push(this.current_line);
};
Output.prototype.get_line_number = function() {
return this.__lines.length;
};
Output.prototype.get_indent_string = function(indent, column) {
return this.__indent_cache.get_indent_string(indent, column);
};
Output.prototype.get_indent_size = function(indent, column) {
return this.__indent_cache.get_indent_size(indent, column);
};
Output.prototype.is_empty = function() {
return !this.previous_line && this.current_line.is_empty();
};
Output.prototype.add_new_line = function(force_newline) {
// 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 raw output is enabled, don't print additional newlines,
// but still return True as though you had
if (!this.raw) {
this.__add_outputline();
}
return true;
};
Output.prototype.get_code = function(eol) {
this.trim(true);
// handle some edge cases where the last tokens
// has text that ends with newline(s)
var last_item = this.current_line.pop();
if (last_item) {
if (last_item[last_item.length - 1] === '\n') {
last_item = last_item.replace(/\n+$/g, '');
}
this.current_line.push(last_item);
}
if (this._end_with_newline) {
this.__add_outputline();
}
var sweet_code = this.__lines.join('\n');
if (eol !== '\n') {
sweet_code = sweet_code.replace(/[\n]/g, eol);
}
return sweet_code;
};
Output.prototype.set_wrap_point = function() {
this.current_line._set_wrap_point();
};
Output.prototype.set_indent = function(indent, alignment) {
indent = indent || 0;
alignment = alignment || 0;
// Next line stores alignment values
this.next_line.set_indent(indent, alignment);
// 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();
return false;
};
Output.prototype.add_raw_token = function(token) {
for (var x = 0; x < token.newlines; x++) {
this.__add_outputline();
}
this.current_line.set_indent(-1);
this.current_line.push(token.whitespace_before);
this.current_line.push(token.text);
this.space_before_token = false;
this.non_breaking_space = false;
this.previous_token_wrapped = false;
};
Output.prototype.add_token = function(printable_token) {
this.__add_space_before_token();
this.current_line.push(printable_token);
this.space_before_token = false;
this.non_breaking_space = false;
this.previous_token_wrapped = this.current_line._allow_wrap();
};
Output.prototype.__add_space_before_token = function() {
if (this.space_before_token && !this.just_added_newline()) {
if (!this.non_breaking_space) {
this.set_wrap_point();
}
this.current_line.push(' ');
}
};
Output.prototype.remove_indent = function(index) {
var output_length = this.__lines.length;
while (index < output_length) {
this.__lines[index]._remove_indent();
index++;
}
this.current_line._remove_wrap_indent();
};
Output.prototype.trim = function(eat_newlines) {
eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
this.current_line.trim();
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.current_line.trim();
}
this.previous_line = this.__lines.length > 1 ?
this.__lines[this.__lines.length - 2] : null;
};
Output.prototype.just_added_newline = function() {
return this.current_line.is_empty();
};
Output.prototype.just_added_blankline = function() {
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;

94
js/src/core/pattern.js Normal file
View File

@ -0,0 +1,94 @@
/*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';
function Pattern(input_scanner, parent) {
this._input = input_scanner;
this._starting_pattern = null;
this._match_pattern = null;
this._until_pattern = null;
this._until_after = false;
if (parent) {
this._starting_pattern = this._input.get_regexp(parent._starting_pattern, true);
this._match_pattern = this._input.get_regexp(parent._match_pattern, true);
this._until_pattern = this._input.get_regexp(parent._until_pattern);
this._until_after = parent._until_after;
}
}
Pattern.prototype.read = function() {
var result = this._input.read(this._starting_pattern);
if (!this._starting_pattern || result) {
result += this._input.read(this._match_pattern, this._until_pattern, this._until_after);
}
return result;
};
Pattern.prototype.read_match = function() {
return this._input.match(this._match_pattern);
};
Pattern.prototype.until_after = function(pattern) {
var result = this._create();
result._until_after = true;
result._until_pattern = this._input.get_regexp(pattern);
result._update();
return result;
};
Pattern.prototype.until = function(pattern) {
var result = this._create();
result._until_after = false;
result._until_pattern = this._input.get_regexp(pattern);
result._update();
return result;
};
Pattern.prototype.starting_with = function(pattern) {
var result = this._create();
result._starting_pattern = this._input.get_regexp(pattern, true);
result._update();
return result;
};
Pattern.prototype.matching = function(pattern) {
var result = this._create();
result._match_pattern = this._input.get_regexp(pattern, true);
result._update();
return result;
};
Pattern.prototype._create = function() {
return new Pattern(this._input, this);
};
Pattern.prototype._update = function() {};
module.exports.Pattern = Pattern;

View File

@ -0,0 +1,188 @@
/*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 Pattern = require('./pattern').Pattern;
var template_names = {
django: false,
erb: false,
handlebars: false,
php: false
};
// This lets templates appear anywhere we would do a readUntil
// The cost is higher but it is pay to play.
function TemplatablePattern(input_scanner, parent) {
Pattern.call(this, input_scanner, parent);
this.__template_pattern = null;
this._disabled = Object.assign({}, template_names);
this._excluded = Object.assign({}, template_names);
if (parent) {
this.__template_pattern = this._input.get_regexp(parent.__template_pattern);
this._excluded = Object.assign(this._excluded, parent._excluded);
this._disabled = Object.assign(this._disabled, parent._disabled);
}
var pattern = new Pattern(input_scanner);
this.__patterns = {
handlebars_comment: pattern.starting_with(/{{!--/).until_after(/--}}/),
handlebars: pattern.starting_with(/{{/).until_after(/}}/),
php: pattern.starting_with(/<\?(?:[=]|php)/).until_after(/\?>/),
erb: pattern.starting_with(/<%[^%]/).until_after(/[^%]%>/),
// django coflicts with handlebars a bit.
django: pattern.starting_with(/{%/).until_after(/%}/),
django_value: pattern.starting_with(/{{/).until_after(/}}/),
django_comment: pattern.starting_with(/{#/).until_after(/#}/)
};
}
TemplatablePattern.prototype = new Pattern();
TemplatablePattern.prototype._create = function() {
return new TemplatablePattern(this._input, this);
};
TemplatablePattern.prototype._update = function() {
this.__set_templated_pattern();
};
TemplatablePattern.prototype.disable = function(language) {
var result = this._create();
result._disabled[language] = true;
result._update();
return result;
};
TemplatablePattern.prototype.read_options = function(options) {
var result = this._create();
for (var language in template_names) {
result._disabled[language] = options.templating.indexOf(language) === -1;
}
result._update();
return result;
};
TemplatablePattern.prototype.exclude = function(language) {
var result = this._create();
result._excluded[language] = true;
result._update();
return result;
};
TemplatablePattern.prototype.read = function() {
var result = '';
if (this._match_pattern) {
result = this._input.read(this._starting_pattern);
} else {
result = this._input.read(this._starting_pattern, this.__template_pattern);
}
var next = this._read_template();
while (next) {
if (this._match_pattern) {
next += this._input.read(this._match_pattern);
} else {
next += this._input.readUntil(this.__template_pattern);
}
result += next;
next = this._read_template();
}
if (this._until_after) {
result += this._input.readUntilAfter(this._until_pattern);
}
return result;
};
TemplatablePattern.prototype.__set_templated_pattern = function() {
var items = [];
if (!this._disabled.php) {
items.push(this.__patterns.php._starting_pattern.source);
}
if (!this._disabled.handlebars) {
items.push(this.__patterns.handlebars._starting_pattern.source);
}
if (!this._disabled.erb) {
items.push(this.__patterns.erb._starting_pattern.source);
}
if (!this._disabled.django) {
items.push(this.__patterns.django._starting_pattern.source);
items.push(this.__patterns.django_value._starting_pattern.source);
items.push(this.__patterns.django_comment._starting_pattern.source);
}
if (this._until_pattern) {
items.push(this._until_pattern.source);
}
this.__template_pattern = this._input.get_regexp('(?:' + items.join('|') + ')');
};
TemplatablePattern.prototype._read_template = function() {
var resulting_string = '';
var c = this._input.peek();
if (c === '<') {
var peek1 = this._input.peek(1);
//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
if (!this._disabled.php && !this._excluded.php && peek1 === '?') {
resulting_string = resulting_string ||
this.__patterns.php.read();
}
if (!this._disabled.erb && !this._excluded.erb && peek1 === '%') {
resulting_string = resulting_string ||
this.__patterns.erb.read();
}
} else if (c === '{') {
if (!this._disabled.handlebars && !this._excluded.handlebars) {
resulting_string = resulting_string ||
this.__patterns.handlebars_comment.read();
resulting_string = resulting_string ||
this.__patterns.handlebars.read();
}
if (!this._disabled.django) {
// django coflicts with handlebars a bit.
if (!this._excluded.django && !this._excluded.handlebars) {
resulting_string = resulting_string ||
this.__patterns.django_value.read();
}
if (!this._excluded.django) {
resulting_string = resulting_string ||
this.__patterns.django_comment.read();
resulting_string = resulting_string ||
this.__patterns.django.read();
}
}
}
return resulting_string;
};
module.exports.TemplatablePattern = TemplatablePattern;

View File

@ -1,4 +1,4 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*jshint node:true */
/*
The MIT License (MIT)
@ -26,7 +26,9 @@
SOFTWARE.
*/
function Token(type, text, newlines, whitespace_before, parent) {
'use strict';
function Token(type, text, newlines, whitespace_before) {
this.type = type;
this.text = text;
@ -34,16 +36,19 @@ function Token(type, text, newlines, whitespace_before, parent) {
// comments that have a new line before them
// and may or may not have a newline after
// this is a set of comments before
this.comments_before = /* inline comment*/ [];
this.comments_before = null; /* inline comment*/
this.comments_after = []; // no new line before and newline after
// this.comments_after = new TokenStream(); // no new line before and newline after
this.newlines = newlines || 0;
this.wanted_newline = newlines > 0;
this.whitespace_before = whitespace_before || '';
this.parent = parent || null;
this.parent = null;
this.next = null;
this.previous = null;
this.opened = null;
this.closed = null;
this.directives = null;
}
module.exports.Token = Token;
module.exports.Token = Token;

140
js/src/core/tokenizer.js Normal file
View File

@ -0,0 +1,140 @@
/*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 InputScanner = require('../core/inputscanner').InputScanner;
var Token = require('../core/token').Token;
var TokenStream = require('../core/tokenstream').TokenStream;
var WhitespacePattern = require('./whitespacepattern').WhitespacePattern;
var TOKEN = {
START: 'TK_START',
RAW: 'TK_RAW',
EOF: 'TK_EOF'
};
var Tokenizer = function(input_string, options) {
this._input = new InputScanner(input_string);
this._options = options || {};
this.__tokens = null;
this._patterns = {};
this._patterns.whitespace = new WhitespacePattern(this._input);
};
Tokenizer.prototype.tokenize = function() {
this._input.restart();
this.__tokens = new TokenStream();
this._reset();
var current;
var previous = new Token(TOKEN.START, '');
var open_token = null;
var open_stack = [];
var comments = new TokenStream();
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(previous, open_token);
}
if (!comments.isEmpty()) {
current.comments_before = comments;
comments = new TokenStream();
}
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.opened = open_token;
open_token.closed = current;
open_token = open_stack.pop();
current.parent = open_token;
}
current.previous = previous;
previous.next = current;
this.__tokens.add(current);
previous = current;
}
return this.__tokens;
};
Tokenizer.prototype._is_first_token = function() {
return this.__tokens.isEmpty();
};
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);
} else {
return this._create_token(TOKEN.EOF, '');
}
};
Tokenizer.prototype._is_comment = function(current_token) { // jshint unused:false
return 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
return false;
};
Tokenizer.prototype._create_token = function(type, text) {
var token = new Token(type, text,
this._patterns.whitespace.newline_count,
this._patterns.whitespace.whitespace_before_token);
return token;
};
Tokenizer.prototype._readWhitespace = function() {
return this._patterns.whitespace.read();
};
module.exports.Tokenizer = Tokenizer;
module.exports.TOKEN = TOKEN;

View File

@ -0,0 +1,78 @@
/*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';
function TokenStream(parent_token) {
// private
this.__tokens = [];
this.__tokens_length = this.__tokens.length;
this.__position = 0;
this.__parent_token = parent_token;
}
TokenStream.prototype.restart = function() {
this.__position = 0;
};
TokenStream.prototype.isEmpty = function() {
return this.__tokens_length === 0;
};
TokenStream.prototype.hasNext = function() {
return this.__position < this.__tokens_length;
};
TokenStream.prototype.next = function() {
var val = null;
if (this.hasNext()) {
val = this.__tokens[this.__position];
this.__position += 1;
}
return val;
};
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];
}
return val;
};
TokenStream.prototype.add = function(token) {
if (this.__parent_token) {
token.parent = this.__parent_token;
}
this.__tokens.push(token);
this.__tokens_length += 1;
};
module.exports.TokenStream = TokenStream;

View File

@ -0,0 +1,105 @@
/*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 Pattern = require('../core/pattern').Pattern;
function WhitespacePattern(input_scanner, parent) {
Pattern.call(this, input_scanner, parent);
if (parent) {
this._line_regexp = this._input.get_regexp(parent._line_regexp);
} else {
this.__set_whitespace_patterns('', '');
}
this.newline_count = 0;
this.whitespace_before_token = '';
}
WhitespacePattern.prototype = new Pattern();
WhitespacePattern.prototype.__set_whitespace_patterns = function(whitespace_chars, newline_chars) {
whitespace_chars += '\\t ';
newline_chars += '\\n\\r';
this._match_pattern = this._input.get_regexp(
'[' + whitespace_chars + newline_chars + ']+', true);
this._newline_regexp = this._input.get_regexp(
'\\r\\n|[' + newline_chars + ']');
};
WhitespacePattern.prototype.read = function() {
this.newline_count = 0;
this.whitespace_before_token = '';
var resulting_string = this._input.read(this._match_pattern);
if (resulting_string === ' ') {
this.whitespace_before_token = ' ';
} else if (resulting_string) {
var matches = this.__split(this._newline_regexp, resulting_string);
this.newline_count = matches.length - 1;
this.whitespace_before_token = matches[this.newline_count];
}
return resulting_string;
};
WhitespacePattern.prototype.matching = function(whitespace_chars, newline_chars) {
var result = this._create();
result.__set_whitespace_patterns(whitespace_chars, newline_chars);
result._update();
return result;
};
WhitespacePattern.prototype._create = function() {
return new WhitespacePattern(this._input, this);
};
WhitespacePattern.prototype.__split = function(regexp, input_string) {
regexp.lastIndex = 0;
var start_index = 0;
var result = [];
var next_match = regexp.exec(input_string);
while (next_match) {
result.push(input_string.substring(start_index, next_match.index));
start_index = next_match.index + next_match[0].length;
next_match = regexp.exec(input_string);
}
if (start_index < input_string.length) {
result.push(input_string.substring(start_index, input_string.length));
} else {
result.push('');
}
return result;
};
module.exports.WhitespacePattern = WhitespacePattern;

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,31 @@
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 Directives = require('../core/directives').Directives;
var lineBreak = acorn.lineBreak;
var allLineBreaks = acorn.allLineBreaks;
var directives_core = new Directives(/\/\*/, /\*\//);
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.readWhile(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.readWhile(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.readWhile(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 +67,404 @@ function Beautifier(source_text, options) {
"@supports": true,
"@document": true
};
}
module.exports.Beautifier = Beautifier;
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) {
this._output.set_indent(this._indentLevel);
this._output.non_breaking_space = true;
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;
var whitespace;
var isAfterSpace;
var previous_ch;
while (true) {
whitespace = this._input.read(whitespacePattern);
isAfterSpace = whitespace !== '';
previous_ch = topCharacter;
this._ch = this._input.next();
if (this._ch === '\\' && this._input.hasNext()) {
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();
var comment = this._input.read(block_comment_pattern);
// Handle ignore directive
var directives = directives_core.get_directives(comment);
if (directives && directives.ignore === 'start') {
comment += directives_core.readIgnored(this._input);
}
this.print_string(comment);
// 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 && parenLevel === 0) {
// '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 (parenLevel === 0) {
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 {
this.print_string(this._ch);
this.eatWhitespace(true);
this._output.space_before_token = true;
}
} else if (this._ch === '(') { // may be a url
if (this._input.lookBack("url")) {
this.print_string(this._ch);
this.eatWhitespace();
parenLevel++;
this.indent();
this._ch = this._input.next();
if (this._ch === ')' || this._ch === '"' || this._ch === '\'') {
this._input.back();
} else if (this._ch) {
this.print_string(this._ch + this.eatString(')'));
if (parenLevel) {
parenLevel--;
this.outdent();
}
}
} else {
this.preserveSingleSpace(isAfterSpace);
this.print_string(this._ch);
this.eatWhitespace();
parenLevel++;
this.indent();
}
} else if (this._ch === ')') {
if (parenLevel) {
parenLevel--;
this.outdent();
}
this.print_string(this._ch);
} else if (this._ch === ',') {
this.print_string(this._ch);
this.eatWhitespace(true);
if (this._options.selector_separator_newline && !insidePropertyValue && parenLevel === 0 && !insideAtImport) {
this._output.add_new_line();
} else {
this._output.space_before_token = true;
}
} else if ((this._ch === '>' || this._ch === '+' || this._ch === '~') && !insidePropertyValue && parenLevel === 0) {
//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 === '!' && !this._input.lookBack("\\")) { // !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,36 +1,42 @@
/*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.
*/
var Beautifier = require('./beautifier').Beautifier;
'use strict';
var Beautifier = require('./beautifier').Beautifier,
Options = require('./options').Options;
function css_beautify(source_text, options) {
var beautifier = new Beautifier(source_text, options);
return beautifier.beautify();
}
module.exports = css_beautify;
module.exports = css_beautify;
module.exports.defaultOptions = function() {
return new 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;

29
js/src/css/tokenizer.js Normal file
View File

@ -0,0 +1,29 @@
/*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';

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +1,42 @@
/*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.
*/
var Beautifier = require('./beautifier').Beautifier;
'use strict';
var Beautifier = require('./beautifier').Beautifier,
Options = require('./options').Options;
function style_html(html_source, options, js_beautify, css_beautify) {
var beautifier = new Beautifier(html_source, options, js_beautify, css_beautify);
return beautifier.beautify();
}
module.exports = style_html;
module.exports = style_html;
module.exports.defaultOptions = function() {
return new Options();
};

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

@ -0,0 +1,91 @@
/*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');
if (this.templating.length === 1 && this.templating[0] === 'auto') {
this.templating = ['django', 'erb', 'handlebars', 'php'];
}
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', 'preserve', 'preserve-aligned']);
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']);
// Block vs inline elements
// https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
// https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
// https://www.w3.org/TR/html5/dom.html#phrasing-content
this.inline = this._get_array('inline', [
'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',
// obsolete inline tags
'acronym', 'big', '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',
// obsolete tags
// basefont: https://www.computerhope.com/jargon/h/html-basefont-tag.htm
// isndex: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/isindex
'basefont', 'isindex'
]);
this.unformatted = this._get_array('unformatted', []);
this.content_unformatted = this._get_array('content_unformatted', [
'pre', 'textarea'
]);
this.unformatted_content_delimiter = this._get_characters('unformatted_content_delimiter');
this.indent_scripts = this._get_selection('indent_scripts', ['normal', 'keep', 'separate']);
}
Options.prototype = new BaseOptions();
module.exports.Options = Options;

310
js/src/html/tokenizer.js Normal file
View File

@ -0,0 +1,310 @@
/*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 BaseTokenizer = require('../core/tokenizer').Tokenizer;
var BASETOKEN = require('../core/tokenizer').TOKEN;
var Directives = require('../core/directives').Directives;
var TemplatablePattern = require('../core/templatablepattern').TemplatablePattern;
var Pattern = require('../core/pattern').Pattern;
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
var templatable_reader = new TemplatablePattern(this._input).read_options(this._options);
var pattern_reader = new Pattern(this._input);
this.__patterns = {
word: templatable_reader.until(/[\n\r\t <]/),
single_quote: templatable_reader.until_after(/'/),
double_quote: templatable_reader.until_after(/"/),
attribute: templatable_reader.until(/[\n\r\t =\/>]/),
element_name: templatable_reader.until(/[\n\r\t >\/]/),
handlebars_comment: pattern_reader.starting_with(/{{!--/).until_after(/--}}/),
handlebars: pattern_reader.starting_with(/{{/).until_after(/}}/),
handlebars_open: pattern_reader.until(/[\n\r\t }]/),
handlebars_raw_close: pattern_reader.until(/}}/),
comment: pattern_reader.starting_with(/<!--/).until_after(/-->/),
cdata: pattern_reader.starting_with(/<!\[cdata\[/).until_after(/]]>/),
// https://en.wikipedia.org/wiki/Conditional_comment
conditional_comment: pattern_reader.starting_with(/<!\[/).until_after(/]>/),
processing: pattern_reader.starting_with(/<\?/).until_after(/\?>/)
};
if (this._options.indent_handlebars) {
this.__patterns.word = this.__patterns.word.exclude('handlebars');
}
this._unformatted_content_delimiter = null;
if (this._options.unformatted_content_delimiter) {
var literal_regexp = this._input.get_literal_regexp(this._options.unformatted_content_delimiter);
this.__patterns.unformatted_content_delimiter =
pattern_reader.matching(literal_regexp)
.until_after(literal_regexp);
}
};
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
var token = null;
this._readWhitespace();
var c = this._input.peek();
if (c === null) {
return this._create_token(TOKEN.EOF, '');
}
token = token || this._read_open_handlebars(c, open_token);
token = token || this._read_attribute(c, previous_token, open_token);
token = token || this._read_raw_content(c, previous_token, open_token);
token = token || this._read_close(c, open_token);
token = token || this._read_content_word(c);
token = token || this._read_comment(c);
token = token || this._read_open(c, open_token);
token = token || this._create_token(TOKEN.UNKNOWN, this._input.next());
return token;
};
Tokenizer.prototype._read_comment = function(c) { // jshint unused:false
var token = null;
var resulting_string = null;
var directives = null;
if (c === '<') {
var peek1 = this._input.peek(1);
//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
if (c === '<' && (peek1 === '!' || peek1 === '?')) {
resulting_string = this.__patterns.comment.read();
// only process directive on html comments
if (resulting_string) {
directives = directives_core.get_directives(resulting_string);
if (directives && directives.ignore === 'start') {
resulting_string += directives_core.readIgnored(this._input);
}
} else {
resulting_string = this.__patterns.cdata.read();
resulting_string = resulting_string || this.__patterns.conditional_comment.read();
resulting_string = resulting_string || this.__patterns.processing.read();
}
}
if (resulting_string) {
token = this._create_token(TOKEN.COMMENT, resulting_string);
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.next();
if (this._input.peek() === '/') {
resulting_string += this._input.next();
}
resulting_string += this.__patterns.element_name.read();
token = this._create_token(TOKEN.TAG_OPEN, resulting_string);
}
}
return token;
};
Tokenizer.prototype._read_open_handlebars = function(c, open_token) {
var resulting_string = null;
var token = null;
if (!open_token) {
if (this._options.indent_handlebars && c === '{' && this._input.peek(1) === '{') {
if (this._input.peek(2) === '!') {
resulting_string = this.__patterns.handlebars_comment.read();
resulting_string = resulting_string || this.__patterns.handlebars.read();
token = this._create_token(TOKEN.COMMENT, resulting_string);
} else {
resulting_string = this.__patterns.handlebars_open.read();
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();
if (c === '"') {
content += this.__patterns.double_quote.read();
} else {
content += this.__patterns.single_quote.read();
}
token = this._create_token(TOKEN.VALUE, content);
} else {
resulting_string = this.__patterns.attribute.read();
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 &&
(this._options.content_unformatted.indexOf(tag_name) !== -1 ||
this._options.unformatted.indexOf(tag_name) !== -1);
};
Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token) { // jshint unused:false
var resulting_string = '';
if (open_token && open_token.text[0] === '{') {
resulting_string = this.__patterns.handlebars_raw_close.read();
} else if (previous_token.type === TOKEN.TAG_CLOSE && (previous_token.opened.text[0] === '<')) {
var tag_name = previous_token.opened.text.substr(1).toLowerCase();
if (tag_name === 'script' || tag_name === 'style') {
// Script and style tags are allowed to have comments wrapping their content
// or just have regular content.
var token = this._read_comment(c);
if (token) {
token.type = TOKEN.TEXT;
return token;
}
resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
} else 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(c) {
var resulting_string = '';
if (this._options.unformatted_content_delimiter) {
if (c === this._options.unformatted_content_delimiter[0]) {
resulting_string = this.__patterns.unformatted_content_delimiter.read();
}
}
if (!resulting_string) {
resulting_string = this.__patterns.word.read();
}
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');
@ -35,7 +37,8 @@ function style_html(html_source, options, js, css) {
css = css || css_beautify;
return html_beautify(html_source, options, js, css);
}
style_html.defaultOptions = html_beautify.defaultOptions;
module.exports.js = js_beautify;
module.exports.css = css_beautify;
module.exports.html = style_html;
module.exports.html = style_html;

View File

@ -0,0 +1,57 @@
/* 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
// taken from [Esprima](http://esprima.org) by Ariya Hidayat.
//
// Git repositories for Acorn are available at
//
// http://marijnhaverbeke.nl/git/acorn
// https://github.com/marijnh/acorn.git
// ## 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 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 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 = "(?:\\\\u[0-9a-fA-F]{4}|[" + baseASCIIidentifierStartChars + nonASCIIidentifierStartChars + "])";
var identifierChars = "(?:\\\\u[0-9a-fA-F]{4}|[" + baseASCIIidentifierChars + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "])*";
exports.identifier = new RegExp(identifierStart + identifierChars, 'g');
exports.identifierStart = new RegExp(identifierStart);
exports.identifierMatch = new RegExp("(?:\\\\u[0-9a-fA-F]{4}|[" + baseASCIIidentifierChars + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "])+");
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]/;
// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.
// 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');

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +1,42 @@
/*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.
*/
var Beautifier = require('./beautifier').Beautifier;
'use strict';
var Beautifier = require('./beautifier').Beautifier,
Options = require('./options').Options;
function js_beautify(js_source_text, options) {
var beautifier = new Beautifier(js_source_text, options);
return beautifier.beautify();
}
module.exports = js_beautify;
module.exports = js_beautify;
module.exports.defaultOptions = function() {
return new 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;

File diff suppressed because it is too large Load Diff

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);
@ -100,4 +129,4 @@ var JavascriptObfuscator = {
}
};
};

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)) {
@ -87,4 +116,4 @@ var MyObfuscate = {
}
};
};

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);
@ -80,4 +108,4 @@ var P_A_C_K_E_R = {
}
};
};

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');
@ -70,4 +101,4 @@ var Urlencoded = {
if (isNode) {
module.exports = Urlencoded;
}
}

View File

@ -1,5 +1,7 @@
/*jshint node:true */
'use strict';
var requirejs = require('requirejs'),
SanityTest = require('./sanitytest'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),
@ -60,4 +62,4 @@ if (require.main === module) {
exit = exit || amd_beautifier_index_tests('html-beautifier', run_html_tests);
process.exit(exit);
}
}

View File

@ -1,10 +0,0 @@
var assert = require('assert');
var InputScanner = require('../../src/core/inputscanner').InputScanner;
describe('IndexScanner', function() {
describe('new', function() {
it('should return empty scanner when input is not present', function() {
assert.equal(new InputScanner().hasNext(), false);
});
});
});

View File

@ -0,0 +1,66 @@
/*jshint mocha:true */
'use strict';
var assert = require('assert');
var InputScanner = require('../../src/core/inputscanner').InputScanner;
describe('IndexScanner', function() {
describe('new', function() {
it('should return empty scanner when input is not present', function() {
assert.equal(new InputScanner().hasNext(), false);
});
});
describe('next', function() {
it('should return the value at current index and increments the index', function() {
var value = 'howdy';
var inputText = new InputScanner(value);
assert.equal(inputText.next(), value[0]);
assert.equal(inputText.next(), value[1]);
});
});
describe('peek', function() {
it('should return value at index passed as parameter', function() {
var value = 'howdy';
var inputText = new InputScanner(value);
assert.equal(inputText.peek(3), value[3]);
inputText.next();
assert.equal(inputText.peek(3), value[4]);
});
});
describe('peek without parameters', function() {
it('should return value at index 0 if parameter is not present', function() {
var value = 'howdy';
var inputText = new InputScanner(value);
assert.equal(inputText.peek(), value[0]);
inputText.next();
assert.equal(inputText.peek(3), value[4]);
});
});
describe('test', function() {
it('should return whether the pattern is matched or not', function() {
var value = 'howdy';
var pattern = /how/;
var index = 0;
var inputText = new InputScanner(value);
assert.equal(inputText.test(pattern, index), true);
inputText.next();
assert.equal(inputText.test(pattern, index), false);
});
});
describe('testChar', function() {
it('should return whether pattern matched or not for particular index', function() {
var value = 'howdy';
var pattern = /o/;
var index = 1;
var inputText = new InputScanner(value);
assert.equal(inputText.testChar(pattern, index), true);
});
});
});

View File

@ -0,0 +1,150 @@
/*jshint mocha:true */
'use strict';
var assert = require('assert');
var options = require('../../src/core/options');
describe('Options', function() {
describe('mergeOpts', function() {
it('should merge child option a with the parent options', function() {
assert.deepEqual(options.mergeOpts({
a: 1,
b: { a: 2 }
}, 'b'), {
a: 2
});
});
it('should include child option c and d with the parent options', function() {
assert.deepEqual(options.mergeOpts({
a: 1,
b: { c: 2, d: 3 }
}, 'b'), {
a: 1,
c: 2,
d: 3
});
});
it('should merge child option a and include c with the parent options', function() {
assert.deepEqual(options.mergeOpts({
a: 1,
b: { a: 2, c: 3 }
}, 'b'), {
a: 2,
c: 3
});
});
});
describe('normalizeOpts', function() {
it('should replace key with - to _', function() {
assert.deepEqual(options.normalizeOpts({
'a-b': 1
}), {
a_b: 1
});
});
it('should do nothing', function() {
assert.deepEqual(options.mergeOpts({
a: 1,
b_c: 2
}, 'b'), {
a: 1,
b_c: 2
});
});
});
describe('_get_boolean', function() {
it('should return false with no option and no default', function() {
assert.equal(new options.Options()._get_boolean(), false);
});
it('should return true as default since no option', function() {
assert.equal(new options.Options()._get_boolean('a', true), true);
});
it('should return false as in option', function() {
assert.equal(new options.Options({ a: false })._get_boolean('a', true), false);
});
});
describe('_get_characters', function() {
it('should return \'\' with no option', function() {
assert.equal(new options.Options()._get_characters(), '');
});
it('should return \'character\' as default since no option', function() {
assert.equal(new options.Options()._get_characters('a', 'character'), 'character');
});
it('should return \'char\' as in option', function() {
assert.equal(new options.Options({ a: 'char' })._get_characters('a', 'character'), 'char');
});
});
describe('_get_number', function() {
it('should return 0 with no option', function() {
assert.equal(new options.Options()._get_number(), 0);
});
it('should return 1 as default since no option', function() {
assert.equal(new options.Options()._get_number('a', 1), 1);
});
it('should return 10 as in option', function() {
assert.equal(new options.Options({ a: 10 })._get_number('a', 1), 10);
});
it('should return 0 for NaN as in option', function() {
assert.equal(new options.Options({ a: 'abc' })._get_number('a'), 0);
});
it('should return 0 for NaN as in default', function() {
assert.equal(new options.Options()._get_number('a', 'abc'), 0);
});
});
describe('_get_array', function() {
it('should return [] with no option', function() {
assert.deepEqual(new options.Options()._get_array(), []);
});
it('should return [\'a\',\'b\'] as default since no option', function() {
assert.deepEqual(new options.Options()._get_array('a', ['a', 'b']), ['a', 'b']);
});
it('should return [\'c\',\'d\'] as in option', function() {
assert.deepEqual(new options.Options({ a: ['c', 'd'] })._get_array('a', ['a', 'b']), ['c', 'd']);
});
it('should return [\'c\',\'d\'] as in option comma separated', function() {
assert.deepEqual(new options.Options({ a: 'c,d' })._get_array('a', ['a', 'b']), ['c', 'd']);
});
});
describe('_is_valid_selection', function() {
it('should return false with empty selection', function() {
assert.equal(new options.Options()._is_valid_selection(['a', 'b'], []), false);
});
it('should return false with selection inexistent', function() {
assert.equal(new options.Options()._is_valid_selection(['a', 'b'], ['c']), false);
});
it('should return true with selection existent', function() {
assert.equal(new options.Options()._is_valid_selection(['a', 'b'], ['a', 'b']), true);
});
});
describe('_get_selection_list', function() {
it('should throw error with empty selection', function() {
assert.throws(new options.Options()._get_selection_list, /^Error: Selection list cannot be empty.$/);
});
it('should throw error with invalid default', function() {
assert.throws(function() { new options.Options()._get_selection_list('a', ['a', 'b'], ['c']); }, /^Error: Invalid Default Value!$/);
});
it('should throw error with invalid option', function() {
assert.throws(function() { new options.Options({ a: ['c', 'd'] })._get_selection_list('a', ['a', 'b'], ['a']); }, /^Error: Invalid Option Value: The option/);
});
it('should return [\'a\'] as in option', function() {
assert.deepEqual(new options.Options({ a: ['a'] })._get_selection_list('a', ['a', 'b'], ['a']), ['a']);
});
});
describe('_get_selection', function() {
it('should throw error with multiple selection', function() {
assert.throws(function() { new options.Options({ a: ['a', 'b'] })._get_selection('a', ['a', 'b'], ['a']); }, /^Error: Invalid Option Value: The option/);
});
it('should return \'a\' as in option ', function() {
assert.equal(new options.Options({ a: ['a'] })._get_selection('a', ['a', 'b'], ['a']), 'a');
});
});
});

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));
@ -46,4 +51,4 @@ function node_beautifier_html_tests() {
if (require.main === module) {
process.exit(node_beautifier_html_tests());
}
}

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));
@ -45,4 +51,4 @@ function node_beautifier_tests() {
if (require.main === module) {
process.exit(node_beautifier_tests());
}
}

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,
@ -16,10 +18,18 @@ function test_legacy_names() {
results.expect(typeof beautify.css, 'function');
results.expect(typeof beautify.html, 'function');
console.log('Ensure defaultOptions are defined');
results.expect(typeof beautify.js.defaultOptions, 'function');
results.expect(typeof beautify.css.defaultOptions, 'function');
results.expect(typeof beautify.html.defaultOptions, 'function');
console.log('Ensure that legacy import names equal the new ones');
results.expect(beautify.js, beautify.js_beautify);
results.expect(beautify.css, beautify.css_beautify);
results.expect(beautify.html, beautify.html_beautify);
results.expect(beautify.js.defaultOptions, beautify.js_beautify.defaultOptions);
results.expect(beautify.css.defaultOptions, beautify.css_beautify.defaultOptions);
results.expect(beautify.html.defaultOptions, beautify.html_beautify.defaultOptions);
console.log(results.results_raw());
return results.get_exitcode();
@ -68,4 +78,4 @@ if (require.main === module) {
exit = exit || node_beautifier_bundle_tests('html-beautifier', run_html_tests);
process.exit(exit);
}
}

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,
@ -45,4 +47,4 @@ if (require.main === module) {
exit = exit || node_beautifier_index_tests('html-beautifier', run_html_tests);
process.exit(exit);
}
}

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;
}
};
@ -141,4 +143,4 @@ function SanityTest(func, name_of_test) {
if (typeof module !== 'undefined' && module.exports) {
module.exports = SanityTest;
}
}

View File

@ -2,59 +2,68 @@
REL_SCRIPT_DIR="`dirname \"$0\"`"
SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`"
PROJECT_DIR="`( cd \"$SCRIPT_DIR/../..\" && pwd )`"
case "$OSTYPE" in
darwin*) PLATFORM="OSX" ;;
linux*) PLATFORM="LINUX" ;;
bsd*) PLATFORM="BSD" ;;
*) PLATFORM="UNKNOWN" ;;
esac
test_cli_common()
{
echo ----------------------------------------
echo Testing common cli behavior...
CLI_SCRIPT_NAME=${1:?missing_param}.js
CLI_SCRIPT=${2:-$SCRIPT_DIR/../bin/$CLI_SCRIPT_NAME}
echo Script: $CLI_SCRIPT
echo ----------------------------------------
echo Testing common cli behavior...
CLI_SCRIPT_NAME=${1:?missing_param}.js
CLI_SCRIPT=${2:-$SCRIPT_DIR/../bin/$CLI_SCRIPT_NAME}
echo Script: $CLI_SCRIPT
# should find the minimal help output
$CLI_SCRIPT 2>&1 | grep -q "Must pipe input or define at least one file\." || {
$CLI_SCRIPT 2>&1
echo "[$CLI_SCRIPT_NAME] Output should be help message."
exit 1
}
# should find the minimal help output
$CLI_SCRIPT 2>&1 < /dev/null | grep -q "Must pipe input or define at least one file\." || {
$CLI_SCRIPT 2>&1 < /dev/null
echo "[$CLI_SCRIPT_NAME] Output should be help message."
exit 1
}
$CLI_SCRIPT 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME (with no parameters)] Return code should be error."
exit 1
}
$CLI_SCRIPT 2> /dev/null < /dev/null && {
echo "[$CLI_SCRIPT_NAME (with no parameters)] Return code should be error."
exit 1
}
$CLI_SCRIPT -Z 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME -Z] Return code for invalid parameter should be error."
exit 1
}
$CLI_SCRIPT -Z 2> /dev/null < /dev/null && {
echo "[$CLI_SCRIPT_NAME -Z] Return code for invalid parameter should be error."
exit 1
}
$CLI_SCRIPT -h > /dev/null || {
echo "[$CLI_SCRIPT_NAME -h] Return code should be success."
exit 1
}
$CLI_SCRIPT -h > /dev/null || {
echo "[$CLI_SCRIPT_NAME -h] Return code should be success."
exit 1
}
$CLI_SCRIPT -v > /dev/null || {
echo "[$CLI_SCRIPT_NAME -v] Return code should be success."
exit 1
}
$CLI_SCRIPT -v > /dev/null || {
echo "[$CLI_SCRIPT_NAME -v] Return code should be success."
exit 1
}
MISSING_FILE="$SCRIPT_DIR/../../../js/bin/missing_file"
MISSING_FILE_MESSAGE="Unable to open path"
$CLI_SCRIPT $MISSING_FILE 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Return code should be error."
exit 1
}
MISSING_FILE="$SCRIPT_DIR/../../../js/bin/missing_file"
MISSING_FILE_MESSAGE="Unable to open path"
$CLI_SCRIPT $MISSING_FILE 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Return code should be error."
exit 1
}
$CLI_SCRIPT $MISSING_FILE 2>&1 | grep -q "$MISSING_FILE_MESSAGE" || {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stderr should have useful message."
exit 1
}
$CLI_SCRIPT $MISSING_FILE 2>&1 | grep -q "$MISSING_FILE_MESSAGE" || {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stderr should have useful message."
exit 1
}
if [ "`$CLI_SCRIPT $MISSING_FILE 2> /dev/null`" != "" ]; then
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stdout should have no text."
exit 1
fi
if [ "`$CLI_SCRIPT $MISSING_FILE 2> /dev/null`" != "" ]; then
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stdout should have no text."
exit 1
fi
}
setup_temp()
@ -73,305 +82,356 @@ cleanup()
test_cli_js_beautify()
{
echo ----------------------------------------
echo Testing js-beautify cli behavior...
CLI_SCRIPT=${1:-$SCRIPT_DIR/../bin/js-beautify.js}
echo ----------------------------------------
echo Testing js-beautify cli behavior...
CLI_SCRIPT=${1:-$SCRIPT_DIR/../bin/js-beautify.js}
$CLI_SCRIPT $SCRIPT_DIR/../bin/js-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../bin/js-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../bin/css-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../bin/css-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../bin/css-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../bin/css-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../bin/js-beautify.js | diff $SCRIPT_DIR/../bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
exit 1
}
setup_temp
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-file.js $SCRIPT_DIR/../bin/js-beautify.js && diff $TEST_TEMP/js-beautify-file.js $SCRIPT_DIR/../bin/js-beautify.js || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
cleanup 1
}
node $SCRIPT_DIR/../lib/cli.js $SCRIPT_DIR/../bin/js-beautify.js | diff $SCRIPT_DIR/../bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
exit 1
}
cat $SCRIPT_DIR/../bin/js-beautify.js | $CLI_SCRIPT -o $TEST_TEMP/js-beautify-pipe.js - && diff $TEST_TEMP/js-beautify-pipe.js $SCRIPT_DIR/../bin/js-beautify.js || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js should have been created in $TEST_TEMP/js-beautify-pipe.js."
cleanup 1
}
cat $SCRIPT_DIR/../bin/js-beautify.js | $CLI_SCRIPT | diff $SCRIPT_DIR/../bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../bin/js-beautify.js | diff $SCRIPT_DIR/../bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
cleanup 1
}
cat $SCRIPT_DIR/../bin/js-beautify.js | $CLI_SCRIPT - | diff $SCRIPT_DIR/../bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
exit 1
}
node $SCRIPT_DIR/../lib/cli.js $SCRIPT_DIR/../bin/js-beautify.js | diff $SCRIPT_DIR/../bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
cleanup 1
}
setup_temp
cat $SCRIPT_DIR/../bin/js-beautify.js | $CLI_SCRIPT -o $TEST_TEMP/js-beautify-pipe.js - && diff $TEST_TEMP/js-beautify-pipe.js $SCRIPT_DIR/../bin/js-beautify.js || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js should have been created in $TEST_TEMP/js-beautify-pipe.js."
cleanup 1
}
cat $SCRIPT_DIR/../bin/js-beautify.js | $CLI_SCRIPT | diff $SCRIPT_DIR/../bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
cleanup 1
}
$CLI_SCRIPT -o $TEST_TEMP/js-beautify.js $SCRIPT_DIR/../bin/js-beautify.js && diff $SCRIPT_DIR/../bin/js-beautify.js $TEST_TEMP/js-beautify.js || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js should have been created in $TEST_TEMP/js-beautify.js."
cleanup 1
}
cat $SCRIPT_DIR/../bin/js-beautify.js | $CLI_SCRIPT - | diff $SCRIPT_DIR/../bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
cleanup 1
}
cat $SCRIPT_DIR/../bin/js-beautify.js | $CLI_SCRIPT -f - | diff $SCRIPT_DIR/../bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
cleanup 1
}
# ensure new line settings work
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-n.js -e '\n' $SCRIPT_DIR/../bin/js-beautify.js
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-rn.js -e '\r\n' $TEST_TEMP/js-beautify-n.js
$CLI_SCRIPT -o $TEST_TEMP/js-beautify.js $SCRIPT_DIR/../bin/js-beautify.js && diff $SCRIPT_DIR/../bin/js-beautify.js $TEST_TEMP/js-beautify.js || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js should have been created in $TEST_TEMP/js-beautify.js."
cleanup 1
}
# ensure eol processed correctly
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-n-dash.js --indent-size 2 --eol '\n' $TEST_TEMP/js-beautify-n.js
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-rn-dash.js --indent-size 2 --eol '\r\n' $TEST_TEMP/js-beautify-n.js
diff -q $TEST_TEMP/js-beautify-n-dash.js $TEST_TEMP/js-beautify-rn-dash.js && {
diff $TEST_TEMP/js-beautify-n-dash.js $TEST_TEMP/js-beautify-rn-dash.js | cat -t -e
echo "js-beautify output for $TEST_TEMP/js-beautify-n-dash.js and $TEST_TEMP/js-beautify-rn-dash.js was expected to be different."
cleanup 1
}
# ensure new line settings work
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-n.js -e '\n' $SCRIPT_DIR/../bin/js-beautify.js
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-rn.js -e '\r\n' $TEST_TEMP/js-beautify-n.js
diff -q $TEST_TEMP/js-beautify-n.js $TEST_TEMP/js-beautify-rn.js && {
diff $TEST_TEMP/js-beautify-n.js $TEST_TEMP/js-beautify-rn.js | cat -t -e
echo "js-beautify output for $TEST_TEMP/js-beautify-n.js and $TEST_TEMP/js-beautify-rn.js was expected to be different."
cleanup 1
}
# ensure eol processed correctly
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-n-dash.js --indent-size 2 --eol '\n' $TEST_TEMP/js-beautify-n.js
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-rn-dash.js --indent-size 2 --eol '\r\n' $TEST_TEMP/js-beautify-n.js
diff -q $TEST_TEMP/js-beautify-n-dash.js $TEST_TEMP/js-beautify-rn-dash.js && {
diff $TEST_TEMP/js-beautify-n-dash.js $TEST_TEMP/js-beautify-rn-dash.js | cat -t -e
echo "js-beautify output for $TEST_TEMP/js-beautify-n-dash.js and $TEST_TEMP/js-beautify-rn-dash.js was expected to be different."
cleanup 1
}
$CLI_SCRIPT $TEST_TEMP/js-beautify-n.js | diff -q $TEST_TEMP/js-beautify-n.js - || {
echo "js-beautify output for $TEST_TEMP/js-beautify-n.js was expected to be unchanged."
cleanup 1
}
diff -q $TEST_TEMP/js-beautify-n.js $TEST_TEMP/js-beautify-rn.js && {
diff $TEST_TEMP/js-beautify-n.js $TEST_TEMP/js-beautify-rn.js | cat -t -e
echo "js-beautify output for $TEST_TEMP/js-beautify-n.js and $TEST_TEMP/js-beautify-rn.js was expected to be different."
cleanup 1
}
$CLI_SCRIPT -e 'auto' $TEST_TEMP/js-beautify-rn.js | diff -q $TEST_TEMP/js-beautify-rn.js - || {
echo "js-beautify output for $TEST_TEMP/js-beautify-rn.js was expected to be unchanged."
cleanup 1
}
$CLI_SCRIPT $TEST_TEMP/js-beautify-n.js | diff -q $TEST_TEMP/js-beautify-n.js - || {
echo "js-beautify output for $TEST_TEMP/js-beautify-n.js was expected to be unchanged."
cleanup 1
}
# EditorConfig related tests
cp -r js/test/resources/editorconfig $TEST_TEMP/
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/example.js --end-with-newline --indent-size 4 -e '\n' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/example-ec.js --indent-size 2 -e '\n' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -e 'auto' $TEST_TEMP/js-beautify-rn.js | diff -q $TEST_TEMP/js-beautify-rn.js - || {
echo "js-beautify output for $TEST_TEMP/js-beautify-rn.js was expected to be unchanged."
cleanup 1
}
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/cr/example.js --end-with-newline --indent-size 4 -e '\n' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/cr/example-ec.js --indent-size 2 -e '\r' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/crlf/example.js --end-with-newline --indent-size 4 -e '\n' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/crlf/example-ec.js --indent-size 2 -e '\r\n' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/error/example.js --end-with-newline --indent-size 4 -e '\n' $TEST_TEMP/editorconfig/example-base.js
pushd $TEST_TEMP/editorconfig
cd $TEST_TEMP/editorconfig/error
$CLI_SCRIPT --editorconfig $TEST_TEMP/js-beautify-n.js \
> /dev/null || {
echo "Invalid editorconfig file should not report error (consistent with the EditorConfig)."
cleanup 1
}
$CLI_SCRIPT --editorconfig example.js \
> /dev/null || {
echo "Invalid editorconfig file should not report error (consistent with the EditorConfig)."
cleanup 1
}
# TODO: EditorConfig setting should NOT overide cli setting, but that is
# the current by-design behavior, due to code limitations.
# file input scenario
SCENARIO=a
cd $TEST_TEMP/editorconfig || exit 1
$CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js example.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
cd $TEST_TEMP/editorconfig/crlf || exit 1
$CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js example.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
cd $TEST_TEMP/editorconfig/cr || exit 1
$CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js example.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
# stdin input to stdout scenario
SCENARIO=b
cd $TEST_TEMP/editorconfig || exit 1
echo "cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js"
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
cd $TEST_TEMP/editorconfig/crlf || exit 1
echo "cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js"
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
cd $TEST_TEMP/editorconfig/cr || exit 1
echo "cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js"
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
# Glob related tests
cp -r $PROJECT_DIR/js/src $TEST_TEMP/
FILE_RCOUNT=$(find $PROJECT_DIR/js/src -name 't*.js' | grep -c .)
FILE_COUNT=$(ls $PROJECT_DIR/js/src/*.js | grep -c .)
MISSING_FILE_GLOB="*/*/missing_file"
$CLI_SCRIPT $MISSING_FILE_GLOB > /dev/null || {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE_GLOB] Return code should be success for globs."
exit 1
}
# stdin input to file scenario
SCENARIO=c
cd $TEST_TEMP/editorconfig || exit 1
echo "cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js"
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js - \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
if [ "$FILE_COUNT" != "$(cd $TEST_TEMP && $CLI_SCRIPT 'src/*.js' | grep -c .)" ]; then
echo "js-beautify output for 'src/*.js' was expected have $FILE_COUNT files."
echo $(cd $TEST_TEMP && $CLI_SCRIPT 'src/*.js')
cleanup 1
fi
cd $TEST_TEMP/editorconfig/crlf || exit 1
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js - \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
if [ "$FILE_COUNT" != "$(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/*.js' | grep -c .)" ]; then
echo "js-beautify output for 'src/*.js' was expected have $FILE_COUNT files."
echo $(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/*.js')
cleanup 1
fi
cd $TEST_TEMP/editorconfig/cr || exit 1
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js - \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
if [ "$FILE_COUNT" != "$(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/cl?.js' --file 'src/??dex.js' | grep -c .)" ]; then
echo "js-beautify output for --file 'src/cl?.js' --file 'src/??dex.js' was expected have $FILE_COUNT files."
echo $(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/cl?.js' --file 'src/??dex.js')
cleanup 1
fi
popd
# End EditorConfig
if [ "1" != "$(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/cl?.js' --file 'src/c??.js' 'src/cli.js' | grep -c .)" ]; then
echo "js-beautify output for --file 'src/cl?.js' --file 'src/cl?.js' was expected have 1 file."
echo $(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/cl?.js' --file 'src/c??.js' 'src/cli.js')
cleanup 1
fi
# ensure unchanged files are not overwritten
$CLI_SCRIPT -o $TEST_TEMP/js-beautify.js $SCRIPT_DIR/../bin/js-beautify.js
cp -p $TEST_TEMP/js-beautify.js $TEST_TEMP/js-beautify-old.js
touch $TEST_TEMP/js-beautify.js
sleep 2
touch $TEST_TEMP/js-beautify-old.js
$CLI_SCRIPT -r $TEST_TEMP/js-beautify.js && test $TEST_TEMP/js-beautify.js -nt $TEST_TEMP/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file $TEST_TEMP/js-beautify.js when using -r"
cleanup 1
}
if [ "$FILE_RCOUNT" != "$(cd $TEST_TEMP && $CLI_SCRIPT 'src/**/t*.js' | grep -c .)" ]; then
echo "js-beautify output for 'src/**/t*.js' was expected have $FILE_RCOUNT files."
echo $(cd $TEST_TEMP && $CLI_SCRIPT 'src/**/t*.js')
cleanup 1
fi
$CLI_SCRIPT -o $TEST_TEMP/js-beautify.js $TEST_TEMP/js-beautify.js && test $TEST_TEMP/js-beautify.js -nt $TEST_TEMP/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file $TEST_TEMP/js-beautify.js when using -o and same file name"
cleanup 1
}
# EditorConfig related tests
cp -r js/test/resources/editorconfig $TEST_TEMP/
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/example.js --end-with-newline --indent-size 4 -e '\n' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/example-ec.js --indent-size 2 -e '\n' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -o $TEST_TEMP/js-beautify.js $TEST_TEMP/js-beautify-old.js && test $TEST_TEMP/js-beautify.js -nt $TEST_TEMP/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file $TEST_TEMP/js-beautify.js when using -o and different file name"
cleanup 1
}
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/cr/example.js --end-with-newline --indent-size 4 -e '\n' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/cr/example-ec.js --indent-size 2 -e '\r' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT $SCRIPT_DIR/../bin/css-beautify.js | diff -q $SCRIPT_DIR/../bin/css-beautify.js - && {
echo "js-beautify output for $SCRIPT_DIR/../bin/css-beautify.js was expected to be different."
cleanup 1
}
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/crlf/example.js --end-with-newline --indent-size 4 -e '\n' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/crlf/example-ec.js --indent-size 2 -e '\r\n' $TEST_TEMP/editorconfig/example-base.js
unset HOME
unset USERPROFILE
$CLI_SCRIPT -o $TEST_TEMP/example1-default.js $SCRIPT_DIR/resources/example1.js || exit 1
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/error/example.js --end-with-newline --indent-size 4 -e '\n' $TEST_TEMP/editorconfig/example-base.js
$CLI_SCRIPT -o $TEST_TEMP/example1-sanity.js $TEST_TEMP/example1-default.js || exit 1
diff -q $TEST_TEMP/example1-default.js $TEST_TEMP/example1-sanity.js || {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be identical after no change in settings."
cleanup 1
}
pushd $TEST_TEMP/editorconfig
cd $SCRIPT_DIR/resources/configerror
$CLI_SCRIPT $TEST_TEMP/example1-default.js 2>&1 | grep -q "Error while loading beautifier configuration\." || {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be configration load error message."
cleanup 1
}
cd $TEST_TEMP/editorconfig/error
$CLI_SCRIPT --editorconfig $TEST_TEMP/js-beautify-n.js \
> /dev/null || {
echo "Invalid editorconfig file should not report error (consistent with the EditorConfig)."
cleanup 1
}
cd $SCRIPT_DIR/resources/indent11chars
$CLI_SCRIPT $TEST_TEMP/example1-default.js | diff -q $TEST_TEMP/example1-default.js - && {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be different based on CWD settings."
cleanup 1
}
$CLI_SCRIPT --editorconfig example.js \
> /dev/null || {
echo "Invalid editorconfig file should not report error (consistent with the EditorConfig)."
cleanup 1
}
cd $SCRIPT_DIR/resources/indent11chars/subDir1/subDir2
$CLI_SCRIPT $TEST_TEMP/example1-default.js | diff -q $TEST_TEMP/example1-default.js - && {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be different based on CWD parent folder settings."
cleanup 1
}
cd $SCRIPT_DIR
# TODO: EditorConfig setting should NOT overide cli setting, but that is
# the current by-design behavior, due to code limitations.
export HOME=$SCRIPT_DIR/resources/indent11chars
$CLI_SCRIPT $TEST_TEMP/example1-default.js | diff -q $TEST_TEMP/example1-default.js - && {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be different based on HOME settings."
cleanup 1
}
# file input scenario
SCENARIO=a
cd $TEST_TEMP/editorconfig || exit 1
$CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js example.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
$CLI_SCRIPT -o $TEST_TEMP/example1-indent11chars.js $TEST_TEMP/example1-default.js
cd $TEST_TEMP/editorconfig/crlf || exit 1
$CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js example.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
unset HOME
export USERPROFILE=$SCRIPT_DIR/resources/indent11chars
# node -p 'process.env["USERPROFILE"] || process.env["HOME"] || "unset"'
$CLI_SCRIPT $TEST_TEMP/example1-default.js | diff -q $TEST_TEMP/example1-indent11chars.js - || {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be identical for same HOME and USERPROFILE settings."
cleanup 1
}
cd $TEST_TEMP/editorconfig/cr || exit 1
$CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js example.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
$CLI_SCRIPT $TEST_TEMP/example1-default.js | diff -q $TEST_TEMP/example1-default.js - && {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be different based on USERPROFILE settings."
cleanup 1
}
# stdin input to stdout scenario
SCENARIO=b
cd $TEST_TEMP/editorconfig || exit 1
echo "cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js"
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
#meta-parameter brace_style
$CLI_SCRIPT -b 'invalid' $TEST_TEMP/example1-default.js > /dev/null && {
cd $TEST_TEMP/editorconfig/crlf || exit 1
echo "cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js"
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
cd $TEST_TEMP/editorconfig/cr || exit 1
echo "cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js"
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig > example-${SCENARIO}.js \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
# stdin input to file scenario
SCENARIO=c
cd $TEST_TEMP/editorconfig || exit 1
echo "cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js"
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js - \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
cd $TEST_TEMP/editorconfig/crlf || exit 1
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js - \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
cd $TEST_TEMP/editorconfig/cr || exit 1
cat example.js | $CLI_SCRIPT --end-with-newline --indent-size 6 --editorconfig -o example-${SCENARIO}.js - \
&& diff -q example-${SCENARIO}.js example-ec.js || {
echo "EditorConfig setting should overide cli setting."
diff example-${SCENARIO}.js example-ec.js | cat -t -e
cleanup 1
}
popd
# End EditorConfig
# ensure unchanged files are not overwritten
$CLI_SCRIPT -o $TEST_TEMP/js-beautify.js $SCRIPT_DIR/../bin/js-beautify.js
cp -p $TEST_TEMP/js-beautify.js $TEST_TEMP/js-beautify-old.js
touch $TEST_TEMP/js-beautify.js
sleep 2
touch $TEST_TEMP/js-beautify-old.js
$CLI_SCRIPT -r $TEST_TEMP/js-beautify.js && test $TEST_TEMP/js-beautify.js -nt $TEST_TEMP/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file $TEST_TEMP/js-beautify.js when using -r"
cleanup 1
}
$CLI_SCRIPT -o $TEST_TEMP/js-beautify.js $TEST_TEMP/js-beautify.js && test $TEST_TEMP/js-beautify.js -nt $TEST_TEMP/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file $TEST_TEMP/js-beautify.js when using -o and same file name"
cleanup 1
}
$CLI_SCRIPT -o $TEST_TEMP/js-beautify.js $TEST_TEMP/js-beautify-old.js && test $TEST_TEMP/js-beautify.js -nt $TEST_TEMP/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file $TEST_TEMP/js-beautify.js when using -o and different file name"
cleanup 1
}
$CLI_SCRIPT $SCRIPT_DIR/../bin/css-beautify.js | diff -q $SCRIPT_DIR/../bin/css-beautify.js - && {
echo "js-beautify output for $SCRIPT_DIR/../bin/css-beautify.js was expected to be different."
cleanup 1
}
unset HOME
unset USERPROFILE
$CLI_SCRIPT -o $TEST_TEMP/example1-default.js $SCRIPT_DIR/resources/example1.js || exit 1
$CLI_SCRIPT -o $TEST_TEMP/example1-sanity.js $TEST_TEMP/example1-default.js || exit 1
diff -q $TEST_TEMP/example1-default.js $TEST_TEMP/example1-sanity.js || {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be identical after no change in settings."
cleanup 1
}
cd $SCRIPT_DIR/resources/configerror
$CLI_SCRIPT $TEST_TEMP/example1-default.js 2>&1 | grep -q "Error while loading beautifier configuration\." || {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be configration load error message."
cleanup 1
}
cd $SCRIPT_DIR/resources/indent11chars
$CLI_SCRIPT $TEST_TEMP/example1-default.js | diff -q $TEST_TEMP/example1-default.js - && {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be different based on CWD settings."
cleanup 1
}
cd $SCRIPT_DIR/resources/indent11chars/subDir1/subDir2
$CLI_SCRIPT $TEST_TEMP/example1-default.js | diff -q $TEST_TEMP/example1-default.js - && {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be different based on CWD parent folder settings."
cleanup 1
}
cd $SCRIPT_DIR
export HOME=$SCRIPT_DIR/resources/indent11chars
$CLI_SCRIPT $TEST_TEMP/example1-default.js | diff -q $TEST_TEMP/example1-default.js - && {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be different based on HOME settings."
cleanup 1
}
$CLI_SCRIPT -o $TEST_TEMP/example1-indent11chars.js $TEST_TEMP/example1-default.js
unset HOME
export USERPROFILE=$SCRIPT_DIR/resources/indent11chars
# node -p 'process.env["USERPROFILE"] || process.env["HOME"] || "unset"'
$CLI_SCRIPT $TEST_TEMP/example1-default.js | diff -q $TEST_TEMP/example1-indent11chars.js - || {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be identical for same HOME and USERPROFILE settings."
cleanup 1
}
$CLI_SCRIPT $TEST_TEMP/example1-default.js | diff -q $TEST_TEMP/example1-default.js - && {
echo "js-beautify output for $TEST_TEMP/example1-default.js was expected to be different based on USERPROFILE settings."
cleanup 1
}
#meta-parameter brace_style
$CLI_SCRIPT -b 'invalid' $TEST_TEMP/example1-default.js > /dev/null && {
echo "[$CLI_SCRIPT_NAME -b 'invalid' $TEST_TEMP/example1-default.js] Return code for invalid brace_style meta-parameter should be error."
cleanup 1
}
$CLI_SCRIPT -b 'expand,preserve-inline,invalid' $TEST_TEMP/example1-default.js > /dev/null && {
}
$CLI_SCRIPT -b 'expand,preserve-inline,invalid' $TEST_TEMP/example1-default.js > /dev/null && {
echo "[$CLI_SCRIPT_NAME -b 'expand,preserve-inline,invalid' $TEST_TEMP/example1-default.js] Return code for invalid brace_style meta-parameter should be error."
cleanup 1
}
$CLI_SCRIPT -b 'preserve-inline' $TEST_TEMP/example1-default.js > /dev/null || {
}
$CLI_SCRIPT -b 'preserve-inline' $TEST_TEMP/example1-default.js > /dev/null || {
echo "[$CLI_SCRIPT_NAME -b 'preserve-inline' $TEST_TEMP/example1-default.js] Return code for only one part of valid brace_style meta-parameter should be success (uses default where it can)."
cleanup 1
}
}
cleanup
cleanup
}
main() {
test_cli_common css-beautify
test_cli_common html-beautify
test_cli_common js-beautify
test_cli_common css-beautify $SCRIPT_DIR/../../build/node_modules/.bin/css-beautify
test_cli_common html-beautify $SCRIPT_DIR/../../build/node_modules/.bin/html-beautify
test_cli_common js-beautify $SCRIPT_DIR/../../build/node_modules/.bin/js-beautify
test_cli_js_beautify
echo ----------------------------------------
echo $0 - PASSED.
echo ----------------------------------------
}
test_cli_common css-beautify
test_cli_common html-beautify
test_cli_common js-beautify
test_cli_common css-beautify $SCRIPT_DIR/../../build/node_modules/.bin/css-beautify
test_cli_common html-beautify $SCRIPT_DIR/../../build/node_modules/.bin/html-beautify
test_cli_common js-beautify $SCRIPT_DIR/../../build/node_modules/.bin/js-beautify
test_cli_js_beautify
echo ----------------------------------------
echo $0 - PASSED.
echo ----------------------------------------
(main $*)

View File

@ -2,6 +2,7 @@
"indent_size": 2,
"indent_char": " ",
"indent_level": 0,
"end-with-newline": true,
"indent_with_tabs": false,
"preserve_newlines": true,
"max_preserve_newlines": 10,

4948
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.10.0",
"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,24 @@
"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"
"config-chain": "^1.1.12",
"editorconfig": "^0.15.3",
"glob": "^7.1.3",
"mkdirp": "~0.5.1",
"nopt": "~4.0.1"
},
"devDependencies": {
"benchmark": "^2.1.4",
"jshint": "~2.9.1",
"mocha": "^5.2.0",
"mustache": "~2.3.0",
"node-static": "^0.7.10",
"requirejs": "^2.3.3",
"webpack": "^4.16.2",
"webpack-command": "^0.4.1"
"jshint": "^2.10.2",
"mocha": "^6.1.4",
"mustache": "^3.0.1",
"node-static": "^0.7.11",
"requirejs": "^2.3.6",
"webpack": "^4.29.6",
"webpack-command": "^0.4.2"
}
}

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>
@ -92,6 +92,7 @@ Output options:
--disable-newline-between-rules
Do not print empty line between rules.
--space-around-combinator Print spaces around combinator.
--indent-empty-lines Keep indentation on empty lines
-r, --replace Write output in-place, replacing input
-o, --outfile=FILE Specify a file to output to (default stdout)
@ -117,7 +118,7 @@ def main():
'indent-size=', 'indent-char=', 'eol=', 'indent-with-tabs',
'preserve-newlines', 'disable-selector-separator-newline',
'end-with-newline', 'disable-newline-between-rules',
'space-around-combinator'])
'space-around-combinator', 'indent-empty-lines'])
except getopt.GetoptError as ex:
print(ex, file=sys.stderr)
return usage(sys.stderr)
@ -160,6 +161,8 @@ def main():
css_options.newline_between_rules = False
elif opt in ('--space-around-combinator'):
css_options.space_around_combinator = True
elif opt in ('--indent-empty-lines'):
css_options.indent_empty_lines = True
if not file:
file = '-'

View File

@ -3,11 +3,16 @@ 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.core.directives import Directives
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 +39,8 @@ from jsbeautifier.__version__ import __version__
# SOFTWARE.
directives_core = Directives(r'/\*', r'\*/')
whitespaceChar = re.compile(r"\s")
whitespacePattern = re.compile(r"(?:\s|\n)+")
@ -63,7 +70,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 +79,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 +138,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 +155,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,74 +169,116 @@ 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):
self._output.set_indent(self._indentLevel)
self._output.non_breaking_space = True
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.readWhile(whitespacePattern)
whitespace = self._input.read(whitespacePattern)
isAfterSpace = whitespace != ''
self.ch = input.next()
previous_ch = topCharacter
self._ch = self._input.next()
if self._ch == '\\' and self._input.hasNext():
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.readWhile(
self.block_comment_pattern))
self._output.add_new_line()
self._input.back()
comment = self._input.read(self.block_comment_pattern)
# handle ignore directive
directives = directives_core.get_directives(comment)
if directives and directives.get('ignore') == 'start':
comment += directives_core.readIgnored(self._input)
self.print_string(comment)
# Ensures any new lines following the comment are preserved
self.eatWhitespace(True)
# 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.readWhile(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 +287,196 @@ 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 and \
parenLevel == 0:
# '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
insideAtExtend = False
printer.print_string(self.ch)
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 parenLevel == 0:
if insidePropertyValue:
self.outdent()
insidePropertyValue = False
insideAtExtend = False
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 == '(':
# 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 self._input.peek() is not '/':
self._output.add_new_line()
else:
self.print_string(self._ch)
self.eatWhitespace(True)
self._output.space_before_token = True
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
else:
parenLevel += 1
printer.preserveSingleSpace(isAfterSpace)
printer.print_string(self.ch)
self.indent()
self._ch = self._input.next()
if self._ch in {')', '"', '\''}:
self._input.back()
elif self._ch is not None:
self.print_string(self._ch + self.eatString(')'))
if parenLevel:
parenLevel -= 1
self.outdent()
else:
self.preserveSingleSpace(isAfterSpace)
self.print_string(self._ch)
self.eatWhitespace()
elif self.ch == ')':
printer.print_string(self.ch)
parenLevel -= 1
elif self.ch == ',':
printer.print_string(self.ch)
parenLevel += 1
self.indent()
elif self._ch == ')':
if parenLevel:
parenLevel -= 1
self.outdent()
self.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 == 0 and \
not insideAtImport:
self._output.add_new_line()
else:
output.space_before_token = True
elif (self.ch == '>' or self.ch == '+' or self.ch == '~') and \
not insidePropertyValue and parenLevel < 1:
self._output.space_before_token = True
elif (self._ch == '>' or self._ch == '+' or self._ch == '~') and \
not insidePropertyValue and parenLevel == 0:
# 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 == '!' and not (self._input.lookBack('\\')):
# !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

@ -6,7 +6,9 @@ import unittest
def run_tests():
suite = unittest.TestLoader().discover(
'jsbeautifier.tests', pattern="test*.py")
'jsbeautifier', pattern="test*.py")
suite.addTests(unittest.TestLoader().discover(
'cssbeautifier', pattern="test*.py"))
return unittest.TextTestRunner(verbosity=2).run(suite)

View File

@ -1,15 +1,17 @@
from __future__ import print_function
import sys
import os
import platform
import io
import getopt
import re
import string
import errno
import copy
import glob
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 +39,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 +64,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,25 +113,32 @@ 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
if platform.platform().lower().startswith('windows'):
if sys.version_info.major >= 3:
# for python 3 on windows this prevents conversion
stream = io.TextIOWrapper(sys.stdin.buffer, newline='')
elif platform.architecture()[0] == '32bit':
# for python 2 x86 on windows this prevents conversion
import msvcrt
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
else:
raise 'Pipe to stdin not supported on Windows with Python 2.x 64-bit.'
input_string = stream.read()
# if you pipe an empty string, that is a failure
if input_string == '':
raise MissingInputStreamError()
else:
stream = io.open(file_name, 'rt', newline='')
input_string = ''.join(stream.readlines())
input_string = stream.read()
return beautify(input_string, opts)
@ -135,7 +147,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 +169,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
@ -169,6 +182,8 @@ Output options:
-w, --wrap-line-length Attempt to wrap line when it exceeds this length.
NOTE: Line continues until next wrap point is found.
-n, --end-with-newline End output with newline
--indent-empty-lines Keep indentation on empty lines
--templating List of templating languages (auto,none,django,erb,handlebars,php) ["auto"] auto = none in JavaScript, all in html
--editorconfig Enable setting configuration from EditorConfig
Rarely needed options:
@ -217,31 +232,34 @@ def main():
argv = sys.argv[1:]
try:
opts, args = getopt.getopt(argv, "s:c:e:o:rdEPjabkil:xhtfvXnCO:w:",
['indent-size=', 'indent-char=', 'eol=', 'outfile=', 'replace', 'disable-preserve-newlines',
opts, args = getopt.getopt(argv, "f:s:c:e:o:rdEPjabkil:xhtvXnCO:w:",
['file=', 'indent-size=', 'indent-char=', 'eol=', 'outfile=', 'replace', 'disable-preserve-newlines',
'space-in-paren', 'space-in-empty-paren', 'jslint-happy', 'space-after-anon-function',
'brace-style=', 'keep-array-indentation', 'indent-level=', 'unescape-strings',
'brace-style=', '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',
'keep-array-indentation', 'indent-empty-lines', 'templating'])
except getopt.GetoptError as ex:
print(ex, file=sys.stderr)
return usage(sys.stderr)
js_options = default_options()
file = None
outfile = 'stdout'
filepath_params = []
filepath_params.extend(args)
outfile_param = 'stdout'
replace = False
if len(args) == 1:
file = args[0]
for opt, arg in opts:
if opt in ('--keep-array-indentation', '-k'):
if opt in ('--file', '-f'):
filepath_params.append(arg)
elif opt in ('--keep-array-indentation', '-k'):
js_options.keep_array_indentation = True
if opt in ('--keep-function-indentation', '-f'):
elif opt in ('--keep-function-indentation'):
js_options.keep_function_indentation = True
elif opt in ('--outfile', '-o'):
outfile = arg
outfile_param = arg
elif opt in ('--replace', '-r'):
replace = True
elif opt in ('--indent-size', '-s'):
@ -262,6 +280,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,11 +295,16 @@ 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 ('--indent-empty-lines'):
js_options.indent_empty_lines = True
elif opt in ('--templating'):
js_options.templating = arg.split(',')
elif opt in ('--stdin', '-i'):
file = '-'
# stdin is the default if no files are passed
filepath_params = []
elif opt in ('--editorconfig'):
js_options.editorconfig = True
elif opt in ('--version', '-v'):
@ -287,54 +312,119 @@ def main():
elif opt in ('--help', '--usage', '-h'):
return usage()
if not file:
file = '-'
try:
if outfile == 'stdout' and replace and not file == '-':
outfile = file
filepaths = []
if not filepath_params or (
len(filepath_params) == 1 and filepath_params[0] == '-'):
# default to stdin
filepath_params = []
filepaths.append('-')
# Editorconfig used only on files, not stdin
if getattr(js_options, 'editorconfig'):
editorconfig_filepath = file
if editorconfig_filepath == '-':
if outfile != 'stdout':
editorconfig_filepath = outfile
for filepath_param in filepath_params:
# ignore stdin setting if files are specified
if '-' == filepath_param:
continue
# Check if each literal filepath exists
if os.path.isfile(filepath_param):
filepaths.append(filepath_param)
elif '*' in filepath_param or '?' in filepath_param:
# handle globs
# empty result is okay
if sys.version_info.major == 2 or (
sys.version_info.major == 3 and
sys.version_info.minor <= 4):
if '**' in filepath_param:
raise 'Recursive globs not supported on Python <= 3.4.'
filepaths.extend(glob.glob(filepath_param))
else:
fileType = 'js'
editorconfig_filepath = 'stdin.' + fileType
filepaths.extend(glob.glob(filepath_param, recursive=True))
else:
# not a glob and not a file
raise OSError(errno.ENOENT, os.strerror(errno.ENOENT),
filepath_param)
# debug("EditorConfig is enabled for ", editorconfig_filepath);
js_options = copy.copy(js_options)
set_file_editorconfig_opts(editorconfig_filepath, js_options)
if len(filepaths) > 1:
replace = True
elif filepaths and filepaths[0] == '-':
replace = False
pretty = beautify_file(file, js_options)
# remove duplicates
filepaths = set(filepaths)
if outfile == 'stdout':
# python automatically converts newlines in text to "\r\n" when on windows
# switch to binary to prevent this
if sys.platform == "win32":
import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
for filepath in filepaths:
if not replace:
outfile = outfile_param
else:
outfile = filepath
sys.stdout.write(pretty)
else:
if isFileDifferent(outfile, pretty):
mkdir_p(os.path.dirname(outfile))
# Editorconfig used only on files, not stdin
if getattr(js_options, 'editorconfig'):
editorconfig_filepath = filepath
if editorconfig_filepath == '-':
if outfile != 'stdout':
editorconfig_filepath = outfile
else:
fileType = 'js'
editorconfig_filepath = 'stdin.' + fileType
# debug("EditorConfig is enabled for ", editorconfig_filepath);
js_options = copy.copy(js_options)
set_file_editorconfig_opts(editorconfig_filepath, js_options)
pretty = beautify_file(filepath, js_options)
if outfile == 'stdout':
stream = sys.stdout
# python automatically converts newlines in text to "\r\n" when on windows
# set newline to empty to prevent this
with io.open(outfile, 'wt', newline='') as f:
print('writing ' + outfile, file=sys.stderr)
try:
f.write(pretty)
except TypeError:
# 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")
f.write(six.u(pretty))
# switch to binary to prevent this
if platform.platform().lower().startswith('windows'):
if sys.version_info.major >= 3:
# for python 3 on windows this prevents conversion
stream = io.TextIOWrapper(sys.stdout.buffer, newline='')
elif platform.architecture()[0] == '32bit':
# for python 2 x86 on windows this prevents conversion
import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
else:
raise 'Pipe to stdout not supported on Windows with Python 2.x 64-bit.'
stream.write(pretty)
else:
if isFileDifferent(outfile, pretty):
mkdir_p(os.path.dirname(outfile))
# python automatically converts newlines in text to "\r\n" when on windows
# set newline to empty to prevent this
with io.open(outfile, 'wt', newline='') as f:
print('beautified ' + outfile, file=sys.stdout)
try:
f.write(pretty)
except TypeError:
# 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")
f.write(six.u(pretty))
else:
print('beautified ' + outfile + ' - unchanged', file=sys.stdout)
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)

View File

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

View File

@ -0,0 +1,53 @@
# 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.
import re
class Directives:
def __init__(self, start_block_pattern, end_block_pattern):
self.__directives_block_pattern = re.compile(start_block_pattern + r' beautify( \w+[:]\w+)+ ' + end_block_pattern)
self.__directive_pattern = re.compile(r' (\w+)[:](\w+)')
self.__directives_end_ignore_pattern = re.compile(start_block_pattern + r'\sbeautify\signore:end\s' + end_block_pattern)
def get_directives(self, text):
if not self.__directives_block_pattern.match(text):
return None
directives = {}
directive_match = self.__directive_pattern.search(text)
while directive_match:
directives[directive_match.group(1)] = directive_match.group(2)
directive_match = self.__directive_pattern.search(
text, directive_match.end())
return directives
def readIgnored(self, input):
return input.readUntilAfter(self.__directives_end_ignore_pattern)

View File

@ -22,15 +22,21 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import re
class InputScanner:
def __init__(self, input_string):
self.__six = __import__("six")
if input_string is None:
input_string = ''
self.__input = input_string
self.__input_length = len(self.__input)
self.__position = 0
def restart(self):
self.__position = 0
def back(self):
if self.__position > 0:
self.__position -= 1
@ -62,7 +68,7 @@ class InputScanner:
def testChar(self, pattern, index=0):
# test one character regex match
val = self.peek(index)
return val is not None and pattern.match(val)
return val is not None and bool(pattern.match(val))
def match(self, pattern):
pattern_match = None
@ -72,21 +78,31 @@ class InputScanner:
self.__position = pattern_match.end(0)
return pattern_match
def readWhile(self, pattern):
def read(self, starting_pattern, until_pattern=None, until_after=False):
val = ''
pattern_match = self.match(pattern)
if bool(pattern_match):
val = pattern_match.group(0)
pattern_match = None
if bool(starting_pattern):
pattern_match = self.match(starting_pattern)
if bool(pattern_match):
val = pattern_match.group(0)
if bool(until_pattern) and \
(bool(pattern_match) or not bool(starting_pattern)):
val += self.readUntil(until_pattern, until_after)
return val
def readUntil(self, pattern):
def readUntil(self, pattern, include_match=False):
val = ''
pattern_match = None
match_index = self.__position
if self.hasNext():
pattern_match = pattern.search(self.__input, self.__position)
if bool(pattern_match):
match_index = pattern_match.start(0)
if include_match:
match_index = pattern_match.end(0)
else:
match_index = pattern_match.start(0)
else:
match_index = self.__input_length
@ -96,20 +112,16 @@ class InputScanner:
return val
def readUntilAfter(self, pattern):
val = ''
pattern_match = None
match_index = self.__position
if self.hasNext():
pattern_match = pattern.search(self.__input, self.__position)
if bool(pattern_match):
match_index = pattern_match.end(0)
else:
match_index = self.__input_length
return self.readUntil(pattern, True)
val = self.__input[self.__position:match_index]
self.__position = match_index
return val
def get_regexp(self, pattern, match_from=False):
result = None
# strings are converted to regexp
if isinstance(pattern, self.__six.string_types) and pattern != '':
result = re.compile(pattern)
elif pattern is not None:
result = re.compile(pattern.pattern)
return result
# css beautifier legacy helpers
def peekUntilAfter(self, pattern):

View File

@ -23,21 +23,194 @@
# 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
self.raw_options = _mergeOpts(options, merge_child_field)
# 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)
self.max_preserve_newlines = self._get_number(
'max_preserve_newlines', 32786)
if not self.preserve_newlines:
self.max_preserve_newlines = 0
self.indent_with_tabs = self._get_boolean(
'indent_with_tabs', self.indent_char == '\t')
if self.indent_with_tabs:
self.indent_char = '\t'
# indent_size behavior changed after 1.8.6
# It used to be that indent_size would be
# set to 1 for indent_with_tabs. That is no longer needed and
# actually doesn't make sense - why not use spaces? Further,
# that might produce unexpected behavior - tabs being used
# for single-column alignment. So, when indent_with_tabs is true
# and indent_size is 1, reset indent_size to 4.
if self.indent_size == 1:
self.indent_size = 4
# Backwards compat with 1.3.x
self.wrap_line_length = self._get_number(
'wrap_line_length', self._get_number('max_char'))
self.indent_empty_lines = self._get_boolean('indent_empty_lines')
# valid templating languages ['django', 'erb', 'handlebars', 'php']
# For now, 'auto' = all off for javascript, all on for html (and inline javascript).
# other values ignored
self.templating = self._get_selection_list('templating',
['auto', 'none', 'django', 'erb', 'handlebars', 'php'], ['auto'])
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}}
# mergeOpts(obj, 'b')
#
# Returns: {a: 2, b: {a: 2}}
# Returns: {a: 2}
def mergeOpts(options, childFieldName):
def _mergeOpts(options, childFieldName):
if options is None:
options = {}
if isinstance(options, tuple):
options = dict(options)
options = _normalizeOpts(options)
finalOpts = copy.copy(options)
if isinstance(options, dict):
local = finalOpts.get(childFieldName, None)
if local:
del(finalOpts[childFieldName])
for key in local:
finalOpts[key] = local[key]
finalOpts = namedtuple("CustomOptions", finalOpts.keys())(
*finalOpts.values())
local = getattr(finalOpts, childFieldName, None)
if local:
delattr(finalOpts, childFieldName)
for key in local:
setattr(finalOpts, key, local[key])
if isinstance(options, Options):
local = getattr(finalOpts, childFieldName, None)
if local:
delattr(finalOpts, childFieldName)
for key in local:
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

@ -23,30 +23,82 @@
# SOFTWARE.
import re
import math
# Using object instead of string to allow for later expansion of info
# about each line
__all__ = ["Output"]
class OutputLine:
def __init__(self, parent):
self.__parent = parent
self.__character_count = 0
self.__indent_count = -1
self.__alignment_count = 0
self.__wrap_point_index = 0
self.__wrap_point_character_count = 0
self.__wrap_point_indent_count = -1
self.__wrap_point_alignment_count = 0
self.__items = []
self.__empty = True
def get_character_count(self):
return self.__character_count
def clone_empty(self):
line = OutputLine(self.__parent)
line.set_indent(self.__indent_count, self.__alignment_count)
return line
def item(self, index):
return self.__items[index]
def is_empty(self):
return self.__empty
return len(self.__items) == 0
def set_indent(self, level):
self.__character_count = self.__parent.baseIndentLength + \
level * self.__parent.indent_length
self.__indent_count = level
def set_indent(self, indent=0, alignment=0):
if self.is_empty():
self.__indent_count = indent
self.__alignment_count = alignment
self.__character_count = self.__parent.get_indent_size(
self.__indent_count, self.__alignment_count)
def _set_wrap_point(self):
if self.__parent.wrap_line_length:
self.__wrap_point_index = len(self.__items)
self.__wrap_point_character_count = self.__character_count
self.__wrap_point_indent_count = \
self.__parent.next_line.__indent_count
self.__wrap_point_alignment_count = \
self.__parent.next_line.__alignment_count
def _should_wrap(self):
return self.__wrap_point_index and \
self.__character_count > \
self.__parent.wrap_line_length and \
self.__wrap_point_character_count > \
self.__parent.next_line.__character_count
def _allow_wrap(self):
if self._should_wrap():
self.__parent.add_new_line()
next = self.__parent.current_line
next.set_indent(self.__wrap_point_indent_count,
self.__wrap_point_alignment_count)
next.__items = self.__items[self.__wrap_point_index:]
self.__items = self.__items[:self.__wrap_point_index]
next.__character_count += self.__character_count - \
self.__wrap_point_character_count
self.__character_count = self.__wrap_point_character_count
if next.__items[0] == " ":
next.__items.pop(0)
next.__character_count -= 1
return True
return False
def last(self):
if not self.is_empty():
@ -56,123 +108,222 @@ class OutputLine:
def push(self, item):
self.__items.append(item)
self.__character_count += len(item)
self.__empty = False
last_newline_index = item.rfind('\n')
if last_newline_index != -1:
self.__character_count = len(item) - last_newline_index
else:
self.__character_count += len(item)
def pop(self):
item = None
if not self.is_empty():
item = self.__items.pop()
self.__character_count -= len(item)
self.__empty = len(self.__items) == 0
return item
def remove_indent(self):
def _remove_indent(self):
if self.__indent_count > 0:
self.__indent_count -= 1
self.__character_count -= self.__parent.indent_length
self.__character_count -= self.__parent.indent_size
def _remove_wrap_indent(self):
if self.__wrap_point_indent_count > 0:
self.__wrap_point_indent_count -= 1
def trim(self):
while self.last() == ' ':
self.__items.pop()
self.__character_count -= 1
self.__empty = len(self.__items) == 0
def toString(self):
result = ''
if not self.is_empty():
if self.__indent_count >= 0:
result = self.__parent.indent_cache[self.__indent_count]
if self.is_empty():
if self.__parent.indent_empty_lines:
result = self.__parent.get_indent_string(self.__indent_count)
else:
result = self.__parent.get_indent_string(
self.__indent_count, self.__alignment_count)
result += ''.join(self.__items)
return result
class Output:
def __init__(self, indent_string, baseIndentString=''):
class IndentStringCache:
def __init__(self, options, base_string):
self.__cache = ['']
self.__indent_size = options.indent_size
self.__indent_string = options.indent_char
if not options.indent_with_tabs:
self.__indent_string = options.indent_char * options.indent_size
self.indent_string = indent_string
self.baseIndentString = baseIndentString
self.indent_cache = [baseIndentString]
self.baseIndentLength = len(baseIndentString)
self.indent_length = len(indent_string)
# Set to null to continue support of auto detection of base indent
base_string = base_string or ''
if options.indent_level > 0:
base_string = options.indent_level * self.__indent_string
self.__base_string = base_string
self.__base_string_length = len(base_string)
def get_indent_size(self, indent, column=0):
result = self.__base_string_length
if indent < 0:
result = 0
result += indent * self.__indent_size
result += column
return result
def get_indent_string(self, indent_level, column=0):
result = self.__base_string
if indent_level < 0:
indent_level = 0
result = ''
column += indent_level * self.__indent_size
self.__ensure_cache(column)
result += self.__cache[column]
return result
def __ensure_cache(self, column):
while column >= len(self.__cache):
self.__add_column()
def __add_column(self):
column = len(self.__cache)
indent = 0
result = ''
if self.__indent_size and column >= self.__indent_size:
indent = int(math.floor(column / self.__indent_size))
column -= indent * self.__indent_size
result = indent * self.__indent_string
if column:
result += column * ' '
self.__cache.append(result)
class Output:
def __init__(self, options, baseIndentString=''):
self.__indent_cache = IndentStringCache(options, baseIndentString)
self.raw = False
self.lines = []
self._end_with_newline = options.end_with_newline
self.indent_size = options.indent_size
self.wrap_line_length = options.wrap_line_length
self.indent_empty_lines = options.indent_empty_lines
self.__lines = []
self.previous_line = None
self.current_line = None
self.next_line = OutputLine(self)
self.space_before_token = False
self.add_outputline()
self.non_breaking_space = False
self.previous_token_wrapped = False
# initialize
self.__add_outputline()
def add_outputline(self):
def __add_outputline(self):
self.previous_line = self.current_line
self.current_line = OutputLine(self)
self.lines.append(self.current_line)
self.current_line = self.next_line.clone_empty()
self.__lines.append(self.current_line)
def get_line_number(self):
return len(self.lines)
return len(self.__lines)
def get_indent_string(self, indent, column=0):
return self.__indent_cache.get_indent_string(indent, column)
def get_indent_size(self, indent, column=0):
return self.__indent_cache.get_indent_size(indent, column)
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)
sweet_code = re.sub('[\r\n\t ]+$', '', sweet_code)
def get_code(self, eol):
self.trim(True)
if end_with_newline:
sweet_code += '\n'
# handle some edge cases where the last tokens
# has text that ends with newline(s)
last_item = self.current_line.pop()
if last_item:
if last_item[-1] == '\n':
last_item = re.sub(r'[\n]+$', '', last_item)
self.current_line.push(last_item)
if self._end_with_newline:
self.__add_outputline()
sweet_code = "\n".join(line.toString() for line in self.__lines)
if not eol == '\n':
sweet_code = sweet_code.replace('\n', eol)
return sweet_code
def set_indent(self, level):
# 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)
def set_wrap_point(self):
self.current_line._set_wrap_point()
self.current_line.set_indent(level)
def set_indent(self, indent=0, alignment=0):
# Next line stores alignment values
self.next_line.set_indent(indent, alignment)
# Never indent your first output indent at the start of the file
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.set_indent(-1)
self.current_line.push(token.whitespace_before)
self.current_line.push(token.text)
self.space_before_token = False
self.non_breaking_space = False
self.previous_token_wrapped = False
def add_token(self, printable_token):
self.add_space_before_token()
self.__add_space_before_token()
self.current_line.push(printable_token)
self.space_before_token = False
self.non_breaking_space = False
self.previous_token_wrapped = self.current_line._allow_wrap()
def add_space_before_token(self):
def __add_space_before_token(self):
if self.space_before_token and not self.just_added_newline():
if not self.non_breaking_space:
self.set_wrap_point()
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
self.current_line._remove_wrap_indent()
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
@ -180,11 +331,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

@ -0,0 +1,82 @@
# 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.
__all__ = ["Pattern"]
class Pattern:
def __init__(self, input_scanner, parent=None):
self._input = input_scanner
self._starting_pattern = None
self._match_pattern = None
self._until_pattern = None
self._until_after = False
if parent is not None:
self._starting_pattern = self._input.get_regexp(parent._starting_pattern)
self._match_pattern = self._input.get_regexp(parent._match_pattern)
self._until_pattern = self._input.get_regexp(parent._until_pattern)
self._until_after = parent._until_after
def read(self):
result = self._input.read(self._starting_pattern)
if (self._starting_pattern is None) or result:
result += self._input.read(self._match_pattern,
self._until_pattern, self._until_after)
return result
def read_match(self):
return self._input.match(self._match_pattern)
def until_after(self, pattern):
result = self._create()
result._until_after = True
result._until_pattern = self._input.get_regexp(pattern)
result._update()
return result
def until(self, pattern):
result = self._create()
result._until_after = False
result._until_pattern = self._input.get_regexp(pattern)
result._update()
return result
def starting_with(self, pattern):
result = self._create()
result._starting_pattern = self._input.get_regexp(pattern)
result._update()
return result
def matching(self, pattern):
result = self._create()
result._match_pattern = self._input.get_regexp(pattern)
result._update()
return result
def _create(self):
return Pattern(self._input, self)
def _update(self):
pass

View File

@ -0,0 +1,174 @@
# 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.
import copy
from ..core.pattern import Pattern
__all__ = ["TemplatablePattern"]
class TemplateNames:
def __init__(self):
self.django = False
self.erb = False
self.handlebars = False
self.php = False
class TemplatePatterns:
def __init__(self, input_scanner):
pattern = Pattern(input_scanner)
self.handlebars_comment = pattern.starting_with(r'{{!--').until_after(r'--}}')
self.handlebars = pattern.starting_with(r'{{').until_after(r'}}')
self.php = pattern.starting_with(r'<\?(?:[=]|php)').until_after(r'\?>')
self.erb = pattern.starting_with(r'<%[^%]').until_after(r'[^%]%>')
# django coflicts with handlebars a bit.
self.django = pattern.starting_with(r'{%').until_after(r'%}')
self.django_value = pattern.starting_with(r'{{').until_after(r'}}')
self.django_comment = pattern.starting_with(r'{#').until_after(r'#}')
class TemplatablePattern(Pattern):
def __init__(self, input_scanner, parent=None):
Pattern.__init__(self, input_scanner, parent)
self.__template_pattern = None
self._disabled = TemplateNames()
self._excluded = TemplateNames()
if parent is not None:
self.__template_pattern = \
self._input.get_regexp(parent.__template_pattern)
self._disabled = copy.copy(parent._disabled)
self._excluded = copy.copy(parent._excluded)
self.__patterns = TemplatePatterns(input_scanner)
def _create(self):
return TemplatablePattern(self._input, self)
def _update(self):
self.__set_templated_pattern()
def read_options(self, options):
result = self._create()
for language in ['django', 'erb', 'handlebars', 'php']:
setattr(result._disabled, language,
not (language in options.templating))
result._update()
return result
def disable(self, language):
result = self._create()
setattr(result._disabled, language, True)
result._update()
return result
def exclude(self, language):
result = self._create()
setattr(result._excluded, language, True)
result._update()
return result
def read(self):
result = ''
if bool(self._match_pattern):
result = self._input.read(self._starting_pattern)
else:
result = self._input.read(self._starting_pattern,
self.__template_pattern)
next = self._read_template()
while (bool(next)):
if self._match_pattern is not None:
next += self._input.read(self._match_pattern)
else:
next += self._input.readUntil(self.__template_pattern)
result += next
next = self._read_template()
if self._until_after:
result += self._input.readUntilAfter(self._until_after)
return result
def __set_templated_pattern(self):
items = list()
if not self._disabled.php:
items.append(self.__patterns.php._starting_pattern.pattern)
if not self._disabled.handlebars:
items.append(self.__patterns.handlebars._starting_pattern.pattern)
if not self._disabled.erb:
items.append(self.__patterns.erb._starting_pattern.pattern)
if not self._disabled.django:
items.append(self.__patterns.django._starting_pattern.pattern)
items.append(self.__patterns.django_value._starting_pattern.pattern)
items.append(self.__patterns.django_comment._starting_pattern.pattern)
if self._until_pattern:
items.append(self._until_pattern.pattern)
self.__template_pattern = self._input.get_regexp(
r'(?:' + '|'.join(items) + ')')
def _read_template(self):
resulting_string = ''
c = self._input.peek()
if c == '<':
peek1 = self._input.peek(1)
if not self._disabled.php and \
not self._excluded.php and \
peek1 == '?':
resulting_string = resulting_string or \
self.__patterns.php.read()
if not self._disabled.erb and \
not self._excluded.erb and \
peek1 == '%':
resulting_string = resulting_string or \
self.__patterns.erb.read()
elif c == '{':
if not self._disabled.handlebars and \
not self._excluded.handlebars:
resulting_string = resulting_string or \
self.__patterns.handlebars_comment.read()
resulting_string = resulting_string or \
self.__patterns.handlebars.read()
if not self._disabled.django:
# django coflicts with handlebars a bit.
if not self._excluded.django and \
not self._excluded.handlebars:
resulting_string = resulting_string or \
self.__patterns.django_value.read()
if not self._excluded.django:
resulting_string = resulting_string or \
self.__patterns.django_comment.read()
resulting_string = resulting_string or \
self.__patterns.django.read()
return resulting_string

View File

@ -29,15 +29,15 @@ class Token:
type,
text,
newlines=0,
whitespace_before='',
mode=None,
parent=None):
whitespace_before=''):
self.type = type
self.text = text
self.comments_before = []
self.comments_before = None
self.newlines = newlines
self.wanted_newline = newlines > 0
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

@ -0,0 +1,135 @@
# 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.
import re
from ..core.inputscanner import InputScanner
from ..core.token import Token
from ..core.tokenstream import TokenStream
from ..core.pattern import Pattern
from ..core.whitespacepattern import WhitespacePattern
__all__ = ["TOKEN", "Tokenizer", "TokenizerPatterns", "TokenTypes"]
class TokenTypes:
START = 'TK_START'
RAW = 'TK_RAW'
EOF = 'TK_EOF'
def __init__(self):
pass
TOKEN = TokenTypes()
class TokenizerPatterns:
def __init__(self, input_scanner):
self.whitespace = WhitespacePattern(input_scanner)
class Tokenizer:
def __init__(self, input_string, options):
self._input = InputScanner(input_string)
self._options = options
self.__tokens = None
self._patterns = TokenizerPatterns(self._input)
def tokenize(self):
self._input.restart()
self.__tokens = TokenStream()
current = None
previous = Token(TOKEN.START,'')
open_token = None
open_stack = []
comments = TokenStream()
while previous.type != TOKEN.EOF:
current = self.__get_next_token_with_comments(previous, open_token)
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(previous, open_token)
if not comments.isEmpty():
current.comments_before = comments
comments = TokenStream()
current.parent = open_token
current.previous = previous
previous.next = current
return current
def _is_first_token(self):
return self.__tokens.isEmpty()
def _reset(self):
pass
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)
else:
return self._create_token(TOKEN.EOF, '')
def _is_comment(self, current_token):
return False
def _is_opening(self, current_token):
return False
def _is_closing(self, current_token, open_token):
return False
def _create_token(self, token_type, text):
token = Token(token_type, text,
self._patterns.whitespace.newline_count,
self._patterns.whitespace.whitespace_before_token)
return token
def _readWhitespace(self):
return self._patterns.whitespace.read()

View File

@ -0,0 +1,74 @@
# 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.
import re
from ..core.inputscanner import InputScanner
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
def restart(self):
self.__position = 0
def isEmpty(self):
return self.__tokens_length == 0
def hasNext(self):
return self.__position < self.__tokens_length
def next(self):
if self.hasNext():
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]
return val
def add(self, token):
if self.__parent_token:
token.parent = self.__parent_token
self.__tokens.append(token)
self.__tokens_length += 1
def __iter__(self):
self.restart()
return self
def __next__(self):
return self.next()

View File

@ -0,0 +1,78 @@
# 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.
import re
from ..core.pattern import Pattern
__all__ = ["WhitespacePattern"]
class WhitespacePattern(Pattern):
def __init__(self, input_scanner, parent=None):
Pattern.__init__(self, input_scanner, parent)
if parent is not None:
self._newline_regexp = \
self._input.get_regexp(parent._newline_regexp)
else:
self.__set_whitespace_patterns('', '')
self.newline_count = 0
self.whitespace_before_token = ''
def __set_whitespace_patterns(self, whitespace_chars, newline_chars):
whitespace_chars += '\\t '
newline_chars += '\\n\\r'
self._match_pattern = self._input.get_regexp(
'[' + whitespace_chars + newline_chars + ']+')
self._newline_regexp = self._input.get_regexp(
'\\r\\n|[' + newline_chars + ']')
def read(self):
self.newline_count = 0
self.whitespace_before_token = ''
resulting_string = self._input.read(self._match_pattern)
if resulting_string == ' ':
self.whitespace_before_token = ' '
elif bool(resulting_string):
lines = self._newline_regexp.split(resulting_string)
self.newline_count = len(lines) - 1
self.whitespace_before_token = lines[-1]
return resulting_string
def matching(self, whitespace_chars, newline_chars):
result = self._create()
result.__set_whitespace_patterns(whitespace_chars, newline_chars)
result._update()
return result
def _create(self):
return WhitespacePattern(self._input, self)

View File

@ -18,44 +18,51 @@ 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.
_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")
# IMPORTANT: These strings must be run through six to handle \u chars
_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(r"(?:\\u[0-9a-fA-F]{4}|[") + \
_baseASCIIidentifierStartChars + \
_nonASCIIidentifierStartChars + \
six.u("])")
_identifierChars = six.u(r"(?:\\u[0-9a-fA-F]{4}|[") + \
_baseASCIIidentifierChars + \
_nonASCIIidentifierStartChars + \
_nonASCIIidentifierChars + \
six.u("])*")
identifier = re.compile(
"[" +
_baseASCIIidentifierStartChars +
_nonASCIIidentifierStartChars +
"][" +
_baseASCIIidentifierChars +
_nonASCIIidentifierStartChars +
_nonASCIIidentifierChars +
"]*")
identifier = re.compile(_identifierStart + _identifierChars)
identifierStart = re.compile(_identifierStart)
identifierMatch = re.compile(six.u(r"(?:\\u[0-9a-fA-F]{4}|[") + \
_baseASCIIidentifierChars + \
_nonASCIIidentifierStartChars + \
_nonASCIIidentifierChars + \
six.u("])+"))
_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
newline = re.compile(six.u(r"[\n\r\u2028\u2029]"))
# Matches a whole line break (where CRLF is considered a single
@ -63,40 +70,6 @@ newline = re.compile(six.u(r"[\n\r\u2028\u2029]"))
# 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
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

@ -24,10 +24,18 @@
import re
from ..core.inputscanner import InputScanner
from ..core.token import Token
from ..core.tokenizer import TokenTypes as BaseTokenTypes
from ..core.tokenizer import Tokenizer as BaseTokenizer
from ..core.tokenizer import TokenizerPatterns as BaseTokenizerPatterns
from ..core.directives import Directives
from ..core.pattern import Pattern
from ..core.templatablepattern import TemplatablePattern
class TokenTypes:
__all__ = ["TOKEN", "Tokenizer", "TokenTypes"]
class TokenTypes(BaseTokenTypes):
START_EXPR = 'TK_START_EXPR'
END_EXPR = 'TK_END_EXPR'
START_BLOCK = 'TK_START_BLOCK'
@ -43,7 +51,6 @@ class TokenTypes:
COMMENT = 'TK_COMMENT'
DOT = 'TK_DOT'
UNKNOWN = 'TK_UNKNOWN'
EOF = 'TK_EOF'
def __init__(self):
pass
@ -51,316 +58,283 @@ class TokenTypes:
TOKEN = TokenTypes()
dot_pattern = re.compile(r'[^\d\.]')
class Tokenizer:
number_pattern = re.compile(
r'0[xX][0123456789abcdefABCDEF]*|0[oO][01234567]*|0[bB][01]*|\d+n|(?:\.\d+|\d+\.?\d*)(?:[eE][+-]?\d+)?')
digit = re.compile(r'[0-9]')
number_pattern = re.compile(
r'0[xX][0123456789abcdefABCDEF]*|0[oO][01234567]*|0[bB][01]*|\d+n|(?:\.\d+|\d+\.?\d*)(?:[eE][+-]?\d+)?')
digit = re.compile(r'[0-9]')
startXmlRegExp = re.compile(
r'<()([-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*>')
xmlRegExp = re.compile(
r'[\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*>')
positionable_operators = frozenset(
(">>> === !== " +
"<< && >= ** != == <= >> || " +
"< / - + > : & % ? ^ | *").split(' '))
positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(
' ')
punct = (positionable_operators +
# non-positionable operators - these do not follow operator
# position settings
'! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~ ...'.split(' '))
punct = (">>>= " +
"... >>= <<= === >>> !== **= " +
"=> ^= :: /= << <= == && -= >= >> != -- += ** || ++ %= &= *= |= " +
"= ! ? > < : / ^ - + * & % ~ |")
# Words which always should start on a new line
line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(
',')
reserved_words = line_starters + ['do',
'in',
'of',
'else',
'get',
'set',
'new',
'catch',
'finally',
'typeof',
'yield',
'async',
'await',
'from',
'as']
punct = re.compile(r'([-[\]{}()*+?.,\\^$|#])').sub(r'\\\1', punct)
punct = punct.replace(' ', '|')
def __init__(self, input_string, opts, indent_string):
import jsbeautifier.core.acorn as acorn
punct_pattern = re.compile(punct)
# Words which always should start on a new line
line_starters = frozenset(
('continue,try,throw,return,var,let,const,if,switch,case,default,for,' +
'while,break,function,import,export').split(','))
reserved_words = line_starters | frozenset(['do',
'in',
'of',
'else',
'get',
'set',
'new',
'catch',
'finally',
'typeof',
'yield',
'async',
'await',
'from',
'as'])
reserved_word_pattern = re.compile(r'^(?:' + '|'.join(reserved_words) + r')$')
directives_core = Directives(r'/\*', r'\*/')
xmlRegExp = re.compile(
r'[\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*>')
class TokenizerPatterns(BaseTokenizerPatterns):
def __init__(self, input_scanner, acorn, options):
BaseTokenizerPatterns.__init__(self, input_scanner)
# 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")
# IMPORTANT: This string must be run through six to handle \u chars
self.whitespace = self.whitespace.matching(
six.u(r'\u00A0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff'),
six.u(r'\u2028\u2029'))
pattern = Pattern(input_scanner)
templatable = TemplatablePattern(input_scanner) \
.read_options(options)
self.identifier = templatable.starting_with(acorn.identifier \
).matching(acorn.identifierMatch)
self.number = pattern.matching(number_pattern)
self.punct = pattern.matching(punct_pattern)
self.comment = pattern.starting_with(r'//').until(
six.u(r'[\n\r\u2028\u2029]'))
self.block_comment = pattern.starting_with(r'/\*').until_after(r'\*/')
self.html_comment_start = pattern.matching(r'<!--')
self.html_comment_end = pattern.matching(r'-->')
self.include = pattern.starting_with(r'#include' \
).until_after(acorn.lineBreak)
self.shebang = pattern.starting_with(r'#!' \
).until_after(acorn.lineBreak)
self.xml = pattern.matching(xmlRegExp)
self.single_quote = templatable.until(six.u(r"['\\\n\r\u2028\u2029]"))
self.double_quote = templatable.until(six.u(r'["\\\n\r\u2028\u2029]'))
self.template_text = templatable.until(r'[`\\$]')
self.template_expression = templatable.until(r'[`}\\]')
class Tokenizer(BaseTokenizer):
positionable_operators = positionable_operators
line_starters = line_starters
def __init__(self, input_string, opts):
BaseTokenizer.__init__(self, input_string, opts)
import jsbeautifier.javascript.acorn as acorn
self.acorn = acorn
self.input = InputScanner(input_string)
self.opts = opts
self.indent_string = indent_string
# /* ... */ comment ends with nearest */ or end of file
self.block_comment_pattern = re.compile(r'([\s\S]*?)((?:\*\/)|$)')
# 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.directives_block_pattern = re.compile(
r'\/\* beautify( \w+[:]\w+)+ \*\/')
self.directive_pattern = re.compile(r' (\w+)[:](\w+)')
self.directives_end_ignore_pattern = re.compile(
r'([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)')
self.template_pattern = re.compile(
r'((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)')
self.whitespacePattern = re.compile(
self.acorn.six.u(r'[\n\r\u2028\u2029\t ]+'))
self.newlinePattern = re.compile(
self.acorn.six.u(r'([\t ]*)(\r\n|[\n\r\u2028\u2029])?'))
def tokenize(self):
self.in_html_comment = False
self.tokens = []
next = None
last = None
open_token = None
open_stack = []
comments = []
while not (last is not None and last.type == TOKEN.EOF):
token_values = self.__tokenize_next()
next = Token(
token_values[1],
token_values[0],
self.n_newlines,
self.whitespace_before_token)
while next.type == TOKEN.COMMENT or next.type == TOKEN.BLOCK_COMMENT or next.type == TOKEN.UNKNOWN:
if next.type == TOKEN.BLOCK_COMMENT:
next.directives = token_values[2]
comments.append(next)
token_values = self.__tokenize_next()
next = Token(
token_values[1],
token_values[0],
self.n_newlines,
self.whitespace_before_token)
if len(comments) > 0:
next.comments_before = comments
comments = []
if next.type == TOKEN.START_BLOCK or next.type == TOKEN.START_EXPR:
next.parent = last
open_stack.append(open_token)
open_token = next
elif (next.type == TOKEN.END_BLOCK or next.type == TOKEN.END_EXPR) and \
(open_token is not None and (
(next.text == ']' and open_token.text == '[') or
(next.text == ')' and open_token.text == '(') or
(next.text == '}' and open_token.text == '{'))):
next.parent = open_token.parent
next.opened = open_token
open_token = open_stack.pop()
self.tokens.append(next)
last = next
return self.tokens
def get_directives(self, text):
if not self.directives_block_pattern.match(text):
return None
directives = {}
directive_match = self.directive_pattern.search(text)
while directive_match:
directives[directive_match.group(1)] = directive_match.group(2)
directive_match = self.directive_pattern.search(
text, directive_match.end())
return directives
def __tokenize_next(self):
self.n_newlines = 0
self.whitespace_before_token = ''
if len(self.tokens) > 0:
last_token = self.tokens[-1]
else:
# For the sake of tokenizing we can pretend that there was on open
# brace to start
last_token = Token(TOKEN.START_BLOCK, '{')
resulting_string = self.input.readWhile(self.whitespacePattern)
if resulting_string != '':
if resulting_string == ' ':
self.whitespace_before_token = resulting_string
else:
for nextMatch in self.newlinePattern.findall(resulting_string):
if nextMatch[1] != '':
self.n_newlines += 1
else:
self.whitespace_before_token = nextMatch[0]
break
resulting_string = self.input.readWhile(self.acorn.identifier)
if resulting_string != '':
if not (last_token.type == TOKEN.DOT or (
last_token.type == TOKEN.RESERVED and last_token.text in [
'set',
'get'])) and resulting_string in self.reserved_words:
if resulting_string in ['in', 'of']: # in and of are operators, need to hack
return resulting_string, TOKEN.OPERATOR
return resulting_string, TOKEN.RESERVED
return resulting_string, TOKEN.WORD
resulting_string = self.input.readWhile(self.number_pattern)
if resulting_string != '':
return resulting_string, TOKEN.WORD
c = self.input.next()
if c is None:
return '', TOKEN.EOF
if c in '([':
return c, TOKEN.START_EXPR
if c in ')]':
return c, TOKEN.END_EXPR
if c == '{':
return c, TOKEN.START_BLOCK
if c == '}':
return c, TOKEN.END_BLOCK
if c == ';':
return c, TOKEN.SEMICOLON
if c == '/':
comment = ''
if self.input.peek() == '*': # peek /* .. */ comment
self.input.next()
comment_match = self.input.match(self.block_comment_pattern)
comment = '/*' + comment_match.group(0)
directives = self.get_directives(comment)
if directives and directives.get('ignore') == 'start':
comment_match = self.input.match(
self.directives_end_ignore_pattern)
comment += comment_match.group(0)
comment = re.sub(self.acorn.allLineBreaks, '\n', comment)
return comment, TOKEN.BLOCK_COMMENT, directives
if self.input.peek() == '/': # peek // comment
self.input.next()
comment_match = self.input.match(self.comment_pattern)
comment = '//' + comment_match.group(0)
return comment, TOKEN.COMMENT
def allowRegExOrXML(self):
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.END_BLOCK, TOKEN.OPERATOR,
TOKEN.EQUALS, TOKEN.EOF, TOKEN.SEMICOLON, TOKEN.COMMA])
self.has_char_escapes = False
isString = (c == '`' or c == "'" or c == '"')
isRegExp = (c == '/' and allowRegExOrXML(self))
isXML = (self.opts.e4x and c == "<" and self.input.test(
self.startXmlRegExp, -1) and allowRegExOrXML(self))
self._patterns = TokenizerPatterns(self._input, self.acorn, opts)
sep = c
esc = False
resulting_string = c
in_char_class = False
if isString:
# handle string
def parse_string(
self,
resulting_string,
delimiter,
allow_unescaped_newlines=False,
start_sub=None):
esc = False
while self.input.hasNext():
current_char = self.input.peek()
if not (esc or (current_char != delimiter and (
allow_unescaped_newlines or not bool(
self.acorn.newline.match(current_char))))):
break
def _reset(self):
self.in_html_comment = False
# Handle \r\n linebreaks after escapes or in template
# strings
if (esc or allow_unescaped_newlines) and bool(
self.acorn.newline.match(current_char)):
if current_char == '\r' and self.input.peek(1) == '\n':
self.input.next()
current_char = self.input.peek()
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
resulting_string += '\n'
else:
resulting_string += current_char
if esc:
if current_char == 'x' or current_char == 'u':
self.has_char_escapes = True
def _is_opening(self, current_token):
return current_token.type == TOKEN.START_BLOCK or current_token.type == TOKEN.START_EXPR
esc = False
else:
esc = current_char == '\\'
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 == '{')))
self.input.next()
def _get_next_token(self, previous_token, open_token):
token = None
self._readWhitespace()
if start_sub and resulting_string.endswith(start_sub):
if delimiter == '`':
resulting_string = parse_string(
self, resulting_string, '}', allow_unescaped_newlines, '`')
else:
resulting_string = parse_string(
self, resulting_string, '`', allow_unescaped_newlines, '${')
c = self._input.peek()
if c is None:
token = self._create_token(TOKEN.EOF, '')
if self.input.hasNext():
resulting_string += self.input.next()
token = token or self._read_string(c)
token = token or self._read_word(previous_token)
token = token or self._read_singles(c)
token = token or self._read_comment(c)
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())
return resulting_string
return token
if sep == '`':
resulting_string = parse_string(
self, resulting_string, '`', True, '${')
def _read_singles(self, c):
token = None
if c == '(' or c == '[':
token = self._create_token(TOKEN.START_EXPR, c)
elif c == ')' or c == ']':
token = self._create_token(TOKEN.END_EXPR, c)
elif c == '{':
token = self._create_token(TOKEN.START_BLOCK, c)
elif c == '}':
token = self._create_token(TOKEN.END_BLOCK, c)
elif c == ';':
token = self._create_token(TOKEN.SEMICOLON, c)
elif c == '.' and self._input.peek(1) is not None and \
bool(dot_pattern.match(self._input.peek(1))):
token = self._create_token(TOKEN.DOT, c)
elif c == ',':
token = self._create_token(TOKEN.COMMA, c)
if token is not None:
self._input.next()
return token
def _read_word(self, previous_token):
resulting_string = self._patterns.identifier.read()
if bool(resulting_string):
resulting_string = re.sub(self.acorn.allLineBreaks, '\n', resulting_string)
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.RESERVED, resulting_string)
return self._create_token(TOKEN.WORD, resulting_string)
resulting_string = self._patterns.number.read()
if resulting_string != '':
return self._create_token(TOKEN.WORD, resulting_string)
def _read_comment(self, c):
token = None
if c == '/':
comment = ''
if self._input.peek(1) == '*': # peek /* .. */ comment
comment = self._patterns.block_comment.read()
directives = directives_core.get_directives(comment)
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.directives = directives
elif self._input.peek(1) == '/': # peek // comment
comment = self._patterns.comment.read()
token = self._create_token(TOKEN.COMMENT, comment)
return token
def _read_string(self, c):
if c == '`' or c == "'" or c == '"':
resulting_string = self._input.next()
self.has_char_escapes = False
if c == '`':
resulting_string += self.parse_string('`', True, '${')
else:
resulting_string = parse_string(self, resulting_string, sep)
elif isRegExp:
resulting_string += self.parse_string(c)
if self.has_char_escapes and self._options.unescape_strings:
resulting_string = self.unescape_string(resulting_string)
if self._input.peek() == c:
resulting_string += self._input.next()
resulting_string = re.sub(
self.acorn.allLineBreaks, '\n', resulting_string)
return self._create_token(TOKEN.STRING, resulting_string)
return None
def _read_regexp(self, c, previous_token):
if c == '/' and self.allowRegExOrXML(previous_token):
# handle regexp
resulting_string = self._input.next()
esc = False
in_char_class = False
while self.input.hasNext() and \
(esc or in_char_class or self.input.peek() != sep) and \
not self.input.testChar(self.acorn.newline):
resulting_string += self.input.peek()
while self._input.hasNext() and \
(esc or in_char_class or self._input.peek() != c) and \
not self._input.testChar(self.acorn.newline):
resulting_string += self._input.peek()
if not esc:
esc = self.input.peek() == '\\'
if self.input.peek() == '[':
esc = self._input.peek() == '\\'
if self._input.peek() == '[':
in_char_class = True
elif self.input.peek() == ']':
elif self._input.peek() == ']':
in_char_class = False
else:
esc = False
self.input.next()
self._input.next()
elif isXML:
if self._input.peek() == c:
resulting_string += self._input.next()
if c == '/':
# regexps may have modifiers /regexp/MOD, so fetch those too
# Only [gim] are valid, but if the user puts in garbage, do
# what we can to take it.
resulting_string += self._input.read(
self.acorn.identifier)
return self._create_token(TOKEN.STRING, resulting_string)
return None
def _read_xml(self, c, previous_token):
if self._options.e4x and c == "<" and self.allowRegExOrXML(previous_token):
# handle e4x xml literals
self.input.back()
xmlStr = ""
match = self.input.match(self.xmlRegExp)
if match:
match = self._patterns.xml.read_match()
if match and not match.group(1):
rootTag = match.group(2)
rootTag = re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', rootTag))
isCurlyRoot = rootTag.startswith('{')
@ -381,109 +355,149 @@ class Tokenizer:
if depth <= 0:
break
match = self.input.match(self.xmlRegExp)
match = self._patterns.xml.read_match()
# if we didn't close correctly, keep unformatted.
if not match:
xmlStr += self.input.match(re.compile(r'[\s\S]*')).group(0)
xmlStr += self._input.match(re.compile(r'[\s\S]*')).group(0)
xmlStr = re.sub(self.acorn.allLineBreaks, '\n', xmlStr)
return xmlStr, TOKEN.STRING
return self._create_token(TOKEN.STRING, xmlStr)
if isRegExp or isString:
if self.has_char_escapes and self.opts.unescape_strings:
resulting_string = self.unescape_string(resulting_string)
return None
if self.input.peek() == sep:
resulting_string += self.input.next()
if sep == '/':
# regexps may have modifiers /regexp/MOD, so fetch those too
# Only [gim] are valid, but if the user puts in garbage, do
# what we can to take it.
resulting_string += self.input.readWhile(
self.acorn.identifier)
resulting_string = re.sub(
self.acorn.allLineBreaks, '\n', resulting_string)
return resulting_string, TOKEN.STRING
def _read_non_javascript(self, c):
resulting_string = ''
if c == '#':
# she-bang
if len(self.tokens) == 0 and self.input.peek() == '!':
resulting_string = c
while self.input.hasNext() and c != '\n':
c = self.input.next()
resulting_string += c
return resulting_string.strip() + '\n', TOKEN.UNKNOWN
if self._is_first_token():
resulting_string = self._patterns.shebang.read()
if resulting_string:
return self._create_token(TOKEN.UNKNOWN, resulting_string.strip() + '\n')
# handles extendscript #includes
resulting_string = self._patterns.include.read()
if resulting_string:
return self._create_token(TOKEN.UNKNOWN, resulting_string.strip() + '\n')
c = self._input.next()
# Spidermonkey-specific sharp variables for circular references
# https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
# http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp
# around line 1935
sharp = '#'
if self.input.hasNext() and self.input.testChar(self.digit):
if self._input.hasNext() and self._input.testChar(digit):
while True:
c = self.input.next()
c = self._input.next()
sharp += c
if (not self.input.hasNext()) or c == '#' or c == '=':
if (not self._input.hasNext()) or c == '#' or c == '=':
break
if c == '#':
pass
elif self.input.peek() == '[' and self.input.peek(1) == ']':
sharp += '[]'
self.input.next()
self.input.next()
elif self.input.peek() == '{' and self.input.peek(1) == '}':
sharp += '{}'
self.input.next()
self.input.next()
return sharp, TOKEN.WORD
if c == '#':
pass
elif self._input.peek() == '[' and self._input.peek(1) == ']':
sharp += '[]'
self._input.next()
self._input.next()
elif self._input.peek() == '{' and self._input.peek(1) == '}':
sharp += '{}'
self._input.next()
self._input.next()
if c == '<' and self.input.peek() in ['?', '%']:
self.input.back()
template_match = self.input.match(self.template_pattern)
if template_match:
c = template_match.group(0)
c = re.sub(self.acorn.allLineBreaks, '\n', c)
return c, TOKEN.STRING
return self._create_token(TOKEN.WORD, sharp)
if c == '<' and self.input.match(re.compile(r'\!--')):
c = '<!--'
while self.input.hasNext() and not self.input.testChar(self.acorn.newline):
c += self.input.next()
self._input.back()
self.in_html_comment = True
return c, TOKEN.COMMENT
elif c == '<' and self._is_first_token():
if c == '-' and self.in_html_comment and self.input.match(
re.compile('->')):
if self._patterns.html_comment_start.read():
c = '<!--'
while self._input.hasNext() and not self._input.testChar(self.acorn.newline):
c += self._input.next()
self.in_html_comment = True
return self._create_token(TOKEN.COMMENT, c)
elif c == '-' and self.in_html_comment and \
self._patterns.html_comment_end.read():
self.in_html_comment = False
return '-->', TOKEN.COMMENT
return self._create_token(TOKEN.COMMENT, '-->')
if c == '.':
if self.input.peek() == '.' and self.input.peek(1) == '.':
c += self.input.next() + self.input.next()
return c, TOKEN.OPERATOR
return None
return c, TOKEN.DOT
def _read_punctuation(self):
token = None
resulting_string = self._patterns.punct.read()
if resulting_string != '':
if resulting_string == '=':
token = self._create_token(TOKEN.EQUALS, resulting_string)
else:
token = self._create_token(TOKEN.OPERATOR, resulting_string)
if c in self.punct:
while self.input.hasNext() and c + self.input.peek() in self.punct:
c += self.input.next()
if not self.input.hasNext():
break
return token
if c == ',':
return c, TOKEN.COMMA
if c == '=':
return c, TOKEN.EQUALS
__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 )
return c, TOKEN.OPERATOR
def parse_string(
self,
delimiter,
allow_unescaped_newlines=False,
start_sub=None):
if delimiter == '\'':
pattern = self._patterns.single_quote
elif delimiter == '"':
pattern = self._patterns.double_quote
elif delimiter == '`':
pattern = self._patterns.template_text
elif delimiter == '}':
pattern = self._patterns.template_expression
resulting_string = pattern.read()
next = ''
while self._input.hasNext():
next = self._input.next()
if next == delimiter or \
(not allow_unescaped_newlines and
self.acorn.newline.match(next)):
self._input.back()
break
elif next == '\\' and self._input.hasNext():
current_char = self._input.peek()
if current_char == 'x' or current_char == 'u':
self.has_char_escapes = True
elif current_char == '\r' and self._input.peek(1) == '\n':
self._input.next()
next += self._input.next()
elif start_sub is not None:
if start_sub == '${' and next == '$' and \
self._input.peek() == '{':
next += self._input.next()
if start_sub == next:
if delimiter == '`':
next += self.parse_string(
'}', allow_unescaped_newlines, '`')
else:
next += self.parse_string(
'`', allow_unescaped_newlines, '${')
if self._input.hasNext():
next += self._input.next()
next += pattern.read()
resulting_string += next
return resulting_string
return c, TOKEN.UNKNOWN
def unescape_string(self, s):
# You think that a regex would work for this

View File

@ -1,17 +0,0 @@
import unittest
from ...core.inputscanner import InputScanner
class TestInputScanner(unittest.TestCase):
@classmethod
def setUpClass(cls):
pass
def test_new(self):
inputscanner = InputScanner(None)
self.assertEqual(inputscanner.hasNext(), False)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,48 @@
import re
import unittest
from ...core.inputscanner import InputScanner
class TestInputScanner(unittest.TestCase):
@classmethod
def setUpClass(cls):
pass
def setUp(self):
self.value = 'howdy'
self.inputscanner = InputScanner(self.value)
def test_new(self):
inputscanner = InputScanner(None)
self.assertEqual(inputscanner.hasNext(), False)
def test_next(self):
self.assertEqual(self.inputscanner.next(), self.value[0])
self.assertEqual(self.inputscanner.next(), self.value[1])
def test_peek(self):
self.assertEqual(self.inputscanner.peek(3), self.value[3])
self.inputscanner.next()
self.assertEqual(self.inputscanner.peek(3), self.value[4])
def test_no_param(self):
self.assertEqual(self.inputscanner.peek(), self.value[0])
self.inputscanner.next()
self.assertEqual(self.inputscanner.peek(), self.value[1])
def test_pattern(self):
pattern = re.compile(r'how')
index = 0
self.assertEqual(self.inputscanner.test(pattern, index), True)
self.inputscanner.next()
self.assertEqual(self.inputscanner.test(pattern, index), False)
def test_Char(self):
pattern = re.compile(r'o')
index = 1
self.assertEqual(self.inputscanner.testChar(pattern, index), True)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,140 @@
import re
import unittest
from ...core.options import _mergeOpts, _normalizeOpts, Options
class TestOptions(unittest.TestCase):
@classmethod
def setUpClass(cls):
pass
def test_mergeOpts(self):
# should convert tuple to dict and merge child with parent
result = _mergeOpts((('a', 1), ('b', {'a': 2})), 'b')
self.assertEqual(result.a, 2)
self.assertNotIn('b', result)
# should merge child option a with the parent options (dict)
result = _mergeOpts({'a': 1, 'b': {'a': 2}}, 'b')
self.assertEqual(result.a, 2)
self.assertNotIn('b', result)
# should include child option c and d with the parent options (dict)
result = _mergeOpts({'a': 1, 'b': {'c': 2, 'd': 3}}, 'b')
self.assertEqual(result.a, 1)
self.assertEqual(result.c, 2)
self.assertEqual(result.d, 3)
self.assertNotIn('b', result)
# should merge child option a and include c as parent options (dict)
result = _mergeOpts({'a': 1, 'b': {'a': 2, 'c': 3}}, 'b')
self.assertEqual(result.a, 2)
self.assertEqual(result.c, 3)
self.assertNotIn('b', result)
# should merge Options instance with child dict key
instance = Options()
instance.a = {'disabled': True}
result = _mergeOpts(instance, 'a')
self.assertEqual(result.disabled, True)
self.assertNotIn('a', list(getattr(result, '__dict__', {})))
def test_normalizeOpts(self):
# should replace key with - to _ in dict
result = _normalizeOpts({
'a-b': 1
})
self.assertEqual(result['a_b'], 1)
self.assertNotIn('a-b', result)
# should replace key with - to _ in Options instance
instance = Options()
setattr(instance, 'a-b', 1)
result = _normalizeOpts(instance)
self.assertEqual(result.a_b, 1)
self.assertNotIn('a-b', list(getattr(result, '__dict__', {})))
# should do nothing
result = _normalizeOpts({
'a_b': 1
})
self.assertEqual(result['a_b'], 1)
def test__get_boolean(self):
# should return default value since no option
self.assertEqual(Options()._get_boolean('a'), False)
# should return true as default since no option
self.assertEqual(Options()._get_boolean('a', True), True)
# should return false as in option
self.assertEqual(Options({'a': False})._get_boolean('a', True), False)
def test__get_characters(self):
# should return default value since no option
self.assertEqual(Options()._get_characters('a'), '')
# should return \'character\' as default since no option
self.assertEqual(Options()._get_characters(
'a', 'character'), 'character')
# should return \'char\' as in option
self.assertEqual(Options({'a': 'char'})._get_characters(
'a', 'character'), 'char')
def test__get_number(self):
# should return default value since no option
self.assertEqual(Options()._get_number('a'), 0)
# should return 1 as default since no option
self.assertEqual(Options()._get_number('a', 1), 1)
# should return 10 as in option
self.assertEqual(Options({'a': 10})._get_number('a', 1), 10)
# should return 0 for NaN as in option
self.assertEqual(Options({'a': 'abc'})._get_number('a'), 0)
# should return 0 for NaN as in default
self.assertEqual(Options()._get_number('a', 'abc'), 0)
def test__get_array(self):
# should return [] with no option
self.assertEqual(Options()._get_array('a'), [])
# should return [\'a\',\'b\'] as default since no option
self.assertEqual(Options()._get_array('a', ['a', 'b']), ['a', 'b'])
# should return [\'c\',\'d\'] as in option
self.assertEqual(Options({'a': ['c', 'd']})._get_array(
'a', ['a', 'b']), ['c', 'd'])
# should return [\'c\',\'d\'] as in option comma separated
self.assertEqual(Options({'a': 'c,d'})._get_array(
'a', ['a', 'b']), ['c', 'd'])
def test__is_valid_selection(self):
# should return false with empty selection
self.assertEqual(Options()._is_valid_selection(['a', 'b'], []), False)
# should return false with selection inexistent
self.assertEqual(Options()._is_valid_selection(
['a', 'b'], ['c']), False)
# should return true with selection existent
self.assertEqual(Options()._is_valid_selection(
['a', 'b'], ['a', 'b']), True)
def test__get_selection_list(self):
# should raise error with empty selection
with self.assertRaisesRegexp(ValueError, 'Selection list cannot'
+ ' be empty.'):
Options()._get_selection_list('a', [])
# should raise error with invalid default
with self.assertRaisesRegexp(ValueError, 'Invalid Default Value!'):
Options()._get_selection_list('a', ['a', 'b'], ['c'])
# should raise error with invalid option
with self.assertRaisesRegexp(ValueError, '^Invalid Option Value:'
+ ' The option'):
Options({'a': ['c', 'd']})._get_selection_list(
'a', ['a', 'b'], ['a', 'b'])
# should return [\'c\'] as in option
opts = Options({'c': ['c']})
self.assertEqual(opts._get_selection_list(
'c', ['c', 'd'], ['c']), ['c'])
def test__get_selection(self):
# should raise error with multiple selection
with self.assertRaisesRegexp(ValueError, '^Invalid Option'
+ ' Value: The option'):
Options({'a': ['a', 'b']})._get_selection('a', ['a', 'b'], ['a'])
# should return [\'a\'] as in option
options = Options({'a': ['a']})
self.assertEqual(options._get_selection(
'a', ['a', 'b'], ['a']), 'a')
if __name__ == '__main__':
unittest.main()

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,15 @@
REL_SCRIPT_DIR="`dirname \"$0\"`"
SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`"
PROJECT_DIR="`( cd \"$SCRIPT_DIR/../../..\" && pwd )`"
case "$OSTYPE" in
darwin*) PLATFORM="OSX" ;;
linux*) PLATFORM="LINUX" ;;
bsd*) PLATFORM="BSD" ;;
*) PLATFORM="UNKNOWN" ;;
esac
test_cli_common()
{
@ -12,13 +21,23 @@ test_cli_common()
echo Script: $CLI_SCRIPT
# should find the minimal help output
$CLI_SCRIPT 2>&1 | grep -q "Must pipe input or define at least one file\." || {
$CLI_SCRIPT 2>&1
$CLI_SCRIPT 2>&1 < /dev/null | grep -q "Must pipe input or define at least one file\." || {
$CLI_SCRIPT 2>&1 < /dev/null
echo "[$CLI_SCRIPT_NAME] Output should be help message."
exit 1
}
$CLI_SCRIPT 2> /dev/null && {
# unicode error - only happens in python
# Note: different exceptions are thrown on different platforms.
if [[ "$PLATFORM" == "OSX" || "$PLATFORM" == "BSD" || "$PLATFORM" == "LINUX" ]]; then
$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
}
fi
$CLI_SCRIPT 2> /dev/null < /dev/null && {
echo "[$CLI_SCRIPT_NAME (with no parameters)] Return code should be error."
exit 1
}
@ -85,30 +104,42 @@ test_cli_js_beautify()
exit 1
}
setup_temp
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-file.js $SCRIPT_DIR/../../../js/bin/js-beautify.js && diff $SCRIPT_DIR/../../../js/bin/js-beautify.js $TEST_TEMP/js-beautify-file.js || {
$CLI_SCRIPT -o $TEST_TEMP/js-beautify-file.js $SCRIPT_DIR/../../../js/bin/js-beautify.js && diff $SCRIPT_DIR/../../../js/bin/js-beautify.js $TEST_TEMP/js-beautify-file.js | cat -t -e
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js was expected to be unchanged."
cleanup 1
}
cat $SCRIPT_DIR/../../../js/bin/js-beautify.js | $CLI_SCRIPT -o $TEST_TEMP/js-beautify-pipe.js - && diff $SCRIPT_DIR/../../../js/bin/js-beautify.js $TEST_TEMP/js-beautify-pipe.js || {
cat $SCRIPT_DIR/../../../js/bin/js-beautify.js | $CLI_SCRIPT -o $TEST_TEMP/js-beautify-pipe.js - && diff $SCRIPT_DIR/../../../js/bin/js-beautify.js $TEST_TEMP/js-beautify-pipe.js | cat -t -e
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js should have been created in $TEST_TEMP/js-beautify-pipe.js."
cleanup 1
}
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - || {
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - | cat -t -e
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js was expected to be unchanged."
exit 1
cleanup 1
}
cat $SCRIPT_DIR/../../../js/bin/js-beautify.js | $CLI_SCRIPT | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - || {
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - | cat -t -e
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js was expected to be unchanged."
exit 1
cleanup 1
}
cat $SCRIPT_DIR/../../../js/bin/js-beautify.js | $CLI_SCRIPT - | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - || {
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - | cat -t -e
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js was expected to be unchanged."
exit 1
}
setup_temp
cat $SCRIPT_DIR/../../../js/bin/js-beautify.js | $CLI_SCRIPT -o $TEST_TEMP/js-beautify-pipe.js - || diff $SCRIPT_DIR/../../../js/bin/js-beautify.js $TEST_TEMP/js-beautify-pipe.js || {
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js should have been created in $TEST_TEMP/js-beautify-pipe.js."
cleanup 1
}
cat $SCRIPT_DIR/../../../js/bin/js-beautify.js | $CLI_SCRIPT -f - | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - || {
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - | cat -t -e
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js was expected to be unchanged."
cleanup 1
}
$CLI_SCRIPT -o $TEST_TEMP/js-beautify.js $SCRIPT_DIR/../../../js/bin/js-beautify.js && diff $SCRIPT_DIR/../../../js/bin/js-beautify.js $TEST_TEMP/js-beautify.js || {
$CLI_SCRIPT -o $TEST_TEMP/js-beautify.js $SCRIPT_DIR/../../../js/bin/js-beautify.js && diff $SCRIPT_DIR/../../../js/bin/js-beautify.js $TEST_TEMP/js-beautify.js | cat -t -e
@ -145,6 +176,51 @@ test_cli_js_beautify()
cleanup 1
}
# Glob related tests
cp -r $PROJECT_DIR/js/src $TEST_TEMP/
FILE_RCOUNT=$(find $PROJECT_DIR/js/src -name 't*.js' | grep -c .)
FILE_COUNT=$(ls $PROJECT_DIR/js/src/*.js | grep -c .)
$CLI_SCRIPT '*/*/missing_file' > /dev/null || {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE_GLOB] Return code should be success for globs."
exit 1
}
if [ "$FILE_COUNT" != "$(cd $TEST_TEMP && $CLI_SCRIPT 'src/*.js' | grep -c .)" ]; then
echo "js-beautify output for 'src/*.js' was expected have $FILE_COUNT files."
echo $(cd $TEST_TEMP && $CLI_SCRIPT 'src/*.js')
cleanup 1
fi
if [ "$FILE_COUNT" != "$(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/*.js' | grep -c .)" ]; then
echo "js-beautify output for 'src/*.js' was expected have $FILE_COUNT files."
echo $(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/*.js')
cleanup 1
fi
if [ "$FILE_COUNT" != "$(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/cl?.js' --file 'src/??dex.js' | grep -c .)" ]; then
echo "js-beautify output for --file 'src/cl?.js' --file 'src/??dex.js' was expected have $FILE_COUNT files."
echo $(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/cl?.js' --file 'src/??dex.js')
cleanup 1
fi
if [ "1" != "$(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/cl?.js' --file 'src/c??.js' 'src/cli.js' | grep -c .)" ]; then
echo "js-beautify output for --file 'src/cl?.js' --file 'src/cl?.js' was expected have 1 file."
echo $(cd $TEST_TEMP && $CLI_SCRIPT --file 'src/cl?.js' --file 'src/c??.js' 'src/cli.js')
cleanup 1
fi
# recursive wildcard not supported in python 3.4 or less
# only run this test if the script doesn't report failure.
$CLI_SCRIPT 'src/**/t*.js' && {
if [ "$FILE_RCOUNT" != "$(cd $TEST_TEMP && $CLI_SCRIPT 'src/**/t*.js' | grep -c .)" ]; then
echo "js-beautify output for 'src/**/t*.js' was expected have $FILE_RCOUNT files."
echo $(cd $TEST_TEMP && $CLI_SCRIPT 'src/**/t*.js')
cleanup 1
fi
}
# EditorConfig related tests
cp -r ../js/test/resources/editorconfig $TEST_TEMP/
$CLI_SCRIPT -o $TEST_TEMP/editorconfig/example.js --end-with-newline --indent-size 4 -e '\n' $TEST_TEMP/editorconfig/example-base.js
@ -294,7 +370,7 @@ test_cli_js_beautify()
echo "[$CLI_SCRIPT_NAME --brace-style=invalid $TEST_TEMP/example.js] Return code for invalid brace_style meta-parameter should be error."
cleanup 1
}
$CLI_SCRIPT --brace-style=expand,preserve-inline,invalid 'expand,preserve-inline,invalid' $TEST_TEMP/example.js > /dev/null && {
$CLI_SCRIPT --brace-style='expand,preserve-inline,invalid' $TEST_TEMP/example.js > /dev/null && {
echo "[$CLI_SCRIPT_NAME --brace-style=expand,preserve-inline,invalid $TEST_TEMP/example.js] Return code for invalid brace_style meta-parameter should be error."
cleanup 1
}
@ -306,7 +382,6 @@ test_cli_js_beautify()
cleanup
}
main() {
#test_cli_common css-beautify
#test_cli_common html-beautify

View File

@ -1,39 +0,0 @@
from __future__ import print_function
import sys
import jsbeautifier
opts = jsbeautifier.default_options()
opts.eol = "\n"
global fails
fails = 0
def test_str(str, expected):
global fails
res = jsbeautifier.beautify(str, opts)
if(res == expected):
print(".")
return True
else:
print("___got:" + res + "\n___expected:" + expected + "\n")
fails = fails + 1
return False
str = "eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('2 0=\"4 3!\";2 1=0.5(/b/6);a.9(\"8\").7=1;',12,12,'str|n|var|W3Schools|Visit|search|i|innerHTML|demo|getElementById|document|w3Schools'.split('|'),0,{}))"
expected = "var str = \"Visit W3Schools!\";\nvar n = str.search(/w3Schools/i);\ndocument.getElementById(\"demo\").innerHTML = n;"
res = test_str(str, expected)
str = "a=b;\r\nwhile(1){\ng=h;{return'\\w+'};break;eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('$(5).4(3(){$(\'.1\').0(2);$(\'.6\').0(d);$(\'.7\').0(b);$(\'.a\').0(8);$(\'.9\').0(c)});',14,14,'html|r5e57|8080|function|ready|document|r1655|rc15b|8888|r39b0|r6ae9|3128|65309|80'.split('|'),0,{}))c=abx;"
expected = "a = b;\nwhile (1) {\n g = h; {\n return '\\w+'\n };\n break;\n $(document).ready(function() {\n $('.r5e57').html(8080);\n $('.r1655').html(80);\n $('.rc15b').html(3128);\n $('.r6ae9').html(8888);\n $('.r39b0').html(65309)\n });\n c = abx;"
res = test_str(str, expected)
str = "eval(function(p,a,c,k,e,r){e=function(c){return c.toString(36)};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'[0-9ab]'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('$(5).a(6(){ $(\'.8\').0(1); $(\'.b\').0(4); $(\'.9\').0(2); $(\'.7\').0(3)})',[],12,'html|52136|555|65103|8088|document|function|r542c|r8ce6|rb0de|ready|rfab0'.split('|'),0,{}))"
expected = "$(document).ready(function() {\n $(\'.r8ce6\').html(52136);\n $(\'.rfab0\').html(8088);\n $(\'.rb0de\').html(555);\n $(\'.r542c\').html(65103)\n})"
res = test_str(str, expected)
if (fails == 0):
print("OK")

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>
#
@ -24,20 +24,24 @@ def detect(source):
global endstr
beginstr = ''
endstr = ''
begin_offset = -1
"""Detects whether `source` is P.A.C.K.E.R. coded."""
mystr = source.replace(' ', '').find('eval(function(p,a,c,k,e,')
if(mystr > 0):
beginstr = source[:mystr]
if(mystr != -1):
mystr = re.search('eval[ ]*\([ ]*function[ ]*\([ ]*p[ ]*,[ ]*a[ ]*,[ ]*c['
' ]*,[ ]*k[ ]*,[ ]*e[ ]*,[ ]*', source)
if(mystr):
begin_offset = mystr.start()
beginstr = source[:begin_offset]
if(begin_offset != -1):
""" Find endstr"""
if(source.split("')))", 1)[0] == source):
source_end = source[begin_offset:]
if(source_end.split("')))", 1)[0] == source_end):
try:
endstr = source.split("}))", 1)[1]
endstr = source_end.split("}))", 1)[1]
except IndexError:
endstr = ''
else:
endstr = source.split("')))", 1)[1]
return (mystr != -1)
endstr = source_end.split("')))", 1)[1]
return (mystr is not None)
def unpack(source):

View File

@ -26,7 +26,8 @@ class TestPacker(unittest.TestCase):
def test_unpack(self):
"""Test unpack() function."""
def check(inp, out): return self.assertEqual(unpack(inp), out)
def check(inp, out):
return detect(inp) and self.assertEqual(unpack(inp), out)
check("eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)"
"){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e="
@ -34,6 +35,22 @@ class TestPacker(unittest.TestCase):
"new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('0 2=1',"
"62,3,'var||a'.split('|'),0,{}))", 'var a=1')
check("function test (){alert ('This is a test!')}; "
"eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String))"
"{while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function"
"(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp("
"'\\b'+e(c)+'\\b','g'),k[c]);return p}('0 2=1',3,3,"
"'var||a'.split('|'),0,{}))",
"function test (){alert ('This is a test!')}; var a=1")
check("eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('2 0=\"4 3!\";2 1=0.5(/b/6);a.9(\"8\").7=1;',12,12,'str|n|var|W3Schools|Visit|search|i|innerHTML|demo|getElementById|document|w3Schools'.split('|'),0,{}))",
"var str=\"Visit W3Schools!\";var n=str.search(/w3Schools/i);document.getElementById(\"demo\").innerHTML=n;")
check("a=b;\r\nwhile(1){\ng=h;{return'\\w+'};break;eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('$(5).4(3(){$(\'.1\').0(2);$(\'.6\').0(d);$(\'.7\').0(b);$(\'.a\').0(8);$(\'.9\').0(c)});',14,14,'html|r5e57|8080|function|ready|document|r1655|rc15b|8888|r39b0|r6ae9|3128|65309|80'.split('|'),0,{}))c=abx;",
"a=b;\r\nwhile(1){\ng=h;{return'\\w+'};break;$(document).ready(function(){$('.r5e57').html(8080);$('.r1655').html(80);$('.rc15b').html(3128);$('.r6ae9').html(8888);$('.r39b0').html(65309)});c=abx;")
check("eval(function(p,a,c,k,e,r){e=function(c){return c.toString(36)};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'[0-9ab]'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('$(5).a(6(){ $(\'.8\').0(1); $(\'.b\').0(4); $(\'.9\').0(2); $(\'.7\').0(3)})',[],12,'html|52136|555|65103|8088|document|function|r542c|r8ce6|rb0de|ready|rfab0'.split('|'),0,{}))",
"$(document).ready(function(){ $('.r8ce6').html(52136); $('.rfab0').html(8088); $('.rb0de').html(555); $('.r542c').html(65103)})")
if __name__ == '__main__':
unittest.main()

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,39 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
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(io.open(github_file).readlines()))
# warm up
beautifier_test_github_css()
report_perf("beautifier_test_github_css")

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
import os
import copy
import jsbeautifier
@ -17,10 +18,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 +41,17 @@ if __name__ == '__main__':
dirname, "../", "test/resources/underscore.js")
underscore_min_file = os.path.join(
dirname, "../", "test/resources/underscore-min.js")
data = copy.copy(''.join(open(underscore_file).readlines()))
data_min = copy.copy(''.join(open(underscore_min_file).readlines()))
github_min_file = os.path.join(
dirname, "../", "test/resources/github-min.js")
data = copy.copy(''.join(io.open(underscore_file, encoding="UTF-8").readlines()))
data_min = copy.copy(''.join(io.open(underscore_min_file, encoding="UTF-8").readlines()))
github_min = copy.copy(''.join(io.open(github_min_file, encoding="UTF-8").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,35 +166,41 @@ 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
t(".tabs {}");
t(".tabs{color:red;}", ".tabs {\n\tcolor: red;\n}");
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}");
t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}");
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}");
t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}");
t("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}");
t(".tabs{color:red;}", ".tabs {\n color: red;\n}");
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n color: rgb(255, 255, 0)\n}");
t(".tabs{background:url('back.jpg')}", ".tabs {\n background: url('back.jpg')\n}");
t("#bla, #foo{color:red}", "#bla,\n#foo {\n color: red\n}");
t("@media print {.tab{}}", "@media print {\n .tab {}\n}");
t("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n .tab {\n background-image: url(foo@2x.png)\n }\n}");
t("a:before {\n" +
"\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
" content: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
"}");
//lead-in whitespace determines base-indent.
// lead-in newlines are stripped.
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }");
t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }");
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n padding: 0.2px\n}");
t(" a, img {padding: 0.2px}", " a,\n img {\n padding: 0.2px\n }");
t(" \na, img {padding: 0.2px}", " a,\n img {\n padding: 0.2px\n }");
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n padding: 0.2px\n}");
// separate selectors
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}");
t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
t("#bla, #foo{color:red}", "#bla,\n#foo {\n color: red\n}");
t("a, img {padding: 0.2px}", "a,\nimg {\n padding: 0.2px\n}");
// block nesting
t("#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}");
t("@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}");
t("#foo {\n background-image: url(foo@2x.png);\n @font-face {\n font-family: 'Bitstream Vera Serif Bold';\n src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n }\n}");
t("@media screen {\n #foo:hover {\n background-image: url(foo@2x.png);\n }\n @font-face {\n font-family: 'Bitstream Vera Serif Bold';\n src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n }\n}");
/*
@font-face {
font-family: 'Bitstream Vera Serif Bold';
@ -190,32 +220,32 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
}
}
*/
t("@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}");
t("@font-face {\n font-family: 'Bitstream Vera Serif Bold';\n src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n #foo:hover {\n background-image: url(foo.png);\n }\n @media screen and (min-device-pixel-ratio: 2) {\n @font-face {\n font-family: 'Helvetica Neue'\n }\n #foo:hover {\n background-image: url(foo@2x.png);\n }\n }\n}");
// less-css cases
t('.well{@well-bg:@bg-color;@well-fg:@fg-color;}','.well {\n\t@well-bg: @bg-color;\n\t@well-fg: @fg-color;\n}');
t('.well{@well-bg:@bg-color;@well-fg:@fg-color;}','.well {\n @well-bg: @bg-color;\n @well-fg: @fg-color;\n}');
t('.well {&.active {\nbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;}}',
'.well {\n' +
'\t&.active {\n' +
'\t\tbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;\n' +
'\t}\n' +
' &.active {\n' +
' box-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;\n' +
' }\n' +
'}');
t('a {\n' +
'\tcolor: blue;\n' +
'\t&:hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'\t& & &&&.active {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
' color: blue;\n' +
' &:hover {\n' +
' color: green;\n' +
' }\n' +
' & & &&&.active {\n' +
' color: green;\n' +
' }\n' +
'}');
// Not sure if this is sensible
// but I believe it is correct to not remove the space in "&: hover".
t('a {\n' +
'\t&: hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
' &: hover {\n' +
' color: green;\n' +
' }\n' +
'}');
// import
@ -223,29 +253,29 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
// don't break nested pseudo-classes
t("a:first-child{color:red;div:first-child{color:black;}}",
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
"a:first-child {\n color: red;\n div:first-child {\n color: black;\n }\n}");
// handle SASS/LESS parent reference
t("div{&:first-letter {text-transform: uppercase;}}",
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
"div {\n &:first-letter {\n text-transform: uppercase;\n }\n}");
//nested modifiers (&:hover etc)
t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&.big{width:10px;}}", ".tabs {\n\t&.big {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&>big{width:10px;}}", ".tabs {\n\t&>big {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&+.big{width:10px;}}", ".tabs {\n\t&+.big {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&:hover{width:10px;}}", ".tabs {\n &:hover {\n width: 10px;\n }\n}");
t(".tabs{&.big{width:10px;}}", ".tabs {\n &.big {\n width: 10px;\n }\n}");
t(".tabs{&>big{width:10px;}}", ".tabs {\n &>big {\n width: 10px;\n }\n}");
t(".tabs{&+.big{width:10px;}}", ".tabs {\n &+.big {\n width: 10px;\n }\n}");
//nested rules
t(".tabs{.child{width:10px;}}", ".tabs {\n\t.child {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{.child{width:10px;}}", ".tabs {\n .child {\n width: 10px;\n }\n}");
//variables
t("@myvar:10px;.tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}");
t("@myvar:10px; .tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}");
t("@myvar:10px;.tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n width: 10px;\n}");
t("@myvar:10px; .tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n width: 10px;\n}");
//mixins
t("div{.px2rem(width,12);}", "div {\n\t.px2rem(width, 12);\n}");
t("div{.px2rem(width,12);}", "div {\n .px2rem(width, 12);\n}");
// mixin next to 'background: url("...")' should not add a line break after the comma
t("div {\n\tbackground: url(\"//test.com/dummy.png\");\n\t.px2rem(width, 12);\n}");
t("div {\n background: url(\"//test.com/dummy.png\");\n .px2rem(width, 12);\n}");
// test options
opts.indent_size = 2;

Some files were not shown because too many files have changed in this diff Show More