mirror of
https://github.com/beautifier/js-beautify.git
synced 2025-02-25 15:53:04 +00:00
Merge remote-tracking branch 'beautify-web/master'
This commit is contained in:
commit
6e710e1413
@ -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
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal 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)
|
||||
|
@ -6,8 +6,7 @@ node_modules/**
|
||||
python/**
|
||||
target/**
|
||||
tools/**
|
||||
test/resources/underscore-min.js
|
||||
test/resources/underscore.js
|
||||
test/resources/*
|
||||
web/lib/**
|
||||
build/**
|
||||
|
||||
|
@ -4,8 +4,10 @@
|
||||
"eqeqeq": true,
|
||||
"noarg": true,
|
||||
"nocomma": true,
|
||||
"node": true,
|
||||
"nonbsp": true,
|
||||
"nonew": true,
|
||||
"strict": true,
|
||||
"unused": true,
|
||||
"esversion": 3
|
||||
}
|
||||
|
19
.travis.yml
19
.travis.yml
@ -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"
|
340
CHANGELOG.md
340
CHANGELOG.md
@ -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
46
CODE_OF_CONDUCT.md
Normal 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/
|
@ -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
|
||||
|
17
Makefile
17
Makefile
@ -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
|
||||
|
91
README.md
91
README.md
@ -1,6 +1,5 @@
|
||||
# JS Beautifier
|
||||
[](http://travis-ci.org/beautify-web/js-beautify)
|
||||
[](https://ci.appveyor.com/project/beautify-web/js-beautify/branch/master)
|
||||
[](https://dev.azure.com/beautifier-io/js-beautify/_build/latest?definitionId=1)
|
||||
|
||||
[](https://pypi.python.org/pypi/jsbeautifier)
|
||||
[](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 Edward’s 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)
|
||||
|
37
appveyor.yml
37
appveyor.yml
@ -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
96
azure-pipelines.yml
Normal 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'
|
||||
|
468
index.html
468
index.html
@ -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 <style>, <script> 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 <head> and <body> 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 <style>, <script> 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 <head> and <body> 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="t">'+(sr.join("\n"))+"\n\n-----\n\n"+tx+'</textarea><script src="https://beautifier.io/js/lib/beautify.js"></script><script>with(document.getElementById("t")){value=js_beautify(value);with(style){width="99%";height="99%";borderStyle="none";}};</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 Diamond, Patrick Hof, Nochum Sossonko, Andreas Schneider,
|
||||
<br>Dave Vasilevsky,
|
||||
<a href="https://moikrug.ru/vital">Vital Batmanov</a>, Ron Baldwin, Gabriel 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="t">'+(sr.join("\n"))+"\n\n-----\n\n"+tx+'</textarea><script src="http://jsbeautifier.org/beautify.js"></script><script>with(document.getElementById("t")){value=js_beautify(value);with(style){width="99%";height="99%";borderStyle="none";}};</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 Diamond, Patrick Hof, Nochum Sossonko, Andreas Schneider,
|
||||
<br>Dave Vasilevsky,
|
||||
<a href="https://moikrug.ru/vital">Vital Batmanov</a>, Ron Baldwin, Gabriel 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>
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
/*jshint node:true */
|
||||
/* globals define */
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
@ -25,6 +27,8 @@
|
||||
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
The following batches are equivalent:
|
||||
|
||||
|
1009
js/src/cli.js
1009
js/src/cli.js
File diff suppressed because it is too large
Load Diff
@ -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
62
js/src/core/directives.js
Normal 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;
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
94
js/src/core/pattern.js
Normal 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;
|
188
js/src/core/templatablepattern.js
Normal file
188
js/src/core/templatablepattern.js
Normal 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;
|
@ -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
140
js/src/core/tokenizer.js
Normal 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;
|
78
js/src/core/tokenstream.js
Normal file
78
js/src/core/tokenstream.js
Normal 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;
|
105
js/src/core/whitespacepattern.js
Normal file
105
js/src/core/whitespacepattern.js
Normal 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;
|
@ -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;
|
||||
|
@ -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
46
js/src/css/options.js
Normal 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
29
js/src/css/tokenizer.js
Normal 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
@ -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
91
js/src/html/options.js
Normal 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
310
js/src/html/tokenizer.js
Normal 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;
|
@ -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;
|
||||
|
57
js/src/javascript/acorn.js
Normal file
57
js/src/javascript/acorn.js
Normal 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
@ -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();
|
||||
};
|
||||
|
93
js/src/javascript/options.js
Normal file
93
js/src/javascript/options.js
Normal 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
@ -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 = {
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
};
|
||||
|
@ -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 = {
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
};
|
||||
|
@ -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 = {
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
66
js/test/core/test_inputscanner.js
Normal file
66
js/test/core/test_inputscanner.js
Normal 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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
150
js/test/core/test_options.js
Normal file
150
js/test/core/test_options.js
Normal 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
45
js/test/node-beautify-css-perf-tests.js
Normal file
45
js/test/node-beautify-css-perf-tests.js
Normal 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());
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 $*)
|
||||
|
@ -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
4948
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
34
package.json
34
package.json
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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 = '-'
|
||||
|
@ -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
|
||||
|
@ -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
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -1 +1 @@
|
||||
__version__ = '1.8.0-rc4'
|
||||
__version__ = '1.10.0'
|
||||
|
53
python/jsbeautifier/core/directives.py
Normal file
53
python/jsbeautifier/core/directives.py
Normal 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)
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
82
python/jsbeautifier/core/pattern.py
Normal file
82
python/jsbeautifier/core/pattern.py
Normal 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
|
||||
|
174
python/jsbeautifier/core/templatablepattern.py
Normal file
174
python/jsbeautifier/core/templatablepattern.py
Normal 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
|
@ -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
|
||||
|
135
python/jsbeautifier/core/tokenizer.py
Normal file
135
python/jsbeautifier/core/tokenizer.py
Normal 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()
|
74
python/jsbeautifier/core/tokenstream.py
Normal file
74
python/jsbeautifier/core/tokenstream.py
Normal 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()
|
78
python/jsbeautifier/core/whitespacepattern.py
Normal file
78
python/jsbeautifier/core/whitespacepattern.py
Normal 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)
|
||||
|
||||
|
@ -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
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
48
python/jsbeautifier/tests/core/test_inputscanner.py
Normal file
48
python/jsbeautifier/tests/core/test_inputscanner.py
Normal 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()
|
140
python/jsbeautifier/tests/core/test_options.py
Normal file
140
python/jsbeautifier/tests/core/test_options.py
Normal 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
@ -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
|
||||
|
@ -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")
|
@ -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>
|
||||
#
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
#
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
39
python/test-perf-cssbeautifier.py
Executable file
39
python/test-perf-cssbeautifier.py
Executable 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")
|
@ -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")
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user