├── .pylintrc ├── python ├── debian │ ├── compat │ ├── source │ │ └── format │ ├── .gitignore │ ├── changelog │ ├── rules │ └── control ├── __init__.py ├── cssbeautifier │ ├── css │ │ ├── __init__.py │ │ └── options.py │ ├── tests │ │ ├── __init__.py │ │ └── generated │ │ │ └── __init__.py │ ├── __version__.py │ └── __init__.py ├── jsbeautifier │ ├── core │ │ ├── __init__.py │ │ ├── token.py │ │ ├── directives.py │ │ ├── tokenstream.py │ │ ├── whitespacepattern.py │ │ ├── pattern.py │ │ ├── tokenizer.py │ │ └── inputscanner.py │ ├── tests │ │ ├── __init__.py │ │ ├── core │ │ │ └── __init__.py │ │ ├── generated │ │ │ └── __init__.py │ │ └── testindentation.py │ ├── __version__.py │ ├── javascript │ │ ├── __init__.py │ │ └── options.py │ └── unpackers │ │ ├── tests │ │ ├── __init__.py │ │ ├── testurlencode.py │ │ ├── testmyobfuscate.py │ │ ├── testjavascriptobfuscator.py │ │ ├── test-myobfuscate-output.js │ │ ├── test-packer-62-input.js │ │ ├── testpacker.py │ │ └── test-packer-non62-input.js │ │ ├── README.specs.mkd │ │ ├── urlencode.py │ │ ├── evalbased.py │ │ ├── javascriptobfuscator.py │ │ ├── __init__.py │ │ └── myobfuscate.py ├── MANIFEST.in ├── css-beautify ├── js-beautify-test ├── pyproject.toml ├── js-beautify-test.py ├── setup-css.py ├── test-perf-cssbeautifier.py ├── setup-js.py ├── js-beautify-profile └── test-perf-jsbeautifier.py ├── js ├── test │ ├── resources │ │ ├── configerror │ │ │ ├── subDir1 │ │ │ │ └── subDir2 │ │ │ │ │ └── empty.txt │ │ │ └── .jsbeautifyrc │ │ ├── indent11chars │ │ │ ├── subDir1 │ │ │ │ └── subDir2 │ │ │ │ │ └── empty.txt │ │ │ └── .jsbeautifyrc │ │ ├── example1.js │ │ └── editorconfig │ │ │ ├── cr │ │ │ └── .editorconfig │ │ │ ├── crlf │ │ │ └── .editorconfig │ │ │ ├── error │ │ │ └── .editorconfig │ │ │ ├── .editorconfig │ │ │ └── example-base.js │ ├── run-tests │ ├── node-beautify-css-perf-tests.js │ ├── node-beautify-perf-tests.js │ ├── node-beautify-html-perf-tests.js │ ├── node-src-index-tests.js │ ├── amd-beautify-tests.js │ ├── node-beautify-tests.js │ └── sanitytest.js ├── bin │ ├── js-beautify.js │ ├── css-beautify.js │ └── html-beautify.js ├── config │ └── defaults.json ├── src │ ├── css │ │ ├── tokenizer.js │ │ ├── index.js │ │ └── options.js │ ├── javascript │ │ ├── index.js │ │ └── options.js │ ├── html │ │ ├── index.js │ │ └── options.js │ ├── index.js │ ├── core │ │ ├── token.js │ │ ├── tokenstream.js │ │ ├── directives.js │ │ ├── pattern.js │ │ ├── whitespacepattern.js │ │ └── tokenizer.js │ └── unpackers │ │ ├── urlencode_unpacker.js │ │ ├── myobfuscate_unpacker.js │ │ ├── javascriptobfuscator_unpacker.js │ │ └── p_a_c_k_e_r_unpacker.js └── index.js ├── tools ├── node ├── python ├── python-dev3 ├── python-dev ├── python-rel ├── git-status-clear.sh ├── npm ├── generate-changelog.sh ├── build.sh ├── template │ ├── beautify-css.wrapper.js │ ├── beautify.wrapper.js │ └── beautify-html.wrapper.js └── release-all.sh ├── web ├── favicon.png ├── google-analytics.js ├── onload.js ├── icon.svg └── common-style.css ├── test ├── resources │ └── unicode-error.js └── data │ └── javascript │ └── inputlib.js ├── .gitattributes ├── .jshintignore ├── bower.json ├── .jshintrc ├── .npmignore ├── .codeclimate.yml ├── .github ├── workflows │ ├── ssh_config.txt │ ├── main.yml │ ├── pr-staging.yml │ ├── codeql-analysis.yml │ └── milestone-publish.yml ├── ISSUE_TEMPLATE │ ├── question-about-usage.md │ ├── feature_request.md │ └── beautification-problem.md ├── PULL_REQUEST_TEMPLATE.md └── dependabot.yml ├── .gitignore ├── jsbeautifyrc ├── LICENSE ├── webpack.config.js ├── package.json └── CODE_OF_CONDUCT.md /.pylintrc: -------------------------------------------------------------------------------- 1 | max-line-length=88 -------------------------------------------------------------------------------- /python/debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /python/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | -------------------------------------------------------------------------------- /python/debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /js/test/resources/configerror/subDir1/subDir2/empty.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js/test/resources/indent11chars/subDir1/subDir2/empty.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /python/cssbeautifier/css/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | -------------------------------------------------------------------------------- /python/cssbeautifier/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | -------------------------------------------------------------------------------- /python/jsbeautifier/core/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | -------------------------------------------------------------------------------- /python/jsbeautifier/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | -------------------------------------------------------------------------------- /python/cssbeautifier/__version__.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.15.4" 2 | -------------------------------------------------------------------------------- /python/jsbeautifier/__version__.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.15.4" 2 | -------------------------------------------------------------------------------- /python/jsbeautifier/javascript/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | -------------------------------------------------------------------------------- /python/jsbeautifier/tests/core/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | -------------------------------------------------------------------------------- /python/cssbeautifier/tests/generated/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | -------------------------------------------------------------------------------- /python/jsbeautifier/tests/generated/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | -------------------------------------------------------------------------------- /tools/node: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | /usr/bin/env node $@ 4 | -------------------------------------------------------------------------------- /tools/python: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | /usr/bin/env python $@ 4 | -------------------------------------------------------------------------------- /python/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include js-beautify 2 | include js-beautify-test 3 | -------------------------------------------------------------------------------- /js/test/resources/example1.js: -------------------------------------------------------------------------------- 1 | function indentMe() { 2 | "no, me!"; 3 | } 4 | -------------------------------------------------------------------------------- /js/test/resources/editorconfig/cr/.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | [*.js] 3 | end_of_line = cr 4 | -------------------------------------------------------------------------------- /js/test/resources/editorconfig/crlf/.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | [*.js] 3 | end_of_line = crlf 4 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beautifier/js-beautify/HEAD/web/favicon.png -------------------------------------------------------------------------------- /js/test/resources/editorconfig/error/.editorconfig: -------------------------------------------------------------------------------- 1 | Random stuff in here to cause parse error 2 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | # pylint: disable=C0111 3 | -------------------------------------------------------------------------------- /js/bin/js-beautify.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var cli = require('../lib/cli'); 4 | cli.interpret(); -------------------------------------------------------------------------------- /python/debian/.gitignore: -------------------------------------------------------------------------------- 1 | /files 2 | /*.log 3 | /*.debhelper 4 | /*.substvars 5 | /python3-jsbeautifier 6 | -------------------------------------------------------------------------------- /js/bin/css-beautify.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var cli = require('../lib/cli'); cli.interpret(); 3 | 4 | 5 | -------------------------------------------------------------------------------- /js/bin/html-beautify.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var cli = require('../lib/cli'); cli.interpret(); 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/resources/unicode-error.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beautifier/js-beautify/HEAD/test/resources/unicode-error.js -------------------------------------------------------------------------------- /js/test/resources/editorconfig/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.js] 4 | indent_style = space 5 | indent_size = 2 6 | insert_final_newline = false 7 | -------------------------------------------------------------------------------- /js/test/resources/editorconfig/example-base.js: -------------------------------------------------------------------------------- 1 | function indentMe() { 2 | "no, me!"; // indent_size 4, will be beautified to 2 with editorconfig 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.java text eol=lf 2 | *.py text eol=lf 3 | *.js text eol=lf 4 | *.json text eol=lf 5 | *.svg text eol=lf 6 | *.css text eol=lf 7 | *.mustache text eol=lf -------------------------------------------------------------------------------- /python/css-beautify: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # 3 | # Stub script to run cssbeautifier 4 | # 5 | import sys 6 | from cssbeautifier import main 7 | sys.exit(main()) 8 | -------------------------------------------------------------------------------- /js/test/resources/configerror/.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "indent_size": 11, 3 | "indent_char": " " 4 | "indent_level": 0, 5 | "indent_with_tabs": false 6 | } 7 | -------------------------------------------------------------------------------- /js/test/resources/indent11chars/.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "indent_size": 11, 3 | "indent_char": " ", 4 | "indent_level": 0, 5 | "indent_with_tabs": false 6 | } 7 | -------------------------------------------------------------------------------- /python/js-beautify-test: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # Test suite launcher 4 | # 5 | 6 | if [ -z $PYTHON ]; then 7 | env python js-beautify-test.py 8 | else 9 | env $PYTHON js-beautify-test.py 10 | fi 11 | -------------------------------------------------------------------------------- /python/debian/changelog: -------------------------------------------------------------------------------- 1 | jsbeautifier (1.5.10-1) smurf; urgency=low 2 | 3 | * source package automatically created by stdeb 0.8.2 4 | 5 | -- Matthias Urlichs Sun, 13 Dec 2015 17:36:34 +0100 6 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | dist/** 2 | js/bin/** 3 | js/lib/** 4 | js/test/resources/** 5 | node_modules/** 6 | python/** 7 | target/** 8 | tools/** 9 | test/resources/* 10 | web/lib/** 11 | build/** 12 | 13 | web/google-analytics.js -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-beautify", 3 | "main": [ 4 | "./js/lib/beautify.js", 5 | "./js/lib/beautify-css.js", 6 | "./js/lib/beautify-html.js" 7 | ], 8 | "ignore": [ 9 | "**/.*" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /python/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # This file was automatically generated by stdeb 0.8.2 at 4 | # Sun, 13 Dec 2015 17:36:34 +0100 5 | export PYBUILD_NAME=jsbeautifier 6 | %: 7 | dh $@ --with python3 --buildsystem=pybuild 8 | 9 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "noarg": true, 6 | "nocomma": true, 7 | "node": true, 8 | "nonbsp": true, 9 | "nonew": true, 10 | "strict": true, 11 | "unused": true, 12 | "esversion": 3 13 | } 14 | -------------------------------------------------------------------------------- /python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | target-version = ['py33', 'py36', 'py37', 'py38'] 3 | exclude = ''' 4 | /( 5 | \.eggs 6 | | \.git 7 | | \.hg 8 | | \.mypy_cache 9 | | \.tox 10 | | \.venv 11 | | _build 12 | | buck-out 13 | | build 14 | | dist 15 | | tests/generated 16 | )/ 17 | ''' 18 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # ANY FUTURE CHANGES TO THIS FILE NEED TO BE MANUALLY TESTED LOCALLY 2 | # USE npm pack AND VERIFY THAT PACKAGE CONTENTS ARE STILL VALID 3 | # Ignore all files 4 | * 5 | 6 | # Add exceptions 7 | !js/ 8 | !js/**/* 9 | !CONTRIBUTING.MD 10 | # README, LICENSE, CHANGELOG and package.json are always added 11 | 12 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | eslint: 3 | enabled: true 4 | pep8: 5 | enabled: true 6 | ratings: 7 | paths: 8 | - "**.js" 9 | - "**.py" 10 | exclude_paths: 11 | - webpack.config.js 12 | - "**/resources/**" 13 | - js/test/** 14 | - test/data/** 15 | - web/lib/** 16 | - tools/template/* 17 | - "**/generated/*" 18 | -------------------------------------------------------------------------------- /.github/workflows/ssh_config.txt: -------------------------------------------------------------------------------- 1 | Host beautifier-github.com 2 | HostName github.com 3 | IdentityFile ~/.ssh/deploy_beautifier_io 4 | IdentitiesOnly yes 5 | 6 | # Other github account: superman 7 | Host js-beautify-github.com 8 | HostName github.com 9 | IdentityFile ~/.ssh/deploy_js_beautify 10 | IdentitiesOnly yes 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.orig 2 | node_modules 3 | gh-pages 4 | gh 5 | 6 | *.pyc 7 | python/setup.py 8 | python/*/__pycache__ 9 | python/MANIFEST 10 | python/build 11 | python/dist 12 | python/.eggs/ 13 | python/jsbeautifier.egg-info 14 | python/cssbeautifier.egg-info 15 | .nvmrc 16 | .nvm/ 17 | 18 | target 19 | dist/ 20 | build/ 21 | js/lib/ 22 | generated/ 23 | .idea/ 24 | .vscode/ 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question-about-usage.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question about usage 3 | about: You have a question about how to use the beautifier 4 | 5 | --- 6 | 7 | # **DO NOT FILE USAGE QUESTIONS AS ISSUES** 8 | Review the [README.md](https://github.com/beautifier/js-beautify/blob/main/README.md). 9 | If that does not help, join us on gitter: https://gitter.im/beautifier/js-beautify . 10 | -------------------------------------------------------------------------------- /tools/python-dev3: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REL_SCRIPT_DIR="`dirname \"$0\"`" 4 | SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`" 5 | PROJECT_DIR="`( cd \"$SCRIPT_DIR/..\" && pwd )`" 6 | PYTHON_ENV=python-dev 7 | PYTHON_VERSION="`$PROJECT_DIR/build/$PYTHON_ENV/bin/python --version 2>&1`" 8 | 9 | # only run the command on python3 10 | if [ -z "${PYTHON_VERSION##Python 3.*}" ]; then 11 | $SCRIPT_DIR/python-dev $@ 12 | fi 13 | 14 | -------------------------------------------------------------------------------- /python/js-beautify-test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import unittest 5 | 6 | 7 | def run_tests(): 8 | suite = unittest.TestLoader().discover("jsbeautifier", pattern="test*.py") 9 | suite.addTests(unittest.TestLoader().discover("cssbeautifier", pattern="test*.py")) 10 | return unittest.TextTestRunner(verbosity=2).run(suite) 11 | 12 | 13 | if __name__ == "__main__": 14 | sys.exit(not run_tests().wasSuccessful()) 15 | -------------------------------------------------------------------------------- /tools/python-dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REL_SCRIPT_DIR="`dirname \"$0\"`" 4 | SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`" 5 | PROJECT_DIR="`( cd \"$SCRIPT_DIR/..\" && pwd )`" 6 | PYTHON_ENV=python-dev 7 | 8 | if [ -d "$PROJECT_DIR/build/$PYTHON_ENV/Scripts" ]; then 9 | RUN_FILE="$PROJECT_DIR/build/$PYTHON_ENV/Scripts/$1" 10 | else 11 | RUN_FILE="$PROJECT_DIR/build/$PYTHON_ENV/bin/$1" 12 | fi 13 | 14 | shift 15 | 16 | $RUN_FILE $@ 17 | -------------------------------------------------------------------------------- /tools/python-rel: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REL_SCRIPT_DIR="`dirname \"$0\"`" 4 | SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`" 5 | PROJECT_DIR="`( cd \"$SCRIPT_DIR/..\" && pwd )`" 6 | PYTHON_ENV=python-rel 7 | 8 | if [ -d "$PROJECT_DIR/build/$PYTHON_ENV/Scripts" ]; then 9 | RUN_FILE="$PROJECT_DIR/build/$PYTHON_ENV/Scripts/$1" 10 | else 11 | RUN_FILE="$PROJECT_DIR/build/$PYTHON_ENV/bin/$1" 12 | fi 13 | 14 | shift 15 | 16 | $RUN_FILE $@ 17 | -------------------------------------------------------------------------------- /python/debian/control: -------------------------------------------------------------------------------- 1 | Source: jsbeautifier 2 | Maintainer: Matthias Urlichs 3 | Section: python 4 | Priority: optional 5 | Build-Depends: python3-all, debhelper (>= 9) 6 | Standards-Version: 3.9.1 7 | 8 | Package: python3-jsbeautifier 9 | Architecture: all 10 | Depends: ${misc:Depends}, ${python3:Depends} 11 | Description: JavaScript unobfuscator and beautifier. 12 | Beautify, unpack or deobfuscate JavaScript. Handles popular online obfuscators. 13 | 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | - [ ] Source branch in your fork has meaningful name (not `main`) 3 | 4 | 5 | Fixes Issue: 6 | 7 | 8 | 9 | # Before Merge Checklist 10 | These items can be completed after PR is created. 11 | 12 | (Check any items that are not applicable (NA) for this PR) 13 | 14 | - [ ] JavaScript implementation 15 | - [ ] Python implementation (NA if HTML beautifier) 16 | - [ ] Added Tests to data file(s) 17 | - [ ] Added command-line option(s) (NA if 18 | - [ ] README.md documents new feature/option(s) 19 | 20 | -------------------------------------------------------------------------------- /web/google-analytics.js: -------------------------------------------------------------------------------- 1 | (function(i, s, o, g, r, a, m) { 2 | i['GoogleAnalyticsObject'] = r; 3 | i[r] = i[r] || function() { 4 | (i[r].q = i[r].q || []).push(arguments) 5 | }, i[r].l = 1 * new Date(); 6 | a = s.createElement(o), 7 | m = s.getElementsByTagName(o)[0]; 8 | a.async = 1; 9 | a.src = g; 10 | m.parentNode.insertBefore(a, m) 11 | })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); 12 | 13 | ga('create', 'UA-7409939-1', 'auto'); // Replace with your property ID. 14 | ga('send', 'pageview'); 15 | -------------------------------------------------------------------------------- /js/test/run-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/spidermonkey-1.7 -s 2 | 3 | //#!/usr/bin/js 4 | 5 | // a little helper for testing from command line 6 | // just run it, it will output the test results 7 | 8 | load('js/lib/beautify.js'); 9 | load('js/test/sanitytest.js') 10 | load('js/test/beautify-tests.js') 11 | load('js/lib/unpackers/urlencode_unpacker.js') 12 | 13 | print(run_beautifier_tests(new SanityTest(), Urlencoded, js_beautify).results_raw()) 14 | 15 | 16 | // for nodejs use this from the command line from the main directory: 17 | // node test/beautify-tests.js 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: You want new functionality added to the beautifier 4 | 5 | --- 6 | 7 | # Description 8 | 14 | 15 | # Input 16 | With this new feature, when I give like this input: 17 | ``` 18 | 19 | ``` 20 | 21 | # Expected Output 22 | I'd like to see this output: 23 | ``` 24 | 25 | ``` 26 | 27 | ## Environment 28 | OS: 29 | -------------------------------------------------------------------------------- /js/config/defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "indent_size": 4, 3 | "indent_char": " ", 4 | "indent_level": 0, 5 | "indent_with_tabs": false, 6 | "preserve_newlines": true, 7 | "max_preserve_newlines": 10, 8 | "jslint_happy": false, 9 | "space_after_named_function": false, 10 | "space_after_anon_function": false, 11 | "brace_style": "collapse", 12 | "keep_array_indentation": false, 13 | "keep_function_indentation": false, 14 | "space_before_conditional": true, 15 | "break_chained_methods": false, 16 | "eval_code": false, 17 | "unescape_strings": false, 18 | "wrap_line_length": 0, 19 | "indent_empty_lines": false, 20 | "templating": ["auto"] 21 | } 22 | -------------------------------------------------------------------------------- /jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "indent_size": 2, 3 | "indent_char": " ", 4 | "indent_level": 0, 5 | "end-with-newline": true, 6 | "indent_with_tabs": false, 7 | "preserve_newlines": true, 8 | "max_preserve_newlines": 10, 9 | "jslint_happy": false, 10 | "space_after_anon_function": false, 11 | "brace_style": "collapse,preserve-inline", 12 | "keep_array_indentation": false, 13 | "keep_function_indentation": false, 14 | "space_before_conditional": true, 15 | "break_chained_methods": false, 16 | "eval_code": false, 17 | "unescape_strings": false, 18 | "wrap_line_length": 0, 19 | "css": { 20 | "selector_separator_newline": false 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /python/setup-css.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | from cssbeautifier.__version__ import __version__ 5 | 6 | 7 | setup( 8 | name="cssbeautifier", 9 | version=__version__, 10 | description="CSS unobfuscator and beautifier.", 11 | long_description=("Beautify, unpack or deobfuscate CSS"), 12 | author="Liam Newman, Einar Lielmanis, et al.", 13 | author_email="team@beautifier.io", 14 | url="https://beautifier.io", 15 | entry_points={"console_scripts": ["css-beautify = cssbeautifier:main"]}, 16 | packages=[ 17 | "cssbeautifier", 18 | "cssbeautifier.tests", 19 | "cssbeautifier.tests.generated", 20 | "cssbeautifier.css", 21 | ], 22 | install_requires=["jsbeautifier", "six>=1.13.0", "editorconfig>=0.12.2"], 23 | license="MIT", 24 | ) 25 | -------------------------------------------------------------------------------- /tools/git-status-clear.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Post-build git status check..." 4 | echo "Ensuring no changes visible to git have been made to '$*' ..." 5 | 6 | git status $* | egrep -q 'nothing to commit.*working (directory|tree) clean' || { 7 | # we should find nothing to commit. If we don't, build has failed. 8 | echo "ERROR: Post-build git status check - FAILED." 9 | echo "Git status reported changes to non-git-ignore'd files." 10 | echo "TO REPRO: Run 'git status $*'. The use git gui or git diff to see what was changed during the build." 11 | echo "TO FIX: Amend your commit and rebuild. Repeat until git status reports no changes both before and after the build." 12 | echo "OUTPUT FOR 'git status $*':" 13 | git status $* 14 | echo "." 15 | echo "OUTPUT FOR 'git diff $*':" 16 | git diff $* | cat -t -e 17 | exit 1 18 | } 19 | echo "Post-build git status check - Succeeded." 20 | exit 0 21 | -------------------------------------------------------------------------------- /tools/npm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REL_SCRIPT_DIR="`dirname \"$0\"`" 4 | SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`" 5 | PROJECT_DIR="`( cd \"$SCRIPT_DIR/..\" && pwd )`" 6 | 7 | # if you ask for the version from this script it will force an update to the latest version 8 | if [ "$1" == "--version" ]; then 9 | echo Ensuring npm is up-to-date... 10 | /usr/bin/env npm i npm --no-save 11 | fi 12 | 13 | if [ ! -f ${PROJECT_DIR}/node_modules/.bin/npm ]; then 14 | /usr/bin/env npm i npm --no-save 15 | fi 16 | 17 | ${PROJECT_DIR}/node_modules/.bin/npm $@ 18 | 19 | # until this is fixed we ned to sed. 20 | # https://npm.community/t/some-packages-have-dist-tarball-as-http-and-not-https/285/16 21 | if [ -f package-lock.json ]; then 22 | if [ "`uname`" = "Darwin" ]; then 23 | sed -i '' -e 's/http:\/\//https:\/\//g' package-lock.json 24 | else 25 | sed -i -e 's/http:\/\//https:\/\//g' package-lock.json 26 | fi 27 | fi 28 | -------------------------------------------------------------------------------- /python/test-perf-cssbeautifier.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import io 5 | import os 6 | import copy 7 | import cssbeautifier 8 | 9 | options = cssbeautifier.default_options() 10 | options.wrap_line_length = 80 11 | data = "" 12 | 13 | 14 | def beautifier_test_github_css(): 15 | cssbeautifier.beautify(data, options) 16 | 17 | 18 | def report_perf(fn): 19 | import timeit 20 | 21 | iter = 5 22 | time = timeit.timeit( 23 | fn + "()", setup="from __main__ import " + fn + "; gc.enable()", number=iter 24 | ) 25 | print(fn + ": " + str(iter / time) + " cycles/sec") 26 | 27 | 28 | if __name__ == "__main__": 29 | dirname = os.path.dirname(os.path.abspath(__file__)) 30 | github_file = os.path.join(dirname, "../", "test/resources/github.css") 31 | data = copy.copy("".join(io.open(github_file).readlines())) 32 | 33 | # warm up 34 | beautifier_test_github_css() 35 | 36 | report_perf("beautifier_test_github_css") 37 | -------------------------------------------------------------------------------- /python/setup-js.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | from jsbeautifier.__version__ import __version__ 5 | 6 | 7 | setup( 8 | name="jsbeautifier", 9 | version=__version__, 10 | description="JavaScript unobfuscator and beautifier.", 11 | long_description=( 12 | "Beautify, unpack or deobfuscate JavaScript. " 13 | "Handles popular online obfuscators." 14 | ), 15 | author="Liam Newman, Einar Lielmanis, et al.", 16 | author_email="team@beautifier.io", 17 | url="https://beautifier.io", 18 | entry_points={"console_scripts": ["js-beautify = jsbeautifier:main"]}, 19 | packages=[ 20 | "jsbeautifier", 21 | "jsbeautifier.tests", 22 | "jsbeautifier.tests.generated", 23 | "jsbeautifier.core", 24 | "jsbeautifier.cli", 25 | "jsbeautifier.javascript", 26 | "jsbeautifier.unpackers", 27 | "jsbeautifier.unpackers.tests", 28 | ], 29 | install_requires=["six>=1.13.0", "editorconfig>=0.12.2"], 30 | license="MIT", 31 | ) 32 | -------------------------------------------------------------------------------- /python/js-beautify-profile: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # To run this: ./js-beautify/tools/python-dev python ./python/js-beautify-profile 4 | 5 | import sys 6 | import unittest 7 | import pstats 8 | from pstats import SortKey 9 | 10 | # Speedup things... 11 | try: 12 | import cProfile as profile 13 | except ImportError: 14 | import profile 15 | import os 16 | import copy 17 | import jsbeautifier 18 | options = jsbeautifier.default_options() 19 | options.wrap_line_length = 80 20 | 21 | def beautifier_test_github_min(): 22 | jsbeautifier.beautify(github_min, options) 23 | 24 | if __name__ == '__main__': 25 | dirname = os.path.dirname(os.path.abspath(__file__)) 26 | github_min_file = os.path.join( 27 | dirname, "../", "test/resources/github-min.js") 28 | github_min = copy.copy(''.join(open( github_min_file).readlines())) 29 | 30 | profile.run('beautifier_test_github_min()', 31 | os.path.join(dirname, "../", 'build/jsbstats')) 32 | 33 | p = pstats.Stats(os.path.join(dirname, "../", 'build/jsbstats')) 34 | p.strip_dirs().sort_stats(SortKey.CUMULATIVE).print_stats() 35 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | rebase-strategy: "disabled" 11 | schedule: 12 | interval: "weekly" 13 | open-pull-requests-limit: 15 14 | - package-ecosystem: "pip" # See documentation for possible values 15 | directory: "/python" # Location of package manifests 16 | rebase-strategy: "disabled" 17 | schedule: 18 | interval: "weekly" 19 | open-pull-requests-limit: 15 20 | - package-ecosystem: "github-actions" 21 | directory: "/" 22 | rebase-strategy: "disabled" 23 | schedule: 24 | # Check for updates to GitHub Actions every week 25 | interval: "weekly" 26 | open-pull-requests-limit: 15 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/README.specs.mkd: -------------------------------------------------------------------------------- 1 | # UNPACKERS SPECIFICATIONS 2 | 3 | Nothing very difficult: an unpacker is a submodule placed in the directory 4 | where this file was found. Each unpacker must define three symbols: 5 | 6 | * `PRIORITY` : integer number expressing the priority in applying this 7 | unpacker. Lower number means higher priority. 8 | Makes sense only if a source file has been packed with 9 | more than one packer. 10 | * `detect(source)` : returns `True` if source is packed, otherwise, `False`. 11 | * `unpack(source)` : takes a `source` string and unpacks it. Must always return 12 | valid JavaScript. That is to say, your code should look 13 | like: 14 | 15 | ``` 16 | if detect(source): 17 | return do_your_fancy_things_with(source) 18 | else: 19 | return source 20 | ``` 21 | 22 | *You can safely define any other symbol in your module, as it will be ignored.* 23 | 24 | `__init__` code will automatically load new unpackers, without any further step 25 | to be accomplished. Simply drop it in this directory. 26 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/urlencode.py: -------------------------------------------------------------------------------- 1 | # 2 | # Trivial bookmarklet/escaped script detector for the javascript beautifier 3 | # written by Einar Lielmanis 4 | # rewritten in Python by Stefano Sanfilippo 5 | # 6 | # Will always return valid javascript: if `detect()` is false, `code` is 7 | # returned, unmodified. 8 | # 9 | # usage: 10 | # 11 | # some_string = urlencode.unpack(some_string) 12 | # 13 | 14 | """Bookmarklet/escaped script unpacker.""" 15 | 16 | # Python 2 retrocompatibility 17 | # pylint: disable=F0401 18 | # pylint: disable=E0611 19 | try: 20 | from urllib import unquote_plus 21 | except ImportError: 22 | from urllib.parse import unquote_plus 23 | 24 | PRIORITY = 0 25 | 26 | 27 | def detect(code): 28 | """Detects if a scriptlet is urlencoded.""" 29 | # the fact that script doesn't contain any space, but has %20 instead 30 | # should be sufficient check for now. 31 | return " " not in code and ("%20" in code or code.count("%") > 3) 32 | 33 | 34 | def unpack(code): 35 | """URL decode `code` source string.""" 36 | return unquote_plus(code) if detect(code) else code 37 | -------------------------------------------------------------------------------- /js/test/node-beautify-css-perf-tests.js: -------------------------------------------------------------------------------- 1 | /*global js_beautify: true */ 2 | /*jshint node:true */ 3 | /*jshint unused:false */ 4 | 5 | 'use strict'; 6 | 7 | var fs = require('fs'), 8 | SanityTest = require('./sanitytest'), 9 | Benchmark = require('benchmark'), 10 | Urlencoded = require('../lib/unpackers/urlencode_unpacker'), 11 | beautifier = require('../src/index'); 12 | 13 | function node_beautifier_html_tests() { 14 | console.log('Testing performance...'); 15 | var github_css = fs.readFileSync(__dirname + '/../../test/resources/github.css', 'utf8'); 16 | var options = { 17 | wrap_line_length: 80 18 | }; 19 | 20 | //warm-up 21 | beautifier.css(github_css, options); 22 | 23 | var suite = new Benchmark.Suite(); 24 | 25 | suite.add("css-beautify (github.css)", function() { 26 | beautifier.css(github_css, options); 27 | }) 28 | // add listeners 29 | .on('cycle', function(event) { 30 | console.log(String(event.target)); 31 | }) 32 | .on('error', function(event) { 33 | return 1; 34 | }) 35 | .on('complete', function(event) {}) 36 | .run(); 37 | return 0; 38 | } 39 | 40 | 41 | 42 | 43 | if (require.main === module) { 44 | process.exit(node_beautifier_html_tests()); 45 | } 46 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/tests/testurlencode.py: -------------------------------------------------------------------------------- 1 | # 2 | # written by Stefano Sanfilippo 3 | # 4 | 5 | """Tests for urlencoded unpacker.""" 6 | 7 | import unittest 8 | 9 | from jsbeautifier.unpackers.urlencode import detect, unpack 10 | 11 | # pylint: disable=R0904 12 | 13 | 14 | class TestUrlencode(unittest.TestCase): 15 | """urlencode test case.""" 16 | 17 | def test_detect(self): 18 | """Test detect() function.""" 19 | 20 | def encoded(source): 21 | return self.assertTrue(detect(source)) 22 | 23 | def unencoded(source): 24 | return self.assertFalse(detect(source)) 25 | 26 | unencoded("") 27 | unencoded("var a = b") 28 | encoded("var%20a+=+b") 29 | encoded("var%20a=b") 30 | encoded("var%20%21%22") 31 | 32 | def test_unpack(self): 33 | """Test unpack function.""" 34 | 35 | def equals(source, result): 36 | return self.assertEqual(unpack(source), result) 37 | 38 | equals("", "") 39 | equals("abcd", "abcd") 40 | equals("var a = b", "var a = b") 41 | equals("var%20a=b", "var a=b") 42 | equals("var%20a+=+b", "var a = b") 43 | 44 | 45 | if __name__ == "__main__": 46 | unittest.main() 47 | -------------------------------------------------------------------------------- /js/src/css/tokenizer.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/evalbased.py: -------------------------------------------------------------------------------- 1 | # 2 | # Unpacker for eval() based packers, a part of javascript beautifier 3 | # by Einar Lielmanis 4 | # 5 | # written by Stefano Sanfilippo 6 | # 7 | # usage: 8 | # 9 | # if detect(some_string): 10 | # unpacked = unpack(some_string) 11 | # 12 | 13 | """Unpacker for eval() based packers: runs JS code and returns result. 14 | Works only if a JS interpreter (e.g. Mozilla's Rhino) is installed and 15 | properly set up on host.""" 16 | 17 | from subprocess import PIPE, Popen 18 | 19 | PRIORITY = 3 20 | 21 | 22 | def detect(source): 23 | """Detects if source is likely to be eval() packed.""" 24 | return source.strip().lower().startswith("eval(function(") 25 | 26 | 27 | def unpack(source): 28 | """Runs source and return resulting code.""" 29 | return jseval("print %s;" % source[4:]) if detect(source) else source 30 | 31 | 32 | # In case of failure, we'll just return the original, without crashing on user. 33 | 34 | 35 | def jseval(script): 36 | """Run code in the JS interpreter and return output.""" 37 | try: 38 | interpreter = Popen(["js"], stdin=PIPE, stdout=PIPE) 39 | except OSError: 40 | return script 41 | result, errors = interpreter.communicate(script) 42 | if interpreter.poll() or errors: 43 | return script 44 | return result 45 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/tests/testmyobfuscate.py: -------------------------------------------------------------------------------- 1 | # 2 | # written by Stefano Sanfilippo 3 | # 4 | 5 | """Tests for MyObfuscate unpacker.""" 6 | 7 | import unittest 8 | import os 9 | from jsbeautifier.unpackers.myobfuscate import detect, unpack 10 | from jsbeautifier.unpackers.tests import __path__ as path 11 | 12 | INPUT = os.path.join(path[0], "test-myobfuscate-input.js") 13 | OUTPUT = os.path.join(path[0], "test-myobfuscate-output.js") 14 | 15 | # pylint: disable=R0904 16 | 17 | 18 | class TestMyObfuscate(unittest.TestCase): 19 | # pylint: disable=C0103 20 | """MyObfuscate obfuscator testcase.""" 21 | 22 | @classmethod 23 | def setUpClass(cls): 24 | """Load source files (encoded and decoded version) for tests.""" 25 | with open(INPUT, "r") as data: 26 | cls.input = data.read() 27 | with open(OUTPUT, "r") as data: 28 | cls.output = data.read() 29 | 30 | def test_detect(self): 31 | """Test detect() function.""" 32 | 33 | def detected(source): 34 | return self.assertTrue(detect(source)) 35 | 36 | detected(self.input) 37 | 38 | def test_unpack(self): 39 | """Test unpack() function.""" 40 | 41 | def check(inp, out): 42 | return self.assertEqual(unpack(inp), out) 43 | 44 | check(self.input, self.output) 45 | 46 | 47 | if __name__ == "__main__": 48 | unittest.main() 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/beautification-problem.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Beautification problem 3 | about: You tried using the beautifier and the resulting format was not what you expected 4 | 5 | --- 6 | 7 | # Description 8 | 64 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | 12 | jobs: 13 | build: 14 | name: Build ${{ matrix.os }} (Node ${{ matrix.node-version }}, Python ${{ matrix.python-version }} ) 15 | runs-on: ${{ matrix.os }}-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | os: [ ubuntu, windows, macos ] 20 | python-version: [3.8, '3.10', 3.11] 21 | include: 22 | - python-version: 3.8 23 | node-version: 16 24 | - python-version: '3.10' 25 | node-version: 18 26 | - python-version: 3.11 27 | node-version: 20 28 | steps: 29 | - uses: actions/checkout@v4 30 | - name: Set up Node ${{ matrix.node-version }} 31 | uses: actions/setup-node@v4 32 | with: 33 | node-version: ${{ matrix.node-version }} 34 | - name: Set up Python ${{ matrix.python-version }} 35 | uses: actions/setup-python@v5 36 | with: 37 | python-version: ${{ matrix.python-version }} 38 | - name: Cached node_modules 39 | uses: actions/cache@v4 40 | with: 41 | path: node_modules 42 | key: ${{ runner.os }}-node-${{ hashFiles('**/package*.json') }} 43 | restore-keys: | 44 | ${{ runner.os }}-node- 45 | - name: Make all (Windows) 46 | if: matrix.os == 'windows' 47 | run: make all 48 | - name: Make CI (Non-windows) 49 | if: matrix.os != 'windows' 50 | run: make ci 51 | -------------------------------------------------------------------------------- /python/jsbeautifier/tests/testindentation.py: -------------------------------------------------------------------------------- 1 | import re 2 | import unittest 3 | import jsbeautifier 4 | 5 | 6 | class TestJSBeautifierIndentation(unittest.TestCase): 7 | def test_tabs(self): 8 | test_fragment = self.decodesto 9 | 10 | self.options.indent_with_tabs = 1 11 | test_fragment("{tabs()}", "{\n\ttabs()\n}") 12 | 13 | def test_function_indent(self): 14 | test_fragment = self.decodesto 15 | 16 | self.options.indent_with_tabs = 1 17 | self.options.keep_function_indentation = 1 18 | test_fragment( 19 | "var foo = function(){ bar() }();", "var foo = function() {\n\tbar()\n}();" 20 | ) 21 | 22 | self.options.tabs = 1 23 | self.options.keep_function_indentation = 0 24 | test_fragment( 25 | "var foo = function(){ baz() }();", "var foo = function() {\n\tbaz()\n}();" 26 | ) 27 | 28 | def decodesto(self, input, expectation=None): 29 | self.assertEqual( 30 | jsbeautifier.beautify(input, self.options), expectation or input 31 | ) 32 | 33 | @classmethod 34 | def setUpClass(cls): 35 | options = jsbeautifier.default_options() 36 | options.indent_size = 4 37 | options.indent_char = " " 38 | options.preserve_newlines = True 39 | options.jslint_happy = False 40 | options.keep_array_indentation = False 41 | options.brace_style = "collapse" 42 | options.indent_level = 0 43 | 44 | cls.options = options 45 | cls.wrapregex = re.compile("^(.+)$", re.MULTILINE) 46 | 47 | 48 | if __name__ == "__main__": 49 | unittest.main() 50 | -------------------------------------------------------------------------------- /js/test/node-beautify-perf-tests.js: -------------------------------------------------------------------------------- 1 | /*global js_beautify: true */ 2 | /*jshint node:true */ 3 | /*jshint unused:false */ 4 | 5 | 'use strict'; 6 | 7 | var fs = require('fs'), 8 | SanityTest = require('./sanitytest'), 9 | Benchmark = require('benchmark'), 10 | Urlencoded = require('../lib/unpackers/urlencode_unpacker'), 11 | beautifier = require('../src/index'); 12 | 13 | function node_beautifier_tests() { 14 | console.log('Testing performance...'); 15 | var data = fs.readFileSync(__dirname + '/../../test/resources/underscore.js', 'utf8'); 16 | var data_min = fs.readFileSync(__dirname + '/../../test/resources/underscore-min.js', 'utf8'); 17 | var github_min = fs.readFileSync(__dirname + '/../../test/resources/github-min.js', 'utf8'); 18 | var options = { 19 | wrap_line_length: 80 20 | }; 21 | 22 | //warm-up 23 | beautifier.js(data, options); 24 | beautifier.js(data_min, options); 25 | 26 | var suite = new Benchmark.Suite(); 27 | 28 | suite.add("js-beautify (underscore)", function() { 29 | beautifier.js(data, options); 30 | }) 31 | .add("js-beautify (underscore-min)", function() { 32 | beautifier.js(data_min, options); 33 | }) 34 | .add("js-beautify (github-min)", function() { 35 | beautifier.js(github_min, options); 36 | }) 37 | // add listeners 38 | .on('cycle', function(event) { 39 | console.log(String(event.target)); 40 | }) 41 | .on('error', function(event) { 42 | return 1; 43 | }) 44 | .on('complete', function(event) {}) 45 | .run(); 46 | return 0; 47 | } 48 | 49 | 50 | 51 | 52 | if (require.main === module) { 53 | process.exit(node_beautifier_tests()); 54 | } 55 | -------------------------------------------------------------------------------- /.github/workflows/pr-staging.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Release PR 5 | 6 | on: 7 | push: 8 | branches: 9 | - 'staging/gh-pages' 10 | - 'staging/main' 11 | - 'staging/release' 12 | 13 | 14 | jobs: 15 | PR-from-staging: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | - name: pull-request gh-pages 22 | if: github.ref == 'refs/heads/staging/gh-pages' 23 | uses: repo-sync/pull-request@v2 24 | with: 25 | pr_title: "Pulling staging/gh-pages into gh-pages" 26 | source_branch: "staging/gh-pages" 27 | destination_branch: "gh-pages" 28 | github_token: ${{ secrets.GITHUB_TOKEN }} 29 | - name: pull-request main 30 | if: github.ref == 'refs/heads/staging/main' 31 | uses: repo-sync/pull-request@v2 32 | with: 33 | pr_title: "Pulling staging/main into main" 34 | source_branch: "staging/main" 35 | destination_branch: "main" 36 | github_token: ${{ secrets.GITHUB_TOKEN }} 37 | - name: pull-request release 38 | if: github.ref == 'refs/heads/staging/release' 39 | uses: repo-sync/pull-request@v2 40 | with: 41 | pr_title: "Pulling staging/release into release" 42 | source_branch: "staging/release" 43 | destination_branch: "release" 44 | github_token: ${{ secrets.GITHUB_TOKEN }} 45 | -------------------------------------------------------------------------------- /js/src/css/index.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var Beautifier = require('./beautifier').Beautifier, 32 | Options = require('./options').Options; 33 | 34 | function css_beautify(source_text, options) { 35 | var beautifier = new Beautifier(source_text, options); 36 | return beautifier.beautify(); 37 | } 38 | 39 | module.exports = css_beautify; 40 | module.exports.defaultOptions = function() { 41 | return new Options(); 42 | }; 43 | -------------------------------------------------------------------------------- /js/test/node-beautify-html-perf-tests.js: -------------------------------------------------------------------------------- 1 | /*global js_beautify: true */ 2 | /*jshint node:true */ 3 | /*jshint unused:false */ 4 | 5 | 'use strict'; 6 | 7 | var fs = require('fs'), 8 | SanityTest = require('./sanitytest'), 9 | Benchmark = require('benchmark'), 10 | Urlencoded = require('../lib/unpackers/urlencode_unpacker'), 11 | beautifier = require('../src/index'); 12 | 13 | function node_beautifier_html_tests() { 14 | console.log('Testing performance...'); 15 | var github_html = fs.readFileSync(__dirname + '/../../test/resources/github.html', 'utf8'); 16 | var index_html = fs.readFileSync(__dirname + '/../../index.html', 'utf8'); 17 | var data_attr = fs.readFileSync(__dirname + '/../../test/resources/html-with-base64image.html', 'utf8'); 18 | var options = { 19 | wrap_line_length: 80 20 | }; 21 | 22 | //warm-up 23 | beautifier.html(github_html, options); 24 | beautifier.html(data_attr, options); 25 | 26 | var suite = new Benchmark.Suite(); 27 | 28 | suite.add("html-beautify (index.html)", function() { 29 | beautifier.html(index_html, options); 30 | }) 31 | .add("html-beautify (base64 image)", function() { 32 | beautifier.html(data_attr, options); 33 | }) 34 | .add("html-beautify (github.html)", function() { 35 | beautifier.html(github_html, options); 36 | }) 37 | // add listeners 38 | .on('cycle', function(event) { 39 | console.log(String(event.target)); 40 | }) 41 | .on('error', function(event) { 42 | return 1; 43 | }) 44 | .on('complete', function(event) {}) 45 | .run(); 46 | return 0; 47 | } 48 | 49 | 50 | 51 | 52 | if (require.main === module) { 53 | process.exit(node_beautifier_html_tests()); 54 | } 55 | -------------------------------------------------------------------------------- /js/src/javascript/index.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var Beautifier = require('./beautifier').Beautifier, 32 | Options = require('./options').Options; 33 | 34 | function js_beautify(js_source_text, options) { 35 | var beautifier = new Beautifier(js_source_text, options); 36 | return beautifier.beautify(); 37 | } 38 | 39 | module.exports = js_beautify; 40 | module.exports.defaultOptions = function() { 41 | return new Options(); 42 | }; 43 | -------------------------------------------------------------------------------- /python/jsbeautifier/core/token.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 4 | # 5 | # Permission is hereby granted, free of charge, to any person 6 | # obtaining a copy of this software and associated documentation files 7 | # (the "Software"), to deal in the Software without restriction, 8 | # including without limitation the rights to use, copy, modify, merge, 9 | # publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, 11 | # subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | 26 | class Token: 27 | def __init__(self, type, text, newlines=0, whitespace_before=""): 28 | self.type = type 29 | self.text = text 30 | self.comments_before = None 31 | self.newlines = newlines 32 | self.whitespace_before = whitespace_before 33 | self.parent = None 34 | self.next = None 35 | self.previous = None 36 | self.opened = None 37 | self.closed = None 38 | self.directives = None 39 | -------------------------------------------------------------------------------- /js/test/node-src-index-tests.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /*jshint node:true */ 4 | 5 | 'use strict'; 6 | 7 | var SanityTest = require('./sanitytest'), 8 | Urlencoded = require('../lib/unpackers/urlencode_unpacker'), 9 | run_javascript_tests = require('./generated/beautify-javascript-tests').run_javascript_tests, 10 | run_css_tests = require('./generated/beautify-css-tests').run_css_tests, 11 | run_html_tests = require('./generated/beautify-html-tests').run_html_tests; 12 | 13 | function test_names() { 14 | var beautify = require('../index'); 15 | var results = new SanityTest(); 16 | 17 | console.log('Ensure all expected functions are defined'); 18 | results.expect(typeof beautify.js, 'function'); 19 | results.expect(typeof beautify.css, 'function'); 20 | results.expect(typeof beautify.html, 'function'); 21 | 22 | console.log(results.results_raw()); 23 | return results.get_exitcode(); 24 | } 25 | 26 | function node_beautifier_index_tests(name, test_runner) { 27 | console.log('Testing ' + name + ' with node.js CommonJS (src/index)...'); 28 | var beautify = require('../src/index'); 29 | 30 | var results = new SanityTest(); 31 | test_runner( 32 | results, 33 | Urlencoded, 34 | beautify.js, 35 | beautify.html, 36 | beautify.css); 37 | 38 | console.log(results.results_raw()); 39 | return results.get_exitcode(); 40 | } 41 | 42 | if (require.main === module) { 43 | var exit = 0; 44 | exit = exit || test_names(); 45 | exit = exit || node_beautifier_index_tests('js-beautifier', run_javascript_tests); 46 | exit = exit || node_beautifier_index_tests('css-beautifier', run_css_tests); 47 | exit = exit || node_beautifier_index_tests('html-beautifier', run_html_tests); 48 | 49 | process.exit(exit); 50 | } 51 | -------------------------------------------------------------------------------- /js/src/html/index.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var Beautifier = require('./beautifier').Beautifier, 32 | Options = require('./options').Options; 33 | 34 | function style_html(html_source, options, js_beautify, css_beautify) { 35 | var beautifier = new Beautifier(html_source, options, js_beautify, css_beautify); 36 | return beautifier.beautify(); 37 | } 38 | 39 | module.exports = style_html; 40 | module.exports.defaultOptions = function() { 41 | return new Options(); 42 | }; 43 | -------------------------------------------------------------------------------- /web/onload.js: -------------------------------------------------------------------------------- 1 | /*jshint node:false, jquery:true, strict:false */ 2 | $(function() { 3 | 4 | read_settings_from_cookie(); 5 | $.getJSON("./package.json", function(data) { 6 | $('#version-number').text('(v' + data.version + ')'); 7 | }); 8 | 9 | var default_text = 10 | "// This is just a sample script. Paste your real code (javascript or HTML) here.\n\nif ('this_is'==/an_example/){of_beautifier();}else{var a=b?(c%d):e[f];}"; 11 | var textArea = $('#source')[0]; 12 | $('#source').val(default_text); 13 | 14 | if (the.use_codemirror && typeof CodeMirror !== 'undefined') { 15 | 16 | the.editor = CodeMirror.fromTextArea(textArea, { 17 | lineNumbers: true 18 | }); 19 | 20 | set_editor_mode(); 21 | the.editor.focus(); 22 | 23 | $('.CodeMirror').click(function() { 24 | if (the.editor.getValue() === default_text) { 25 | the.editor.setValue(''); 26 | } 27 | }); 28 | } else { 29 | $('#source').bind('click focus', function() { 30 | if ($(this).val() === default_text) { 31 | $(this).val(''); 32 | } 33 | }).bind('blur', function() { 34 | if (!$(this).val()) { 35 | $(this).val(default_text); 36 | } 37 | }); 38 | } 39 | 40 | setPreferredColorScheme(); 41 | 42 | $(window).bind('keydown', function(e) { 43 | if ((e.ctrlKey || e.metaKey) && e.keyCode === 13) { 44 | beautify(); 45 | } 46 | }); 47 | 48 | if (typeof window.navigator !== "undefined" && typeof window.navigator.platform === "string" && window.navigator.platform.includes("Mac")) { 49 | $(".submit em").text("(cmd-enter)"); 50 | } 51 | 52 | $('.submit').click(beautify); 53 | $('select').change(beautify); 54 | $(':checkbox').change(beautify); 55 | $('#additional-options').change(beautify); 56 | 57 | 58 | 59 | 60 | }); 61 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | var legacy = { 4 | mode: 'none', 5 | entry: { 6 | beautify_js: './js/src/javascript/index.js', 7 | beautify_css: './js/src/css/index.js', 8 | beautify_html: './js/src/html/index.js' 9 | }, 10 | resolve: { 11 | modules: [ path.resolve(__dirname, "js/src") ] 12 | }, 13 | output: { 14 | library: 'legacy_[name]', 15 | filename: 'legacy_[name].js', 16 | path: path.resolve(__dirname, 'build/legacy') 17 | } 18 | }; 19 | 20 | var dist_full = { 21 | entry: './js/src/index.js', 22 | mode: 'none', 23 | resolve: { 24 | modules: [ path.resolve(__dirname, "js/src") ] 25 | }, 26 | devtool: 'source-map', 27 | output: { 28 | library: 'beautifier', 29 | libraryTarget: 'umd', 30 | umdNamedDefine: true, 31 | filename: 'beautifier.js', 32 | path: path.resolve(__dirname, 'js/lib'), 33 | // Workaround for https://github.com/webpack/webpack/issues/6525 34 | globalObject: "typeof self !== 'undefined' ? self : typeof windows !== 'undefined' ? window : typeof global !== 'undefined' ? global : this" 35 | } 36 | }; 37 | 38 | var dist_prod = { 39 | entry: './js/src/index.js', 40 | mode: 'production', 41 | resolve: { 42 | modules: [ path.resolve(__dirname, "js/src") ] 43 | }, 44 | devtool: 'source-map', 45 | output: { 46 | library: 'beautifier', 47 | libraryTarget: 'umd', 48 | umdNamedDefine: true, 49 | filename: 'beautifier.min.js', 50 | path: path.resolve(__dirname, 'js/lib'), 51 | // Workaround for https://github.com/webpack/webpack/issues/6525 52 | globalObject: "typeof self !== 'undefined' ? self : typeof windows !== 'undefined' ? window : typeof global !== 'undefined' ? global : this" 53 | } 54 | }; 55 | 56 | 57 | module.exports = [dist_full, dist_prod, legacy]; 58 | -------------------------------------------------------------------------------- /web/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /js/src/index.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var js_beautify = require('./javascript/index'); 32 | var css_beautify = require('./css/index'); 33 | var html_beautify = require('./html/index'); 34 | 35 | function style_html(html_source, options, js, css) { 36 | js = js || js_beautify; 37 | css = css || css_beautify; 38 | return html_beautify(html_source, options, js, css); 39 | } 40 | style_html.defaultOptions = html_beautify.defaultOptions; 41 | 42 | module.exports.js = js_beautify; 43 | module.exports.css = css_beautify; 44 | module.exports.html = style_html; 45 | -------------------------------------------------------------------------------- /python/test-perf-jsbeautifier.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import io 5 | import os 6 | import copy 7 | import jsbeautifier 8 | 9 | options = jsbeautifier.default_options() 10 | options.wrap_line_length = 80 11 | data = "" 12 | data_min = "" 13 | 14 | 15 | def beautifier_test_underscore(): 16 | jsbeautifier.beautify(data, options) 17 | 18 | 19 | def beautifier_test_underscore_min(): 20 | jsbeautifier.beautify(data_min, options) 21 | 22 | 23 | def beautifier_test_github_min(): 24 | jsbeautifier.beautify(github_min, options) 25 | 26 | 27 | def report_perf(fn): 28 | import timeit 29 | 30 | iter = 5 31 | time = timeit.timeit( 32 | fn + "()", setup="from __main__ import " + fn + "; gc.enable()", number=iter 33 | ) 34 | print(fn + ": " + str(iter / time) + " cycles/sec") 35 | 36 | 37 | if __name__ == "__main__": 38 | dirname = os.path.dirname(os.path.abspath(__file__)) 39 | underscore_file = os.path.join(dirname, "../", "test/resources/underscore.js") 40 | underscore_min_file = os.path.join( 41 | dirname, "../", "test/resources/underscore-min.js" 42 | ) 43 | github_min_file = os.path.join(dirname, "../", "test/resources/github-min.js") 44 | data = copy.copy("".join(io.open(underscore_file, encoding="UTF-8").readlines())) 45 | data_min = copy.copy( 46 | "".join(io.open(underscore_min_file, encoding="UTF-8").readlines()) 47 | ) 48 | github_min = copy.copy( 49 | "".join(io.open(github_min_file, encoding="UTF-8").readlines()) 50 | ) 51 | 52 | # warm up 53 | beautifier_test_underscore() 54 | beautifier_test_underscore_min() 55 | beautifier_test_github_min() 56 | 57 | report_perf("beautifier_test_underscore") 58 | report_perf("beautifier_test_underscore_min") 59 | report_perf("beautifier_test_github_min") 60 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/tests/testjavascriptobfuscator.py: -------------------------------------------------------------------------------- 1 | # 2 | # written by Stefano Sanfilippo 3 | # 4 | 5 | """Tests for JavaScriptObfuscator unpacker.""" 6 | 7 | import unittest 8 | from jsbeautifier.unpackers.javascriptobfuscator import unpack, detect, smartsplit 9 | 10 | # pylint: disable=R0904 11 | 12 | 13 | class TestJavascriptObfuscator(unittest.TestCase): 14 | """JavascriptObfuscator.com test case.""" 15 | 16 | def test_smartsplit(self): 17 | """Test smartsplit() function.""" 18 | split = smartsplit 19 | 20 | def equals(data, result): 21 | return self.assertEqual(split(data), result) 22 | 23 | equals("", []) 24 | equals('"a", "b"', ['"a"', '"b"']) 25 | equals('"aaa","bbbb"', ['"aaa"', '"bbbb"']) 26 | equals('"a", "b\\""', ['"a"', '"b\\""']) 27 | 28 | def test_detect(self): 29 | """Test detect() function.""" 30 | 31 | def positive(source): 32 | return self.assertTrue(detect(source)) 33 | 34 | def negative(source): 35 | return self.assertFalse(detect(source)) 36 | 37 | negative("") 38 | negative("abcd") 39 | negative("var _0xaaaa") 40 | positive('var _0xaaaa = ["a", "b"]') 41 | positive('var _0xaaaa=["a", "b"]') 42 | positive('var _0x1234=["a","b"]') 43 | 44 | def test_unpack(self): 45 | """Test unpack() function.""" 46 | 47 | def decodeto(ob, original): 48 | return self.assertEqual(unpack(ob), original) 49 | 50 | decodeto("var _0x8df3=[];var a=10;", "var a=10;") 51 | decodeto( 52 | 'var _0xb2a7=["\x74\x27\x65\x73\x74"];var i;for(i=0;i<10;++i)' 53 | "{alert(_0xb2a7[0]);} ;", 54 | "var i;for(i=0;i<10;++i){alert" '("t\'est");} ;', 55 | ) 56 | 57 | 58 | if __name__ == "__main__": 59 | unittest.main() 60 | -------------------------------------------------------------------------------- /test/data/javascript/inputlib.js: -------------------------------------------------------------------------------- 1 | //--------// 2 | // Inputs // 3 | //--------// 4 | 5 | var operator_position = { 6 | sanity: [ 7 | 'var res = a + b - c / d * e % f;', 8 | 'var res = g & h | i ^ j |> console.log;', 9 | 'var res = (k && l || m) ? n ?? nn : o;', 10 | 'var res = p >> q << r >>> s;', 11 | 'var res = t === u !== v != w == x >= y <= z > aa < ab;', 12 | 'res ??= a;', 13 | 'res ||= b;', 14 | 'res &&= c;', 15 | 'ac + -ad' 16 | ], 17 | comprehensive: [ 18 | 'var res = a + b', 19 | '- c /', 20 | 'd * e', 21 | '%', 22 | 'f;', 23 | ' var res = g & h', 24 | '| i ^', 25 | 'j', 26 | '|> console.log;', 27 | 'var res = (k &&', 28 | 'l', 29 | '|| m) ?', 30 | 'n', 31 | '?? nn', 32 | ': o', 33 | ';', 34 | 'var res = p', 35 | '>> q <<', 36 | 'r', 37 | '>>> s;', 38 | 'var res', 39 | ' = t', 40 | '', 41 | ' === u !== v', 42 | ' !=', 43 | 'w', 44 | '== x >=', 45 | 'y <= z > aa <', 46 | 'ab;', 47 | 'res??=a;res||=b;res&&=c;', 48 | 'ac +', 49 | '-ad' 50 | ], 51 | colon_special_case: [ 52 | 'var a = {', 53 | ' b', 54 | ': bval,', 55 | ' c:', 56 | 'cval', 57 | ' ,d: dval', 58 | '};', 59 | 'var e = f ? g', 60 | ': h;', 61 | 'var i = j ? k :', 62 | 'l;' 63 | ], 64 | catch_all: [ 65 | 'var d = 1;', 66 | 'if (a === b', 67 | ' && c) {', 68 | ' d = (c * everything', 69 | ' / something_else) %', 70 | ' b;', 71 | ' e', 72 | ' += d;', 73 | '', 74 | '} else if (!(complex && simple) ||', 75 | ' (emotion && emotion.name === "happy")) {', 76 | ' cryTearsOfJoy(many ||', 77 | ' anOcean', 78 | ' || aRiver);', 79 | '}' 80 | ] 81 | }; 82 | 83 | 84 | //---------// 85 | // Exports // 86 | //---------// 87 | 88 | module.exports = { 89 | operator_position: operator_position 90 | }; 91 | -------------------------------------------------------------------------------- /tools/generate-changelog.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REL_SCRIPT_DIR="`dirname \"$0\"`" 4 | SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`" 5 | 6 | # based on https://gist.github.com/joelittlejohn/5937573 7 | # 8 | 9 | main() 10 | { 11 | cd $SCRIPT_DIR/.. 12 | 13 | if [ "$#" -ne 2 ]; then 14 | echo "Usage: ./generate-changelog.sh user/repo " 15 | exit 1 16 | fi 17 | 18 | 19 | local JQ 20 | JQ=$(command -v jq) || { 21 | echo "Required tool 'jq' missing. Failed." 22 | exit 1 23 | } 24 | 25 | local GSORT 26 | GSORT=$(command -v gsort || command -v sort) || { 27 | echo "Required tool 'GNU sort' missing. Failed." 28 | exit 1 29 | } 30 | 31 | IFS=$'\n' 32 | echo "# Changelog" > CHANGELOG.md 33 | 34 | for m in $( 35 | curl -s -H "Authorization: token $2" "https://api.github.com/repos/$1/milestones?state=closed&per_page=100" \ 36 | | jq -c '.[] | [.title, .number]' \ 37 | | sed 's/-/\!/' | sort -rV | sed 's/\!/-/' # sed trick to put -alpha, -beta, etc earlier than GA release 38 | ); do 39 | 40 | echo "Processing milestone: $title..." 41 | echo $m | sed 's/\["\(.*\)",.*\]/\n## \1/' >> CHANGELOG.md 42 | mid=$(echo $m | sed 's/.*,\(.*\)]/\1/') 43 | 44 | for i in $(curl -s -H "Authorization: token $2" "https://api.github.com/repos/$1/issues?milestone=$mid&state=closed" | jq -r '.[] | [.html_url, .number, .title, (.labels[] | select(.name == "breaking") | .name)] | @tsv'); do 45 | if [ "$(echo "$i" | cut -f 4)" = "breaking" ]; then 46 | echo "* **$(echo "$i" | cut -f 3 | sed 's/_/\\_/g') ([#$(echo "$i" | cut -f 2)]($(echo "$i" | cut -f 1)))**" >> CHANGELOG.md 47 | else 48 | echo "* $(echo "$i" | cut -f 3 | sed 's/_/\\_/g') ([#$(echo "$i" | cut -f 2)]($(echo "$i" | cut -f 1)))" >> CHANGELOG.md 49 | fi 50 | done 51 | done 52 | 53 | git commit -am "Update Changelog" 54 | } 55 | 56 | (main $*) 57 | -------------------------------------------------------------------------------- /js/src/core/token.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | 31 | function Token(type, text, newlines, whitespace_before) { 32 | this.type = type; 33 | this.text = text; 34 | 35 | // comments_before are 36 | // comments that have a new line before them 37 | // and may or may not have a newline after 38 | // this is a set of comments before 39 | this.comments_before = null; /* inline comment*/ 40 | 41 | 42 | // this.comments_after = new TokenStream(); // no new line before and newline after 43 | this.newlines = newlines || 0; 44 | this.whitespace_before = whitespace_before || ''; 45 | this.parent = null; 46 | this.next = null; 47 | this.previous = null; 48 | this.opened = null; 49 | this.closed = null; 50 | this.directives = null; 51 | } 52 | 53 | 54 | module.exports.Token = Token; 55 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/javascriptobfuscator.py: -------------------------------------------------------------------------------- 1 | # 2 | # simple unpacker/deobfuscator for scripts messed up with 3 | # javascriptobfuscator.com 4 | # 5 | # written by Einar Lielmanis 6 | # rewritten in Python by Stefano Sanfilippo 7 | # 8 | # Will always return valid javascript: if `detect()` is false, `code` is 9 | # returned, unmodified. 10 | # 11 | # usage: 12 | # 13 | # if javascriptobfuscator.detect(some_string): 14 | # some_string = javascriptobfuscator.unpack(some_string) 15 | # 16 | 17 | """deobfuscator for scripts messed up with JavascriptObfuscator.com""" 18 | 19 | import re 20 | 21 | PRIORITY = 1 22 | 23 | 24 | def smartsplit(code): 25 | """Split `code` at " symbol, only if it is not escaped.""" 26 | strings = [] 27 | pos = 0 28 | while pos < len(code): 29 | if code[pos] == '"': 30 | word = "" # new word 31 | pos += 1 32 | while pos < len(code): 33 | if code[pos] == '"': 34 | break 35 | if code[pos] == "\\": 36 | word += "\\" 37 | pos += 1 38 | word += code[pos] 39 | pos += 1 40 | strings.append('"%s"' % word) 41 | pos += 1 42 | return strings 43 | 44 | 45 | def detect(code): 46 | """Detects if `code` is JavascriptObfuscator.com packed.""" 47 | # prefer `is not` idiom, so that a true boolean is returned 48 | return re.search(r"^var _0x[a-f0-9]+ ?\= ?\[", code) is not None 49 | 50 | 51 | def unpack(code): 52 | """Unpacks JavascriptObfuscator.com packed code.""" 53 | if detect(code): 54 | matches = re.search(r"var (_0x[a-f\d]+) ?\= ?\[(.*?)\];", code) 55 | if matches: 56 | variable = matches.group(1) 57 | dictionary = smartsplit(matches.group(2)) 58 | code = code[len(matches.group(0)) :] 59 | for key, value in enumerate(dictionary): 60 | code = code.replace(r"%s[%s]" % (variable, key), value) 61 | return code 62 | -------------------------------------------------------------------------------- /python/cssbeautifier/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License (MIT) 3 | 4 | # Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 5 | 6 | # Permission is hereby granted, free of charge, to any person 7 | # obtaining a copy of this software and associated documentation files 8 | # (the "Software"), to deal in the Software without restriction, 9 | # including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, 11 | # and to permit persons to whom the Software is furnished to do so, 12 | # subject to the following conditions: 13 | 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | 26 | import sys 27 | 28 | 29 | def default_options(): 30 | _main = __import__("cssbeautifier", globals(), locals(), ["_main"])._main 31 | return _main.default_options() 32 | 33 | 34 | def beautify(string, opts=None): 35 | _main = __import__("cssbeautifier", globals(), locals(), ["_main"])._main 36 | return _main.beautify(string, opts) 37 | 38 | 39 | def beautify_file(file_name, opts=None): 40 | _main = __import__("cssbeautifier", globals(), locals(), ["_main"])._main 41 | return _main.beautify_file(file_name, opts) 42 | 43 | 44 | def usage(stream=sys.stdout): 45 | _main = __import__("cssbeautifier", globals(), locals(), ["_main"])._main 46 | return _main._sage(stream) 47 | 48 | 49 | def main(): 50 | _main = __import__("cssbeautifier", globals(), locals(), ["_main"])._main 51 | return _main.main() 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-beautify", 3 | "version": "1.15.4", 4 | "description": "beautifier.io for node", 5 | "main": "js/index.js", 6 | "bin": { 7 | "css-beautify": "./js/bin/css-beautify.js", 8 | "html-beautify": "./js/bin/html-beautify.js", 9 | "js-beautify": "./js/bin/js-beautify.js" 10 | }, 11 | "directories": { 12 | "lib": "js/lib", 13 | "test": "js/test" 14 | }, 15 | "files": [ 16 | "js/bin/", 17 | "js/lib/*.js", 18 | "js/lib/unpackers/", 19 | "js/index.js", 20 | "js/src/" 21 | ], 22 | "scripts": {}, 23 | "bugs": "https://github.com/beautifier/js-beautify/issues", 24 | "homepage": "https://beautifier.io/", 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/beautifier/js-beautify.git" 28 | }, 29 | "keywords": [ 30 | "beautify", 31 | "beautifier", 32 | "code-quality" 33 | ], 34 | "author": "Einar Lielmanis ", 35 | "contributors": [ 36 | "Vital Batmanov ", 37 | "Chris J. Shull ", 38 | "Gian Marco Gherardi ", 39 | "Stan ", 40 | "Vittorio Gambaletta ", 41 | "Daniel Stockman ", 42 | "Harutyun Amirjanyan ", 43 | "Nochum Sossonko ", 44 | "Liam Newman " 45 | ], 46 | "license": "MIT", 47 | "engines": { 48 | "node": ">=14" 49 | }, 50 | "browserslist": "ie 11", 51 | "dependencies": { 52 | "config-chain": "^1.1.13", 53 | "editorconfig": "^1.0.4", 54 | "glob": "^10.4.2", 55 | "js-cookie": "^3.0.5", 56 | "nopt": "^7.2.1" 57 | }, 58 | "devDependencies": { 59 | "ansi-regex": "^6.0.1", 60 | "benchmark": "^2.1.4", 61 | "codemirror": "^5.65.16", 62 | "jquery": "^3.6.4", 63 | "jshint": "^2.13.6", 64 | "minimist": "^1.2.8", 65 | "mocha": "^11.0.1", 66 | "mustache": "^4.2.0", 67 | "requirejs": "^2.3.6", 68 | "serve": "^14.2.0", 69 | "strip-ansi": "^7.0.1", 70 | "webpack": "^5.81.0", 71 | "webpack-cli": "^6.0.1" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /js/test/amd-beautify-tests.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | 3 | 'use strict'; 4 | 5 | var requirejs = require('requirejs'), 6 | SanityTest = require('./sanitytest'), 7 | Urlencoded = require('../lib/unpackers/urlencode_unpacker'), 8 | run_javascript_tests = require('./generated/beautify-javascript-tests').run_javascript_tests, 9 | run_css_tests = require('./generated/beautify-css-tests').run_css_tests, 10 | run_html_tests = require('./generated/beautify-html-tests').run_html_tests; 11 | 12 | requirejs.config({ 13 | paths: { 14 | 'beautify': "..", 15 | 'beautify-lib': "../lib" 16 | } 17 | }); 18 | 19 | function amd_beautifier_index_tests(name, test_runner) { 20 | console.log('Testing ' + name + ' with node.js Require.js (index)...'); 21 | var results = new SanityTest(); 22 | var beautify = requirejs('beautify/index'); 23 | 24 | test_runner( 25 | results, 26 | Urlencoded, 27 | beautify.js, 28 | beautify.html, 29 | beautify.css); 30 | 31 | console.log(results.results_raw()); 32 | return results.get_exitcode(); 33 | } 34 | 35 | function amd_beautifier_bundle_tests(name, test_runner) { 36 | console.log('Testing ' + name + ' with node.js Require.js (bundle)...'); 37 | var results = new SanityTest(); 38 | var js_beautify = requirejs('beautify-lib/beautify'), 39 | css_beautify = requirejs('beautify-lib/beautify-css'), 40 | html_beautify = requirejs('beautify-lib/beautify-html'); 41 | 42 | test_runner( 43 | results, 44 | Urlencoded, 45 | js_beautify.js_beautify, 46 | html_beautify.html_beautify, 47 | css_beautify.css_beautify); 48 | 49 | console.log(results.results_raw()); 50 | return results.get_exitcode(); 51 | } 52 | 53 | 54 | 55 | if (require.main === module) { 56 | var exit = 0; 57 | exit = exit || amd_beautifier_bundle_tests('js-beautifier', run_javascript_tests); 58 | exit = exit || amd_beautifier_bundle_tests('cs-beautifier', run_css_tests); 59 | exit = exit || amd_beautifier_bundle_tests('html-beautifier', run_html_tests); 60 | exit = exit || amd_beautifier_index_tests('js-beautifier', run_javascript_tests); 61 | exit = exit || amd_beautifier_index_tests('css-beautifier', run_css_tests); 62 | exit = exit || amd_beautifier_index_tests('html-beautifier', run_html_tests); 63 | 64 | process.exit(exit); 65 | } 66 | -------------------------------------------------------------------------------- /tools/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REL_SCRIPT_DIR="`dirname \"$0\"`" 4 | SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`" 5 | PROJECT_DIR="`( cd \"$SCRIPT_DIR/..\" && pwd )`" 6 | 7 | build_js() 8 | { 9 | echo Building javascript... 10 | cd $PROJECT_DIR 11 | 12 | # jshint 13 | $PROJECT_DIR/node_modules/.bin/jshint . || exit 1 14 | 15 | # generate lib files 16 | $PROJECT_DIR/node_modules/.bin/webpack || exit 1 17 | 18 | mkdir -p ./js/lib/unpackers 19 | cp -r ./js/src/unpackers ./js/lib/ 20 | cp ./js/src/cli.js ./js/lib/ 21 | 22 | # Wrap webkit output into an non-breaking form. 23 | # In an upcoming verion these will be replaced with standard webpack umd 24 | sed '/GENERATED_BUILD_OUTPUT/ r ./build/legacy/legacy_beautify_js.js' <./tools/template/beautify.wrapper.js >./js/lib/beautify.js || exit 1 25 | sed '/GENERATED_BUILD_OUTPUT/ r ./build/legacy/legacy_beautify_css.js' <./tools/template/beautify-css.wrapper.js >./js/lib/beautify-css.js || exit 1 26 | sed '/GENERATED_BUILD_OUTPUT/ r ./build/legacy/legacy_beautify_html.js' <./tools/template/beautify-html.wrapper.js >./js/lib/beautify-html.js || exit 1 27 | } 28 | 29 | build_beautify() 30 | { 31 | cd $PROJECT_DIR 32 | # beautify test and data 33 | $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/test/*.js || exit 1 34 | $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/test/core/*.js || exit 1 35 | $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/test/data/**/*.js || exit 1 36 | 37 | # beautify product code 38 | $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/src/**/*.js || exit 1 39 | $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/web/*.js || exit 1 40 | 41 | $PROJECT_DIR/js/bin/css-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/web/common-style.css || exit 1 42 | 43 | $PROJECT_DIR/js/bin/html-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r index.html 44 | 45 | build_js 46 | } 47 | 48 | main() { 49 | cd $PROJECT_DIR 50 | local ACTION 51 | ACTION=build_${1:-full} 52 | if [ -n "$(type -t $ACTION)" ] && [ "$(type -t $ACTION)" = "function" ]; then 53 | $ACTION 54 | else 55 | build_help 56 | fi 57 | 58 | } 59 | 60 | (main $*) 61 | -------------------------------------------------------------------------------- /python/jsbeautifier/core/directives.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 4 | # 5 | # Permission is hereby granted, free of charge, to any person 6 | # obtaining a copy of this software and associated documentation files 7 | # (the "Software"), to deal in the Software without restriction, 8 | # including without limitation the rights to use, copy, modify, merge, 9 | # publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, 11 | # subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | import re 26 | 27 | 28 | class Directives: 29 | def __init__(self, start_block_pattern, end_block_pattern): 30 | self.__directives_block_pattern = re.compile( 31 | start_block_pattern + r" beautify( \w+[:]\w+)+ " + end_block_pattern 32 | ) 33 | self.__directive_pattern = re.compile(r" (\w+)[:](\w+)") 34 | 35 | self.__directives_end_ignore_pattern = re.compile( 36 | start_block_pattern + r"\sbeautify\signore:end\s" + end_block_pattern 37 | ) 38 | 39 | def get_directives(self, text): 40 | if not self.__directives_block_pattern.match(text): 41 | return None 42 | 43 | directives = {} 44 | directive_match = self.__directive_pattern.search(text) 45 | 46 | while directive_match: 47 | directives[directive_match.group(1)] = directive_match.group(2) 48 | directive_match = self.__directive_pattern.search( 49 | text, directive_match.end() 50 | ) 51 | 52 | return directives 53 | 54 | def readIgnored(self, input): 55 | return input.readUntilAfter(self.__directives_end_ignore_pattern) 56 | -------------------------------------------------------------------------------- /js/src/css/options.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var BaseOptions = require('../core/options').Options; 32 | 33 | function Options(options) { 34 | BaseOptions.call(this, options, 'css'); 35 | 36 | this.selector_separator_newline = this._get_boolean('selector_separator_newline', true); 37 | this.newline_between_rules = this._get_boolean('newline_between_rules', true); 38 | var space_around_selector_separator = this._get_boolean('space_around_selector_separator'); 39 | this.space_around_combinator = this._get_boolean('space_around_combinator') || space_around_selector_separator; 40 | 41 | var brace_style_split = this._get_selection_list('brace_style', ['collapse', 'expand', 'end-expand', 'none', 'preserve-inline']); 42 | this.brace_style = 'collapse'; 43 | for (var bs = 0; bs < brace_style_split.length; bs++) { 44 | if (brace_style_split[bs] !== 'expand') { 45 | // default to collapse, as only collapse|expand is implemented for now 46 | this.brace_style = 'collapse'; 47 | } else { 48 | this.brace_style = brace_style_split[bs]; 49 | } 50 | } 51 | } 52 | Options.prototype = new BaseOptions(); 53 | 54 | 55 | 56 | module.exports.Options = Options; 57 | -------------------------------------------------------------------------------- /js/src/core/tokenstream.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | 31 | function TokenStream(parent_token) { 32 | // private 33 | this.__tokens = []; 34 | this.__tokens_length = this.__tokens.length; 35 | this.__position = 0; 36 | this.__parent_token = parent_token; 37 | } 38 | 39 | TokenStream.prototype.restart = function() { 40 | this.__position = 0; 41 | }; 42 | 43 | TokenStream.prototype.isEmpty = function() { 44 | return this.__tokens_length === 0; 45 | }; 46 | 47 | TokenStream.prototype.hasNext = function() { 48 | return this.__position < this.__tokens_length; 49 | }; 50 | 51 | TokenStream.prototype.next = function() { 52 | var val = null; 53 | if (this.hasNext()) { 54 | val = this.__tokens[this.__position]; 55 | this.__position += 1; 56 | } 57 | return val; 58 | }; 59 | 60 | TokenStream.prototype.peek = function(index) { 61 | var val = null; 62 | index = index || 0; 63 | index += this.__position; 64 | if (index >= 0 && index < this.__tokens_length) { 65 | val = this.__tokens[index]; 66 | } 67 | return val; 68 | }; 69 | 70 | TokenStream.prototype.add = function(token) { 71 | if (this.__parent_token) { 72 | token.parent = this.__parent_token; 73 | } 74 | this.__tokens.push(token); 75 | this.__tokens_length += 1; 76 | }; 77 | 78 | module.exports.TokenStream = TokenStream; 79 | -------------------------------------------------------------------------------- /python/cssbeautifier/css/options.py: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License (MIT) 3 | 4 | # Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 5 | 6 | # Permission is hereby granted, free of charge, to any person 7 | # obtaining a copy of this software and associated documentation files 8 | # (the "Software"), to deal in the Software without restriction, 9 | # including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, 11 | # and to permit persons to whom the Software is furnished to do so, 12 | # subject to the following conditions: 13 | 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | 26 | from jsbeautifier.core.options import Options as BaseOptions 27 | 28 | 29 | class BeautifierOptions(BaseOptions): 30 | def __init__(self, options=None): 31 | BaseOptions.__init__(self, options, "css") 32 | 33 | self.selector_separator_newline = self._get_boolean( 34 | "selector_separator_newline", True 35 | ) 36 | self.newline_between_rules = self._get_boolean("newline_between_rules", True) 37 | 38 | brace_style_split = self._get_selection_list( 39 | "brace_style", 40 | ["collapse", "expand", "end-expand", "none", "preserve-inline"], 41 | ) 42 | self.brace_style = "collapse" 43 | for bs in brace_style_split: 44 | if bs != "expand": 45 | # default to collapse, as only collapse|expand is implemented for now 46 | self.brace_style = "collapse" 47 | else: 48 | self.brace_style = bs 49 | 50 | # deprecated 51 | space_around_selector_separator = self._get_boolean( 52 | "space_around_selector_separator" 53 | ) 54 | 55 | # Continue to accept deprecated option 56 | self.space_around_combinator = ( 57 | self._get_boolean("space_around_combinator") 58 | or space_around_selector_separator 59 | ) 60 | self.keep_quiet = False 61 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # General code for JSBeautifier unpackers infrastructure. See README.specs 3 | # written by Stefano Sanfilippo 4 | # 5 | 6 | """General code for JSBeautifier unpackers infrastructure.""" 7 | 8 | import pkgutil 9 | import re 10 | from jsbeautifier.unpackers import evalbased 11 | 12 | # NOTE: AT THE MOMENT, IT IS DEACTIVATED FOR YOUR SECURITY: it runs js! 13 | BLACKLIST = ["jsbeautifier.unpackers.evalbased"] 14 | 15 | 16 | class UnpackingError(Exception): 17 | """Badly packed source or general error. Argument is a 18 | meaningful description.""" 19 | 20 | pass 21 | 22 | 23 | def getunpackers(): 24 | """Scans the unpackers dir, finds unpackers and add them to UNPACKERS list. 25 | An unpacker will be loaded only if it is a valid python module (name must 26 | adhere to naming conventions) and it is not blacklisted (i.e. inserted 27 | into BLACKLIST.""" 28 | path = __path__ 29 | prefix = __name__ + "." 30 | unpackers = [] 31 | interface = ["unpack", "detect", "PRIORITY"] 32 | for _importer, modname, _ispkg in pkgutil.iter_modules(path, prefix): 33 | if "tests" not in modname and modname not in BLACKLIST: 34 | try: 35 | module = __import__(modname, fromlist=interface) 36 | except ImportError: 37 | raise UnpackingError("Bad unpacker: %s" % modname) 38 | else: 39 | unpackers.append(module) 40 | 41 | return sorted(unpackers, key=lambda mod: mod.PRIORITY) 42 | 43 | 44 | UNPACKERS = getunpackers() 45 | 46 | 47 | def run(source, evalcode=False): 48 | """Runs the applicable unpackers and return unpacked source as a string.""" 49 | for unpacker in [mod for mod in UNPACKERS if mod.detect(source)]: 50 | source = unpacker.unpack(source) 51 | if evalcode and evalbased.detect(source): 52 | source = evalbased.unpack(source) 53 | return source 54 | 55 | 56 | def filtercomments(source): 57 | """NOT USED: strips trailing comments and put them at the top.""" 58 | trailing_comments = [] 59 | comment = True 60 | 61 | while comment: 62 | if re.search(r"^\s*\/\*", source): 63 | comment = source[0, source.index("*/") + 2] 64 | elif re.search(r"^\s*\/\/", source): 65 | comment = re.search(r"^\s*\/\/", source).group(0) 66 | else: 67 | comment = None 68 | 69 | if comment: 70 | source = re.sub(r"^\s+", "", source[len(comment) :]) 71 | trailing_comments.append(comment) 72 | 73 | return "\n".join(trailing_comments) + source 74 | -------------------------------------------------------------------------------- /js/src/core/directives.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | 31 | function Directives(start_block_pattern, end_block_pattern) { 32 | start_block_pattern = typeof start_block_pattern === 'string' ? start_block_pattern : start_block_pattern.source; 33 | end_block_pattern = typeof end_block_pattern === 'string' ? end_block_pattern : end_block_pattern.source; 34 | this.__directives_block_pattern = new RegExp(start_block_pattern + / beautify( \w+[:]\w+)+ /.source + end_block_pattern, 'g'); 35 | this.__directive_pattern = / (\w+)[:](\w+)/g; 36 | 37 | this.__directives_end_ignore_pattern = new RegExp(start_block_pattern + /\sbeautify\signore:end\s/.source + end_block_pattern, 'g'); 38 | } 39 | 40 | Directives.prototype.get_directives = function(text) { 41 | if (!text.match(this.__directives_block_pattern)) { 42 | return null; 43 | } 44 | 45 | var directives = {}; 46 | this.__directive_pattern.lastIndex = 0; 47 | var directive_match = this.__directive_pattern.exec(text); 48 | 49 | while (directive_match) { 50 | directives[directive_match[1]] = directive_match[2]; 51 | directive_match = this.__directive_pattern.exec(text); 52 | } 53 | 54 | return directives; 55 | }; 56 | 57 | Directives.prototype.readIgnored = function(input) { 58 | return input.readUntilAfter(this.__directives_end_ignore_pattern); 59 | }; 60 | 61 | 62 | module.exports.Directives = Directives; 63 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main, release, staging/* ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '24 6 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript', 'python' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v4 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v3 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v3 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v3 68 | -------------------------------------------------------------------------------- /python/jsbeautifier/core/tokenstream.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 4 | # 5 | # Permission is hereby granted, free of charge, to any person 6 | # obtaining a copy of this software and associated documentation files 7 | # (the "Software"), to deal in the Software without restriction, 8 | # including without limitation the rights to use, copy, modify, merge, 9 | # publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, 11 | # subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | import re 26 | from ..core.inputscanner import InputScanner 27 | from ..core.token import Token 28 | 29 | 30 | class TokenStream: 31 | def __init__(self, parent_token=None): 32 | self.__tokens = [] 33 | self.__tokens_length = len(self.__tokens) 34 | self.__position = 0 35 | self.__parent_token = parent_token 36 | 37 | def restart(self): 38 | self.__position = 0 39 | 40 | def isEmpty(self): 41 | return self.__tokens_length == 0 42 | 43 | def hasNext(self): 44 | return self.__position < self.__tokens_length 45 | 46 | def next(self): 47 | if self.hasNext(): 48 | val = self.__tokens[self.__position] 49 | self.__position += 1 50 | return val 51 | else: 52 | raise StopIteration 53 | 54 | def peek(self, index=0): 55 | val = None 56 | index += self.__position 57 | if index >= 0 and index < self.__tokens_length: 58 | val = self.__tokens[index] 59 | 60 | return val 61 | 62 | def add(self, token): 63 | if self.__parent_token: 64 | token.parent = self.__parent_token 65 | 66 | self.__tokens.append(token) 67 | self.__tokens_length += 1 68 | 69 | def __iter__(self): 70 | self.restart() 71 | return self 72 | 73 | def __next__(self): 74 | return self.next() 75 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/tests/test-myobfuscate-output.js: -------------------------------------------------------------------------------- 1 | // 2 | // Unpacker warning: be careful when using myobfuscate.com for your projects: 3 | // scripts obfuscated by the free online version call back home. 4 | // 5 | 6 | // 7 | // Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier 8 | // written by Einar Lielmanis 9 | // 10 | // Coincidentally, it can defeat a couple of other eval-based compressors. 11 | // 12 | // usage: 13 | // 14 | // if (P_A_C_K_E_R.detect(some_string)) { 15 | // var unpacked = P_A_C_K_E_R.unpack(some_string); 16 | // } 17 | // 18 | // 19 | 20 | var P_A_C_K_E_R = { 21 | detect: function (str) { 22 | return P_A_C_K_E_R._starts_with(str.toLowerCase().replace(/ +/g, ''), 'eval(function(') || 23 | P_A_C_K_E_R._starts_with(str.toLowerCase().replace(/ +/g, ''), 'eval((function(') ; 24 | }, 25 | 26 | unpack: function (str) { 27 | var unpacked_source = ''; 28 | if (P_A_C_K_E_R.detect(str)) { 29 | try { 30 | eval('unpacked_source = ' + str.substring(4) + ';') 31 | if (typeof unpacked_source == 'string' && unpacked_source) { 32 | str = unpacked_source; 33 | } 34 | } catch (error) { 35 | // well, it failed. we'll just return the original, instead of crashing on user. 36 | } 37 | } 38 | return str; 39 | }, 40 | 41 | _starts_with: function (str, what) { 42 | return str.substr(0, what.length) === what; 43 | }, 44 | 45 | run_tests: function (sanity_test) { 46 | var t = sanity_test || new SanityTest(); 47 | t.test_function(P_A_C_K_E_R.detect, "P_A_C_K_E_R.detect"); 48 | t.expect('', false); 49 | t.expect('var a = b', false); 50 | t.expect('eval(function(p,a,c,k,e,r', true); 51 | t.expect('eval ( function(p, a, c, k, e, r', true); 52 | 53 | t.test_function(P_A_C_K_E_R.unpack, 'P_A_C_K_E_R.unpack'); 54 | t.expect("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,{}))", 55 | 'var a=1'); 56 | 57 | var starts_with_a = function(what) { return P_A_C_K_E_R._starts_with(what, 'a'); } 58 | t.test_function(starts_with_a, "P_A_C_K_E_R._starts_with(?, a)"); 59 | t.expect('abc', true); 60 | t.expect('bcd', false); 61 | t.expect('a', true); 62 | t.expect('', false); 63 | return t; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/milestone-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Release new version 5 | 6 | on: 7 | milestone: 8 | types: [closed] 9 | 10 | 11 | jobs: 12 | publish: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Set up Node 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: 18 22 | registry-url: https://registry.npmjs.org/ 23 | - name: Set up Python 24 | uses: actions/setup-python@v5 25 | with: 26 | python-version: 3.11 27 | - name: Set git user 28 | run: | 29 | git config --global user.email "github-action@users.noreply.github.com" 30 | git config --global user.name "GitHub Action" 31 | - name: Fetch beautifier/beautifier.io 32 | env: 33 | BEAUTIFIER_IO_DEPLOY_KEY: ${{ secrets.BEAUTIFIER_IO_DEPLOY_KEY }} 34 | JS_BEAUTIFY_DEPLOY_KEY: ${{ secrets.JS_BEAUTIFY_DEPLOY_KEY }} 35 | SSH_AUTH_SOCK: /tmp/ssh_agent.sock 36 | run: | 37 | mkdir -p ~/.ssh 38 | cat .github/workflows/ssh_config.txt > ~/.ssh/config 39 | ssh-agent -a $SSH_AUTH_SOCK > /dev/null 40 | ssh-keyscan github.com >> ~/.ssh/known_hosts 41 | 42 | cat > ~/.ssh/deploy_beautifier_io <<< "${BEAUTIFIER_IO_DEPLOY_KEY}" 43 | cat > ~/.ssh/deploy_js_beautify <<< "${JS_BEAUTIFY_DEPLOY_KEY}" 44 | 45 | chmod 400 ~/.ssh/deploy_beautifier_io 46 | chmod 400 ~/.ssh/deploy_js_beautify 47 | 48 | ssh-add ~/.ssh/deploy_beautifier_io 49 | ssh-add ~/.ssh/deploy_js_beautify 50 | 51 | git remote add site git@beautifier-github.com:beautifier/beautifier.io.git 52 | git remote add trigger git@js-beautify-github.com:beautifier/js-beautify.git 53 | git fetch --all 54 | - name: Install python twinE 55 | run: pip install twine wheel 56 | - name: Run release script for ${{ github.event.milestone.title }} 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | SSH_AUTH_SOCK: /tmp/ssh_agent.sock 60 | MILESTONE_VERSION: ${{ github.event.milestone.title }} 61 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 62 | TWINE_USERNAME: ${{secrets.PYPI_USERNAME}} 63 | TWINE_PASSWORD: ${{secrets.PYPI_PASSWORD}} 64 | run: | 65 | ./tools/release-all.sh ${MILESTONE_VERSION} 66 | -------------------------------------------------------------------------------- /python/jsbeautifier/core/whitespacepattern.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 4 | # 5 | # Permission is hereby granted, free of charge, to any person 6 | # obtaining a copy of this software and associated documentation files 7 | # (the "Software"), to deal in the Software without restriction, 8 | # including without limitation the rights to use, copy, modify, merge, 9 | # publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, 11 | # subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | import re 26 | from ..core.pattern import Pattern 27 | 28 | __all__ = ["WhitespacePattern"] 29 | 30 | 31 | class WhitespacePattern(Pattern): 32 | def __init__(self, input_scanner, parent=None): 33 | Pattern.__init__(self, input_scanner, parent) 34 | 35 | if parent is not None: 36 | self._newline_regexp = self._input.get_regexp(parent._newline_regexp) 37 | else: 38 | self.__set_whitespace_patterns("", "") 39 | 40 | self.newline_count = 0 41 | self.whitespace_before_token = "" 42 | 43 | def __set_whitespace_patterns(self, whitespace_chars, newline_chars): 44 | whitespace_chars += "\\t " 45 | newline_chars += "\\n\\r" 46 | 47 | self._match_pattern = self._input.get_regexp( 48 | "[" + whitespace_chars + newline_chars + "]+" 49 | ) 50 | self._newline_regexp = self._input.get_regexp("\\r\\n|[" + newline_chars + "]") 51 | 52 | def read(self): 53 | self.newline_count = 0 54 | self.whitespace_before_token = "" 55 | 56 | resulting_string = self._input.read(self._match_pattern) 57 | if resulting_string == " ": 58 | self.whitespace_before_token = " " 59 | elif bool(resulting_string): 60 | lines = self._newline_regexp.split(resulting_string) 61 | self.newline_count = len(lines) - 1 62 | self.whitespace_before_token = lines[-1] 63 | 64 | return resulting_string 65 | 66 | def matching(self, whitespace_chars, newline_chars): 67 | result = self._create() 68 | result.__set_whitespace_patterns(whitespace_chars, newline_chars) 69 | result._update() 70 | return result 71 | 72 | def _create(self): 73 | return WhitespacePattern(self._input, self) 74 | -------------------------------------------------------------------------------- /python/jsbeautifier/unpackers/myobfuscate.py: -------------------------------------------------------------------------------- 1 | # 2 | # deobfuscator for scripts messed up with myobfuscate.com 3 | # by Einar Lielmanis 4 | # 5 | # written by Stefano Sanfilippo 6 | # 7 | # usage: 8 | # 9 | # if detect(some_string): 10 | # unpacked = unpack(some_string) 11 | # 12 | 13 | # CAVEAT by Einar Lielmanis 14 | 15 | # 16 | # You really don't want to obfuscate your scripts there: they're tracking 17 | # your unpackings, your script gets turned into something like this, 18 | # as of 2011-08-26: 19 | # 20 | # var _escape = 'your_script_escaped'; 21 | # var _111 = document.createElement('script'); 22 | # _111.src = 'http://api.www.myobfuscate.com/?getsrc=ok' + 23 | # '&ref=' + encodeURIComponent(document.referrer) + 24 | # '&url=' + encodeURIComponent(document.URL); 25 | # var 000 = document.getElementsByTagName('head')[0]; 26 | # 000.appendChild(_111); 27 | # document.write(unescape(_escape)); 28 | # 29 | 30 | """Deobfuscator for scripts messed up with MyObfuscate.com""" 31 | 32 | import re 33 | import base64 34 | 35 | # Python 2 retrocompatibility 36 | # pylint: disable=F0401 37 | # pylint: disable=E0611 38 | try: 39 | from urllib import unquote 40 | except ImportError: 41 | from urllib.parse import unquote 42 | 43 | from jsbeautifier.unpackers import UnpackingError 44 | 45 | PRIORITY = 1 46 | 47 | CAVEAT = """// 48 | // Unpacker warning: be careful when using myobfuscate.com for your projects: 49 | // scripts obfuscated by the free online version call back home. 50 | // 51 | 52 | """ 53 | 54 | SIGNATURE = ( 55 | r'["\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F' 56 | r"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x61\x62\x63\x64\x65" 57 | r"\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75" 58 | r"\x76\x77\x78\x79\x7A\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2B" 59 | r'\x2F\x3D","","\x63\x68\x61\x72\x41\x74","\x69\x6E\x64\x65\x78' 60 | r'\x4F\x66","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","' 61 | r'\x6C\x65\x6E\x67\x74\x68"]' 62 | ) 63 | 64 | 65 | def detect(source): 66 | """Detects MyObfuscate.com packer.""" 67 | return SIGNATURE in source 68 | 69 | 70 | def unpack(source): 71 | """Unpacks js code packed with MyObfuscate.com""" 72 | if not detect(source): 73 | return source 74 | payload = unquote(_filter(source)) 75 | match = re.search(r"^var _escape\='')) { 82 | unescaped = unescaped.substr(0, unescaped.length - 9); 83 | } 84 | unpacked = unescaped; 85 | } 86 | // throw to terminate the script 87 | unpacked = "// Unpacker warning: be careful when using myobfuscate.com for your projects:\n" + 88 | "// scripts obfuscated by the free online version may call back home.\n" + 89 | "\n//\n" + unpacked; 90 | throw unpacked; 91 | }; // jshint ignore:line 92 | __eval(str); // should throw 93 | } catch (e) { 94 | // well, it failed. we'll just return the original, instead of crashing on user. 95 | if (typeof e === "string") { 96 | str = e; 97 | } 98 | } 99 | eval = __eval; // jshint ignore:line 100 | } 101 | return str; 102 | }, 103 | 104 | starts_with: function(str, what) { 105 | return str.substr(0, what.length) === what; 106 | }, 107 | 108 | ends_with: function(str, what) { 109 | return str.substr(str.length - what.length, what.length) === what; 110 | }, 111 | 112 | run_tests: function(sanity_test) { 113 | var t = sanity_test || new SanityTest(); 114 | 115 | return t; 116 | } 117 | 118 | 119 | }; 120 | -------------------------------------------------------------------------------- /js/src/html/options.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var BaseOptions = require('../core/options').Options; 32 | 33 | function Options(options) { 34 | BaseOptions.call(this, options, 'html'); 35 | if (this.templating.length === 1 && this.templating[0] === 'auto') { 36 | this.templating = ['django', 'erb', 'handlebars', 'php']; 37 | } 38 | 39 | this.indent_inner_html = this._get_boolean('indent_inner_html'); 40 | this.indent_body_inner_html = this._get_boolean('indent_body_inner_html', true); 41 | this.indent_head_inner_html = this._get_boolean('indent_head_inner_html', true); 42 | 43 | this.indent_handlebars = this._get_boolean('indent_handlebars', true); 44 | this.wrap_attributes = this._get_selection('wrap_attributes', 45 | ['auto', 'force', 'force-aligned', 'force-expand-multiline', 'aligned-multiple', 'preserve', 'preserve-aligned']); 46 | this.wrap_attributes_min_attrs = this._get_number('wrap_attributes_min_attrs', 2); 47 | this.wrap_attributes_indent_size = this._get_number('wrap_attributes_indent_size', this.indent_size); 48 | this.extra_liners = this._get_array('extra_liners', ['head', 'body', '/html']); 49 | 50 | // Block vs inline elements 51 | // https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements 52 | // https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements 53 | // https://www.w3.org/TR/html5/dom.html#phrasing-content 54 | this.inline = this._get_array('inline', [ 55 | 'a', 'abbr', 'area', 'audio', 'b', 'bdi', 'bdo', 'br', 'button', 'canvas', 'cite', 56 | 'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed', 'i', 'iframe', 'img', 57 | 'input', 'ins', 'kbd', 'keygen', 'label', 'map', 'mark', 'math', 'meter', 'noscript', 58 | 'object', 'output', 'progress', 'q', 'ruby', 's', 'samp', /* 'script', */ 'select', 'small', 59 | 'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var', 60 | 'video', 'wbr', 'text', 61 | // obsolete inline tags 62 | 'acronym', 'big', 'strike', 'tt' 63 | ]); 64 | this.inline_custom_elements = this._get_boolean('inline_custom_elements', true); 65 | this.void_elements = this._get_array('void_elements', [ 66 | // HTLM void elements - aka self-closing tags - aka singletons 67 | // https://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements 68 | 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 69 | 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr', 70 | // NOTE: Optional tags are too complex for a simple list 71 | // they are hard coded in _do_optional_end_element 72 | 73 | // Doctype and xml elements 74 | '!doctype', '?xml', 75 | 76 | // obsolete tags 77 | // basefont: https://www.computerhope.com/jargon/h/html-basefont-tag.htm 78 | // isndex: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/isindex 79 | 'basefont', 'isindex' 80 | ]); 81 | this.unformatted = this._get_array('unformatted', []); 82 | this.content_unformatted = this._get_array('content_unformatted', [ 83 | 'pre', 'textarea' 84 | ]); 85 | this.unformatted_content_delimiter = this._get_characters('unformatted_content_delimiter'); 86 | this.indent_scripts = this._get_selection('indent_scripts', ['normal', 'keep', 'separate']); 87 | 88 | } 89 | Options.prototype = new BaseOptions(); 90 | 91 | 92 | 93 | module.exports.Options = Options; 94 | -------------------------------------------------------------------------------- /js/src/core/tokenizer.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | 'use strict'; 30 | 31 | var InputScanner = require('../core/inputscanner').InputScanner; 32 | var Token = require('../core/token').Token; 33 | var TokenStream = require('../core/tokenstream').TokenStream; 34 | var WhitespacePattern = require('./whitespacepattern').WhitespacePattern; 35 | 36 | var TOKEN = { 37 | START: 'TK_START', 38 | RAW: 'TK_RAW', 39 | EOF: 'TK_EOF' 40 | }; 41 | 42 | var Tokenizer = function(input_string, options) { 43 | this._input = new InputScanner(input_string); 44 | this._options = options || {}; 45 | this.__tokens = null; 46 | 47 | this._patterns = {}; 48 | this._patterns.whitespace = new WhitespacePattern(this._input); 49 | }; 50 | 51 | Tokenizer.prototype.tokenize = function() { 52 | this._input.restart(); 53 | this.__tokens = new TokenStream(); 54 | 55 | this._reset(); 56 | 57 | var current; 58 | var previous = new Token(TOKEN.START, ''); 59 | var open_token = null; 60 | var open_stack = []; 61 | var comments = new TokenStream(); 62 | 63 | while (previous.type !== TOKEN.EOF) { 64 | current = this._get_next_token(previous, open_token); 65 | while (this._is_comment(current)) { 66 | comments.add(current); 67 | current = this._get_next_token(previous, open_token); 68 | } 69 | 70 | if (!comments.isEmpty()) { 71 | current.comments_before = comments; 72 | comments = new TokenStream(); 73 | } 74 | 75 | current.parent = open_token; 76 | 77 | if (this._is_opening(current)) { 78 | open_stack.push(open_token); 79 | open_token = current; 80 | } else if (open_token && this._is_closing(current, open_token)) { 81 | current.opened = open_token; 82 | open_token.closed = current; 83 | open_token = open_stack.pop(); 84 | current.parent = open_token; 85 | } 86 | 87 | current.previous = previous; 88 | previous.next = current; 89 | 90 | this.__tokens.add(current); 91 | previous = current; 92 | } 93 | 94 | return this.__tokens; 95 | }; 96 | 97 | 98 | Tokenizer.prototype._is_first_token = function() { 99 | return this.__tokens.isEmpty(); 100 | }; 101 | 102 | Tokenizer.prototype._reset = function() {}; 103 | 104 | Tokenizer.prototype._get_next_token = function(previous_token, open_token) { // jshint unused:false 105 | this._readWhitespace(); 106 | var resulting_string = this._input.read(/.+/g); 107 | if (resulting_string) { 108 | return this._create_token(TOKEN.RAW, resulting_string); 109 | } else { 110 | return this._create_token(TOKEN.EOF, ''); 111 | } 112 | }; 113 | 114 | Tokenizer.prototype._is_comment = function(current_token) { // jshint unused:false 115 | return false; 116 | }; 117 | 118 | Tokenizer.prototype._is_opening = function(current_token) { // jshint unused:false 119 | return false; 120 | }; 121 | 122 | Tokenizer.prototype._is_closing = function(current_token, open_token) { // jshint unused:false 123 | return false; 124 | }; 125 | 126 | Tokenizer.prototype._create_token = function(type, text) { 127 | var token = new Token(type, text, 128 | this._patterns.whitespace.newline_count, 129 | this._patterns.whitespace.whitespace_before_token); 130 | return token; 131 | }; 132 | 133 | Tokenizer.prototype._readWhitespace = function() { 134 | return this._patterns.whitespace.read(); 135 | }; 136 | 137 | 138 | 139 | module.exports.Tokenizer = Tokenizer; 140 | module.exports.TOKEN = TOKEN; 141 | -------------------------------------------------------------------------------- /tools/release-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REL_SCRIPT_DIR="`dirname \"$0\"`" 4 | SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`" 5 | 6 | case "$OSTYPE" in 7 | darwin*) PLATFORM="OSX" ;; 8 | linux*) PLATFORM="LINUX" ;; 9 | bsd*) PLATFORM="BSD" ;; 10 | *) PLATFORM="UNKNOWN" ;; 11 | esac 12 | 13 | release_python() 14 | { 15 | cd $SCRIPT_DIR/.. 16 | git clean -xfd || exit 1 17 | git fetch --all || exit 1 18 | 19 | git checkout -B staging/release origin/staging/release 20 | git clean -xfd || exit 1 21 | cd python 22 | # python setup.py register -r pypi 23 | cp setup-js.py setup.py || exit 1 24 | python setup.py sdist bdist_wheel || exit 1 25 | cp setup-css.py setup.py || exit 1 26 | python setup.py sdist bdist_wheel || exit 1 27 | rm setup.py || exit 1 28 | python -m twine upload dist/* || exit 1 29 | } 30 | 31 | release_node() 32 | { 33 | cd $SCRIPT_DIR/.. 34 | git clean -xfd || exit 1 35 | git fetch --all || exit 1 36 | 37 | git checkout -B staging/release origin/staging/release 38 | git clean -xfd || exit 1 39 | unset NPM_TAG 40 | if [[ $NEW_VERSION =~ .*(rc|beta).* ]]; then 41 | NPM_TAG='--tag next' 42 | fi 43 | $SCRIPT_DIR/npm publish . $NPM_TAG || exit 1 44 | } 45 | 46 | release_web() 47 | { 48 | cd $SCRIPT_DIR/.. 49 | git clean -xfd || exit 1 50 | git fetch --all || exit 1 51 | 52 | git checkout -B staging/gh-pages site/staging/gh-pages || exit 1 53 | git reset --hard site/gh-pages || exit 1 54 | git merge origin/staging/release --no-edit || exit 1 55 | git push || exit 1 56 | 57 | git checkout -B staging/main site/staging/main || exit 1 58 | git reset --hard site/main || exit 1 59 | git merge origin/staging/main --no-edit || exit 1 60 | git push || exit 1 61 | } 62 | 63 | sedi() { 64 | if [[ "$PLATFORM" == "OSX" || "$PLATFORM" == "BSD" ]]; then 65 | sed -i "" $@ 66 | elif [ "$PLATFORM" == "LINUX" ]; then 67 | sed -i $@ 68 | else 69 | exit 1 70 | fi 71 | } 72 | 73 | update_versions() 74 | { 75 | cd $SCRIPT_DIR/.. 76 | git clean -xfd || exit 1 77 | git fetch --all || exit 1 78 | 79 | # trigger remote uses deploy key, push will cause downstream GitHub Actions to fire 80 | git checkout -B staging/main trigger/staging/main || exit 1 81 | git merge origin/main --no-edit || exit 1 82 | git clean -xfd || exit 1 83 | 84 | # Disabled due to build break 85 | $SCRIPT_DIR/generate-changelog.sh beautifier/js-beautify $GITHUB_TOKEN || exit 1 86 | 87 | $SCRIPT_DIR/npm version --no-git-tag-version $NEW_VERSION || exit 1 88 | 89 | sedi -E 's@(cdnjs.cloudflare.+beautify/)[^/]+@\1'$NEW_VERSION'@' README.md 90 | sedi -E 's/\((README\.md:.js-beautify@).+\)/(\1'$NEW_VERSION')/' README.md 91 | 92 | echo "__version__ = \"$NEW_VERSION\"" > python/jsbeautifier/__version__.py 93 | echo "__version__ = \"$NEW_VERSION\"" > python/cssbeautifier/__version__.py 94 | git add . || exit 1 95 | git commit -am "Bump version numbers for $NEW_VERSION" || exit 1 96 | git push || exit 1 97 | } 98 | 99 | update_release_branch() 100 | { 101 | cd $SCRIPT_DIR/.. 102 | git clean -xfd || exit 1 103 | git fetch --all || exit 1 104 | 105 | git reset --hard 106 | # trigger remote uses deploy key, push will cause downstream GitHub Actions to fire 107 | git checkout -B staging/release trigger/staging/release || exit 1 108 | git merge origin/release --no-edit || exit 1 109 | git merge origin/staging/main --no-edit || exit 1 110 | 111 | make js || exit 1 112 | git add -f js/lib/ || exit 1 113 | git add -f js/test/generated/ 114 | git add -f python/jsbeautifier/tests/generated/ 115 | git add -f python/cssbeautifier/tests/generated/ 116 | 117 | git commit -m "Release: $NEW_VERSION" 118 | git tag "v$NEW_VERSION" || exit 1 119 | git push || exit 1 120 | git push --tags 121 | } 122 | 123 | main() 124 | { 125 | cd $SCRIPT_DIR/.. 126 | 127 | local NEW_VERSION=$1 128 | NEW_VERSION=${NEW_VERSION/v/} 129 | 130 | if [[ ! $NEW_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9].*$ ]]; then 131 | echo Version number must start with MAJOR.MINOR.INCREMENTAL numbering. 132 | exit 1 133 | fi 134 | 135 | npm --version > /dev/null || { 136 | echo ERROR: npm must be installed before attempting release 137 | exit 1 138 | } 139 | 140 | twine -h > /dev/null || { 141 | exit 1 142 | } 143 | 144 | update_versions 145 | update_release_branch 146 | 147 | release_python 148 | release_node 149 | release_web 150 | 151 | git checkout main 152 | } 153 | 154 | (main $*) 155 | -------------------------------------------------------------------------------- /js/src/unpackers/javascriptobfuscator_unpacker.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 6 | 7 | Permission is hereby granted, free of charge, to any person 8 | obtaining a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 22 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 23 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | */ 27 | 28 | // 29 | // simple unpacker/deobfuscator for scripts messed up with javascriptobfuscator.com 30 | // written by Einar Lielmanis 31 | // 32 | // usage: 33 | // 34 | // if (JavascriptObfuscator.detect(some_string)) { 35 | // var unpacked = JavascriptObfuscator.unpack(some_string); 36 | // } 37 | // 38 | // 39 | 40 | /*jshint strict:false */ 41 | 42 | var JavascriptObfuscator = { 43 | detect: function(str) { 44 | return /^var _0x[a-f0-9]+ ?\= ?\[/.test(str); 45 | }, 46 | 47 | unpack: function(str) { 48 | if (JavascriptObfuscator.detect(str)) { 49 | var matches = /var (_0x[a-f\d]+) ?\= ?\[(.*?)\];/.exec(str); 50 | if (matches) { 51 | var var_name = matches[1]; 52 | var strings = JavascriptObfuscator._smart_split(matches[2]); 53 | str = str.substring(matches[0].length); 54 | for (var k in strings) { 55 | str = str.replace(new RegExp(var_name + '\\[' + k + '\\]', 'g'), 56 | JavascriptObfuscator._fix_quotes(JavascriptObfuscator._unescape(strings[k]))); 57 | } 58 | } 59 | } 60 | return str; 61 | }, 62 | 63 | _fix_quotes: function(str) { 64 | var matches = /^"(.*)"$/.exec(str); 65 | if (matches) { 66 | str = matches[1]; 67 | str = "'" + str.replace(/'/g, "\\'") + "'"; 68 | } 69 | return str; 70 | }, 71 | 72 | _smart_split: function(str) { 73 | var strings = []; 74 | var pos = 0; 75 | while (pos < str.length) { 76 | if (str.charAt(pos) === '"') { 77 | // new word 78 | var word = ''; 79 | pos += 1; 80 | while (pos < str.length) { 81 | if (str.charAt(pos) === '"') { 82 | break; 83 | } 84 | if (str.charAt(pos) === '\\') { 85 | word += '\\'; 86 | pos++; 87 | } 88 | word += str.charAt(pos); 89 | pos++; 90 | } 91 | strings.push('"' + word + '"'); 92 | } 93 | pos += 1; 94 | } 95 | return strings; 96 | }, 97 | 98 | 99 | _unescape: function(str) { 100 | // inefficient if used repeatedly or on small strings, but wonderful on single large chunk of text 101 | for (var i = 32; i < 128; i++) { 102 | str = str.replace(new RegExp('\\\\x' + i.toString(16), 'ig'), String.fromCharCode(i)); 103 | } 104 | str = str.replace(/\\x09/g, "\t"); 105 | return str; 106 | }, 107 | 108 | run_tests: function(sanity_test) { 109 | var t = sanity_test || new SanityTest(); 110 | 111 | t.test_function(JavascriptObfuscator._smart_split, "JavascriptObfuscator._smart_split"); 112 | t.expect('', []); 113 | t.expect('"a", "b"', ['"a"', '"b"']); 114 | t.expect('"aaa","bbbb"', ['"aaa"', '"bbbb"']); 115 | t.expect('"a", "b\\\""', ['"a"', '"b\\\""']); 116 | t.test_function(JavascriptObfuscator._unescape, 'JavascriptObfuscator._unescape'); 117 | t.expect('\\x40', '@'); 118 | t.expect('\\x10', '\\x10'); 119 | t.expect('\\x1', '\\x1'); 120 | t.expect("\\x61\\x62\\x22\\x63\\x64", 'ab"cd'); 121 | t.test_function(JavascriptObfuscator.detect, 'JavascriptObfuscator.detect'); 122 | t.expect('', false); 123 | t.expect('abcd', false); 124 | t.expect('var _0xaaaa', false); 125 | t.expect('var _0xaaaa = ["a", "b"]', true); 126 | t.expect('var _0xaaaa=["a", "b"]', true); 127 | t.expect('var _0x1234=["a","b"]', true); 128 | return t; 129 | } 130 | 131 | 132 | }; 133 | -------------------------------------------------------------------------------- /python/jsbeautifier/javascript/options.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 4 | # 5 | # Permission is hereby granted, free of charge, to any person 6 | # obtaining a copy of this software and associated documentation files 7 | # (the "Software"), to deal in the Software without restriction, 8 | # including without limitation the rights to use, copy, modify, merge, 9 | # publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, 11 | # subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | 26 | from ..core.options import Options as BaseOptions 27 | 28 | OPERATOR_POSITION = ["before-newline", "after-newline", "preserve-newline"] 29 | 30 | 31 | class BeautifierOptions(BaseOptions): 32 | def __init__(self, options=None): 33 | BaseOptions.__init__(self, options, "js") 34 | 35 | self.css = None 36 | self.js = None 37 | self.html = None 38 | 39 | # compatibility, re 40 | 41 | raw_brace_style = getattr(self.raw_options, "brace_style", None) 42 | if raw_brace_style == "expand-strict": # graceful handling of deprecated option 43 | setattr(self.raw_options, "brace_style", "expand") 44 | elif ( 45 | raw_brace_style == "collapse-preserve-inline" 46 | ): # graceful handling of deprecated option 47 | setattr(self.raw_options, "brace_style", "collapse,preserve-inline") 48 | # elif bool(self.raw_options.braces_on_own_line): # graceful handling of deprecated option 49 | # raw_brace_style = "expand": "collapse" 50 | # elif raw_brace_style is None: # Nothing exists to set it 51 | # setattr(self.raw_options, 'brace_style', "collapse") 52 | 53 | # preserve-inline in delimited string will trigger brace_preserve_inline, everything 54 | # else is considered a brace_style and the last one only will have an effect 55 | 56 | brace_style_split = self._get_selection_list( 57 | "brace_style", 58 | ["collapse", "expand", "end-expand", "none", "preserve-inline"], 59 | ) 60 | 61 | # preserve-inline in delimited string will trigger brace_preserve_inline 62 | # Everything else is considered a brace_style and the last one only will 63 | # have an effect 64 | # specify defaults in case one half of meta-option is missing 65 | self.brace_preserve_inline = False 66 | self.brace_style = "collapse" 67 | 68 | for bs in brace_style_split: 69 | if bs == "preserve-inline": 70 | self.brace_preserve_inline = True 71 | else: 72 | self.brace_style = bs 73 | 74 | self.unindent_chained_methods = self._get_boolean("unindent_chained_methods") 75 | self.break_chained_methods = self._get_boolean("break_chained_methods") 76 | self.space_in_paren = self._get_boolean("space_in_paren") 77 | self.space_in_empty_paren = self._get_boolean("space_in_empty_paren") 78 | self.jslint_happy = self._get_boolean("jslint_happy") 79 | self.space_after_anon_function = self._get_boolean("space_after_anon_function") 80 | self.space_after_named_function = self._get_boolean( 81 | "space_after_named_function" 82 | ) 83 | self.keep_array_indentation = self._get_boolean("keep_array_indentation") 84 | self.space_before_conditional = self._get_boolean( 85 | "space_before_conditional", True 86 | ) 87 | self.unescape_strings = self._get_boolean("unescape_strings") 88 | self.e4x = self._get_boolean("e4x") 89 | self.comma_first = self._get_boolean("comma_first") 90 | self.operator_position = self._get_selection( 91 | "operator_position", OPERATOR_POSITION 92 | ) 93 | 94 | # For testing of beautify preserve:start directive 95 | self.test_output_raw = False 96 | 97 | # force opts.space_after_anon_function to true if opts.jslint_happy 98 | if self.jslint_happy: 99 | self.space_after_anon_function = True 100 | 101 | self.keep_quiet = False 102 | self.eval_code = False 103 | -------------------------------------------------------------------------------- /js/test/sanitytest.js: -------------------------------------------------------------------------------- 1 | // 2 | // simple testing interface 3 | // written by Einar Lielmanis, einar@beautifier.io 4 | // 5 | // usage: 6 | // 7 | // var t = new SanityTest(function (x) { return x; }, 'my function'); 8 | // t.expect('input', 'output'); 9 | // t.expect('a', 'a'); 10 | // output_somewhere(t.results()); // good for
, html safe-ish
 11 | // alert(t.results_raw());        // html unescaped
 12 | 
 13 | function SanityTest(func, name_of_test) {
 14 |   'use strict';
 15 | 
 16 |   var test_func = func || function(x) {
 17 |     return x;
 18 |   };
 19 | 
 20 |   var test_name = name_of_test || '';
 21 | 
 22 |   var n_failed = 0;
 23 |   var n_succeeded = 0;
 24 | 
 25 |   var failures = [];
 26 | 
 27 |   this.test_function = function(func, name) {
 28 |     test_func = func;
 29 |     test_name = name || '';
 30 |   };
 31 | 
 32 |   this.get_exitcode = function() {
 33 |     return n_succeeded === 0 || n_failed !== 0 ? 1 : 0;
 34 |   };
 35 | 
 36 |   this.expect = function(parameters, expected_value) {
 37 |     // multi-parameter calls not supported (I don't need them now).
 38 |     var result = test_func(parameters);
 39 |     // proper array checking is a pain. i'll maybe do it later, compare strings representations instead
 40 |     if ((result === expected_value) || (expected_value instanceof Array && result.join(', ') === expected_value.join(', '))) {
 41 |       n_succeeded += 1;
 42 |       return true;
 43 |     } else {
 44 |       n_failed += 1;
 45 |       failures.push([test_name, parameters, expected_value, result]);
 46 |       return false;
 47 |     }
 48 |   };
 49 | 
 50 | 
 51 |   this.results_raw = function() {
 52 |     var results = '';
 53 |     if (n_failed === 0) {
 54 |       if (n_succeeded === 0) {
 55 |         results = 'No tests run.';
 56 |       } else {
 57 |         results = 'All ' + n_succeeded + ' tests passed.';
 58 |       }
 59 |     } else {
 60 |       for (var i = 0; i < failures.length; i++) {
 61 |         var f = failures[i];
 62 |         if (f[0]) {
 63 |           f[0] = f[0] + ' ';
 64 |         }
 65 |         results += '==== ' + f[0] + '============================================================\n';
 66 |         results += '---- input -------\n' + this.prettyprint(f[1]) + '\n';
 67 |         results += '---- expected ----\n' + this.prettyprint(f[2]) + '\n';
 68 |         results += '---- output ------\n' + this.prettyprint(f[3]) + '\n';
 69 |         results += '---- expected-ws ------\n' + this.prettyprint_whitespace(f[2]) + '\n';
 70 |         results += '---- output-ws ------\n' + this.prettyprint_whitespace(f[3]) + '\n';
 71 |         results += '================================================================\n\n';
 72 |       }
 73 |       results += n_failed + ' tests failed.\n';
 74 |     }
 75 |     return results;
 76 |   };
 77 | 
 78 | 
 79 |   this.results = function() {
 80 |     return this.lazy_escape(this.results_raw());
 81 |   };
 82 | 
 83 |   this.prettyprint_whitespace = function(something, quote_strings) {
 84 |     return (this.prettyprint(something, quote_strings)
 85 |       .replace(/\r\n/g, '\\r\n')
 86 |       .replace(/\n/g, '\\n\n')
 87 |       .replace(/\r/g, '\\r\n')
 88 |       .replace(/ /g, '_')
 89 |       .replace(/\t/g, '===|'));
 90 |   };
 91 | 
 92 |   this.prettyprint = function(something, quote_strings) {
 93 |     var type = typeof something;
 94 |     switch (type.toLowerCase()) {
 95 |       case 'string':
 96 |         if (quote_strings) {
 97 |           return "'" + something.replace("'", "\\'") + "'";
 98 |         }
 99 |         return something;
100 |       case 'number':
101 |         return '' + something;
102 |       case 'boolean':
103 |         return something ? 'true' : 'false';
104 |       case 'undefined':
105 |         return 'undefined';
106 |       case 'object':
107 |         if (something instanceof Array) {
108 |           var x = [];
109 |           var expected_index = 0;
110 |           for (var k in something) {
111 |             if (k === expected_index) {
112 |               x.push(this.prettyprint(something[k], true));
113 |               expected_index += 1;
114 |             } else {
115 |               x.push('\n' + k + ': ' + this.prettyprint(something[k], true));
116 |             }
117 |           }
118 |           return '[' + x.join(', ') + ']';
119 |         }
120 |         return 'object: ' + something;
121 |       default:
122 |         return type + ': ' + something;
123 |     }
124 |   };
125 | 
126 | 
127 |   this.lazy_escape = function(str) {
128 |     return str.replace(//g, '>').replace(/\n/g, '
'); 129 | }; 130 | 131 | 132 | this.log = function() { 133 | if (window.console) { 134 | if (console.firebug) { 135 | console.log.apply(console, Array.prototype.slice.call(arguments)); 136 | } else { 137 | console.log.call(console, Array.prototype.slice.call(arguments)); 138 | } 139 | } 140 | }; 141 | 142 | } 143 | 144 | if (typeof module !== 'undefined' && module.exports) { 145 | module.exports = SanityTest; 146 | } 147 | -------------------------------------------------------------------------------- /python/jsbeautifier/core/tokenizer.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 4 | # 5 | # Permission is hereby granted, free of charge, to any person 6 | # obtaining a copy of this software and associated documentation files 7 | # (the "Software"), to deal in the Software without restriction, 8 | # including without limitation the rights to use, copy, modify, merge, 9 | # publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, 11 | # subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | import re 26 | from ..core.inputscanner import InputScanner 27 | from ..core.token import Token 28 | from ..core.tokenstream import TokenStream 29 | from ..core.pattern import Pattern 30 | from ..core.whitespacepattern import WhitespacePattern 31 | 32 | __all__ = ["TOKEN", "Tokenizer", "TokenizerPatterns", "TokenTypes"] 33 | 34 | 35 | class TokenTypes: 36 | START = "TK_START" 37 | RAW = "TK_RAW" 38 | EOF = "TK_EOF" 39 | 40 | def __init__(self): 41 | pass 42 | 43 | 44 | TOKEN = TokenTypes() 45 | 46 | 47 | class TokenizerPatterns: 48 | def __init__(self, input_scanner): 49 | self.whitespace = WhitespacePattern(input_scanner) 50 | 51 | 52 | class Tokenizer: 53 | def __init__(self, input_string, options): 54 | self._input = InputScanner(input_string) 55 | self._options = options 56 | self.__tokens = None 57 | 58 | self._patterns = TokenizerPatterns(self._input) 59 | 60 | def tokenize(self): 61 | self._input.restart() 62 | self.__tokens = TokenStream() 63 | 64 | current = None 65 | previous = Token(TOKEN.START, "") 66 | open_token = None 67 | open_stack = [] 68 | comments = TokenStream() 69 | 70 | while previous.type != TOKEN.EOF: 71 | current = self.__get_next_token_with_comments(previous, open_token) 72 | 73 | if self._is_opening(current): 74 | open_stack.append(open_token) 75 | open_token = current 76 | elif open_token is not None and self._is_closing(current, open_token): 77 | current.opened = open_token 78 | open_token.closed = current 79 | open_token = open_stack.pop() 80 | current.parent = open_token 81 | 82 | self.__tokens.add(current) 83 | previous = current 84 | return self.__tokens 85 | 86 | def __get_next_token_with_comments(self, previous, open_token): 87 | current = self._get_next_token(previous, open_token) 88 | 89 | if self._is_comment(current): 90 | comments = TokenStream() 91 | while self._is_comment(current): 92 | comments.add(current) 93 | current = self._get_next_token(previous, open_token) 94 | 95 | if not comments.isEmpty(): 96 | current.comments_before = comments 97 | comments = TokenStream() 98 | 99 | current.parent = open_token 100 | current.previous = previous 101 | previous.next = current 102 | 103 | return current 104 | 105 | def _is_first_token(self): 106 | return self.__tokens.isEmpty() 107 | 108 | def _reset(self): 109 | pass 110 | 111 | def _get_next_token(self, previous_token, open_token): 112 | self._readWhitespace() 113 | resulting_string = self._input.read(re.compile(r".+")) 114 | if resulting_string: 115 | return self._create_token(TOKEN.RAW, resulting_string) 116 | else: 117 | return self._create_token(TOKEN.EOF, "") 118 | 119 | def _is_comment(self, current_token): 120 | return False 121 | 122 | def _is_opening(self, current_token): 123 | return False 124 | 125 | def _is_closing(self, current_token, open_token): 126 | return False 127 | 128 | def _create_token(self, token_type, text): 129 | token = Token( 130 | token_type, 131 | text, 132 | self._patterns.whitespace.newline_count, 133 | self._patterns.whitespace.whitespace_before_token, 134 | ) 135 | return token 136 | 137 | def _readWhitespace(self): 138 | return self._patterns.whitespace.read() 139 | -------------------------------------------------------------------------------- /tools/template/beautify.wrapper.js: -------------------------------------------------------------------------------- 1 | /* AUTO-GENERATED. DO NOT MODIFY. */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | JS Beautifier 29 | --------------- 30 | 31 | 32 | Written by Einar Lielmanis, 33 | https://beautifier.io/ 34 | 35 | Originally converted to javascript by Vital, 36 | "End braces on own line" added by Chris J. Shull, 37 | Parsing improvements for brace-less statements by Liam Newman 38 | 39 | 40 | Usage: 41 | js_beautify(js_source_text); 42 | js_beautify(js_source_text, options); 43 | 44 | The options are: 45 | indent_size (default 4) - indentation size, 46 | indent_char (default space) - character to indent with, 47 | preserve_newlines (default true) - whether existing line breaks should be preserved, 48 | max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk, 49 | 50 | jslint_happy (default false) - if true, then jslint-stricter mode is enforced. 51 | 52 | jslint_happy !jslint_happy 53 | --------------------------------- 54 | function () function() 55 | 56 | switch () { switch() { 57 | case 1: case 1: 58 | break; break; 59 | } } 60 | 61 | space_after_anon_function (default false) - should the space before an anonymous function's parens be added, "function()" vs "function ()", 62 | NOTE: This option is overridden by jslint_happy (i.e. if jslint_happy is true, space_after_anon_function is true by design) 63 | 64 | brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "none" | any of the former + ",preserve-inline" 65 | put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line, or attempt to keep them where they are. 66 | preserve-inline will try to preserve inline blocks of curly braces 67 | 68 | space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)", 69 | 70 | unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65" 71 | 72 | wrap_line_length (default unlimited) - lines should wrap at next opportunity after this number of characters. 73 | NOTE: This is not a hard limit. Lines will continue until a point where a newline would 74 | be preserved if it were present. 75 | 76 | end_with_newline (default false) - end output with a newline 77 | 78 | 79 | e.g 80 | 81 | js_beautify(js_source_text, { 82 | 'indent_size': 1, 83 | 'indent_char': '\t' 84 | }); 85 | 86 | */ 87 | 88 | (function() { 89 | 90 | /* GENERATED_BUILD_OUTPUT */ 91 | 92 | var js_beautify = legacy_beautify_js; 93 | /* Footer */ 94 | if (typeof define === "function" && define.amd) { 95 | // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- ) 96 | define([], function() { 97 | return { js_beautify: js_beautify }; 98 | }); 99 | } else if (typeof exports !== "undefined") { 100 | // Add support for CommonJS. Just put this file somewhere on your require.paths 101 | // and you will be able to `var js_beautify = require("beautify").js_beautify`. 102 | exports.js_beautify = js_beautify; 103 | } else if (typeof window !== "undefined") { 104 | // If we're running a web page and don't have either of the above, add our one global 105 | window.js_beautify = js_beautify; 106 | } else if (typeof global !== "undefined") { 107 | // If we don't even have window, try global. 108 | global.js_beautify = js_beautify; 109 | } 110 | 111 | }()); 112 | 113 | -------------------------------------------------------------------------------- /web/common-style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #eee; 3 | color: #333; 4 | height: 95vh; 5 | margin: 8px; 6 | max-width: 100%; 7 | font: 13px/1.231 arial, sans-serif; 8 | } 9 | 10 | a.self { 11 | font-weight: bold; 12 | text-decoration: none; 13 | color: #444; 14 | border-bottom: 1px solid #aaa 15 | } 16 | 17 | h1.logo { 18 | display: flex; 19 | line-height: 1em; 20 | margin: 0; 21 | } 22 | 23 | h1 img { 24 | font-family: montserrat, nexa, gotham, sans-serif; 25 | font-weight: 600; 26 | } 27 | 28 | p, select, label, .blurb, a.turn-off-codemirror { 29 | font-size: small; 30 | } 31 | 32 | .sub-title { 33 | margin-bottom: 8px; 34 | } 35 | 36 | .title { 37 | margin-left: 40px; 38 | } 39 | 40 | textarea { 41 | width: 100%; 42 | } 43 | 44 | textarea#source { 45 | width: 100%; 46 | height: 100%; 47 | } 48 | 49 | a.turn-off-codemirror { 50 | margin-left: 25px; 51 | } 52 | 53 | input[type="checkbox"], select, button { 54 | cursor: pointer; 55 | } 56 | 57 | #version-number { 58 | align-self: flex-end; 59 | font-family: arial, sans-serif; 60 | font-size: .5em; 61 | margin-left: 10px; 62 | } 63 | 64 | #testresults { 65 | display: none; 66 | font-family: monaco, consolas, "lucida console", "courier new", monospace; 67 | } 68 | 69 | .CodeMirror { 70 | border: 1px solid #ccc; 71 | height: 100%; 72 | font-size: 90%; 73 | background: white; 74 | } 75 | 76 | .hide { 77 | display: none; 78 | } 79 | 80 | .container { 81 | max-width: 95%; 82 | display: grid; 83 | grid-template-columns: 65% 35%; 84 | grid-template-rows: 0.2fr 1.8fr; 85 | gap: 0 14px; 86 | grid-auto-flow: row; 87 | grid-template-areas: 88 | "title options" 89 | "editor options"; 90 | } 91 | 92 | .title { 93 | grid-area: title; 94 | } 95 | 96 | .editor { 97 | grid-area: editor; 98 | } 99 | 100 | .options { 101 | grid-area: options; 102 | } 103 | 104 | select, .buttons-box button { 105 | background-color: white; 106 | border: 1px solid #ccc; 107 | border-radius: 4px; 108 | line-height: 1.5em; 109 | padding: 0.5em; 110 | margin: 0 0 5px; 111 | -webkit-box-sizing: border-box; 112 | -moz-box-sizing: border-box; 113 | box-sizing: border-box; 114 | } 115 | 116 | select { 117 | display: block; 118 | } 119 | 120 | .options-checkboxes, .options-select { 121 | display: inline-table; 122 | max-width: 100%; 123 | } 124 | 125 | #language { 126 | font-size: 20px; 127 | width: 100%; 128 | padding-left: 16px; 129 | font-weight: 800; 130 | margin-bottom: 10px; 131 | } 132 | 133 | .buttons-box { 134 | margin-top: 20px; 135 | } 136 | 137 | .buttons-box .submit { 138 | margin-top: 10px; 139 | margin-bottom: 10px; 140 | display: block; 141 | font-size: 1.5em; 142 | width: 100%; 143 | background-color: #7cffcb; 144 | background-image: linear-gradient(315deg, #7cffcb 0%, #74f2ce 74%); 145 | } 146 | 147 | .buttons-box .control { 148 | background-color: #2e3138; 149 | color: white; 150 | display: inline-flex; 151 | } 152 | 153 | @media only screen and (max-width: 800px) { 154 | .container { 155 | grid-template-columns: 100%; 156 | grid-template-areas: 157 | "title" 158 | "editor" 159 | "options"; 160 | grid-template-rows: 0.1fr 1.8fr 0.1fr; 161 | max-width: 100%; 162 | } 163 | 164 | .options-checkboxes, .options-select { 165 | max-width: 50%; 166 | } 167 | 168 | .title { 169 | margin-left: 10px; 170 | } 171 | 172 | .editor, .title, .options { 173 | max-width: 100%; 174 | } 175 | 176 | .CodeMirror { 177 | height: 50vh; 178 | } 179 | } 180 | 181 | .dark-mode { 182 | background: #202124; 183 | color: #f8f9fa; 184 | } 185 | 186 | .dark-mode a { 187 | color: #9da2ff; 188 | } 189 | 190 | .dark-mode a:visited { 191 | color: #d7affb; 192 | } 193 | 194 | .dark-mode a.self { 195 | color: #d2d3d7; 196 | } 197 | 198 | .dark-mode select, 199 | .dark-mode textarea { 200 | background-color: #2e3037; 201 | color: #e8eaed; 202 | border: 1px solid #333333; 203 | } 204 | 205 | #theme-toggle-wrapper { 206 | display: flex; 207 | align-items: center; 208 | justify-content: flex-end; 209 | margin: 5px; 210 | } 211 | 212 | #theme-toggle-wrapper span { 213 | margin-left: 10px; 214 | font-size: 1rem; 215 | } 216 | 217 | #theme-toggle-label { 218 | display: inline-block; 219 | height: 20px; 220 | position: relative; 221 | width: 40px; 222 | } 223 | 224 | #theme-toggle-slider { 225 | background-color: #ccc; 226 | bottom: 0; 227 | cursor: pointer; 228 | left: 0; 229 | position: absolute; 230 | right: 0; 231 | top: 0; 232 | transition: .4s; 233 | } 234 | 235 | #theme-toggle-slider:before { 236 | background-color: #fff; 237 | content: ""; 238 | height: 20px; 239 | position: absolute; 240 | transition: .4s; 241 | width: 20px; 242 | } 243 | 244 | input:checked+#theme-toggle-slider { 245 | background-color: #66bb6a; 246 | } 247 | 248 | input:checked+#theme-toggle-slider:before { 249 | transform: translateX(20px); 250 | } 251 | 252 | #theme-toggle-slider { 253 | border-radius: 20px; 254 | } 255 | 256 | #theme-toggle-slider:before { 257 | border-radius: 50%; 258 | } 259 | -------------------------------------------------------------------------------- /python/jsbeautifier/core/inputscanner.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 4 | # 5 | # Permission is hereby granted, free of charge, to any person 6 | # obtaining a copy of this software and associated documentation files 7 | # (the "Software"), to deal in the Software without restriction, 8 | # including without limitation the rights to use, copy, modify, merge, 9 | # publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, 11 | # subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | import re 26 | 27 | 28 | class InputScanner: 29 | def __init__(self, input_string): 30 | self.__six = __import__("six") 31 | if input_string is None: 32 | input_string = "" 33 | self.__input = input_string 34 | self.__input_length = len(self.__input) 35 | self.__position = 0 36 | 37 | def restart(self): 38 | self.__position = 0 39 | 40 | def back(self): 41 | if self.__position > 0: 42 | self.__position -= 1 43 | 44 | def hasNext(self): 45 | return self.__position < self.__input_length 46 | 47 | def next(self): 48 | val = None 49 | if self.hasNext(): 50 | val = self.__input[self.__position] 51 | self.__position += 1 52 | 53 | return val 54 | 55 | def peek(self, index=0): 56 | val = None 57 | index += self.__position 58 | if index >= 0 and index < self.__input_length: 59 | val = self.__input[index] 60 | 61 | return val 62 | 63 | def test(self, pattern, index=0): 64 | index += self.__position 65 | return ( 66 | index >= 0 67 | and index < self.__input_length 68 | and bool(pattern.match(self.__input, index)) 69 | ) 70 | 71 | def testChar(self, pattern, index=0): 72 | # test one character regex match 73 | val = self.peek(index) 74 | return val is not None and bool(pattern.match(val)) 75 | 76 | def match(self, pattern): 77 | pattern_match = None 78 | if self.hasNext(): 79 | pattern_match = pattern.match(self.__input, self.__position) 80 | if bool(pattern_match): 81 | self.__position = pattern_match.end(0) 82 | return pattern_match 83 | 84 | def read(self, starting_pattern, until_pattern=None, until_after=False): 85 | val = "" 86 | pattern_match = None 87 | if bool(starting_pattern): 88 | pattern_match = self.match(starting_pattern) 89 | if bool(pattern_match): 90 | val = pattern_match.group(0) 91 | 92 | if bool(until_pattern) and (bool(pattern_match) or not bool(starting_pattern)): 93 | val += self.readUntil(until_pattern, until_after) 94 | 95 | return val 96 | 97 | def readUntil(self, pattern, include_match=False): 98 | val = "" 99 | pattern_match = None 100 | match_index = self.__position 101 | if self.hasNext(): 102 | pattern_match = pattern.search(self.__input, self.__position) 103 | if bool(pattern_match): 104 | if include_match: 105 | match_index = pattern_match.end(0) 106 | else: 107 | match_index = pattern_match.start(0) 108 | else: 109 | match_index = self.__input_length 110 | 111 | val = self.__input[self.__position : match_index] 112 | self.__position = match_index 113 | 114 | return val 115 | 116 | def readUntilAfter(self, pattern): 117 | return self.readUntil(pattern, True) 118 | 119 | def get_regexp(self, pattern, match_from=False): 120 | result = None 121 | # strings are converted to regexp 122 | if isinstance(pattern, self.__six.string_types) and pattern != "": 123 | result = re.compile(pattern) 124 | elif pattern is not None: 125 | result = re.compile(pattern.pattern) 126 | return result 127 | 128 | # css beautifier legacy helpers 129 | def peekUntilAfter(self, pattern): 130 | start = self.__position 131 | val = self.readUntilAfter(pattern) 132 | self.__position = start 133 | return val 134 | 135 | def lookBack(self, testVal): 136 | start = self.__position - 1 137 | return ( 138 | start >= len(testVal) 139 | and self.__input[start - len(testVal) : start].lower() == testVal 140 | ) 141 | -------------------------------------------------------------------------------- /js/src/unpackers/p_a_c_k_e_r_unpacker.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 6 | 7 | Permission is hereby granted, free of charge, to any person 8 | obtaining a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 22 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 23 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | */ 27 | 28 | // 29 | // Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier 30 | // 31 | // Coincidentally, it can defeat a couple of other eval-based compressors. 32 | // 33 | // usage: 34 | // 35 | // if (P_A_C_K_E_R.detect(some_string)) { 36 | // var unpacked = P_A_C_K_E_R.unpack(some_string); 37 | // } 38 | // 39 | // 40 | 41 | /*jshint strict:false */ 42 | 43 | var P_A_C_K_E_R = { 44 | detect: function(str) { 45 | return (P_A_C_K_E_R.get_chunks(str).length > 0); 46 | }, 47 | 48 | get_chunks: function(str) { 49 | var chunks = str.match(/eval\(\(?function\(.*?(,0,\{\}\)\)|split\('\|'\)\)\))($|\n)/g); 50 | return chunks ? chunks : []; 51 | }, 52 | 53 | unpack: function(str) { 54 | var chunks = P_A_C_K_E_R.get_chunks(str), 55 | chunk; 56 | for (var i = 0; i < chunks.length; i++) { 57 | chunk = chunks[i].replace(/\n$/, ''); 58 | str = str.split(chunk).join(P_A_C_K_E_R.unpack_chunk(chunk)); 59 | } 60 | return str; 61 | }, 62 | 63 | unpack_chunk: function(str) { 64 | var unpacked_source = ''; 65 | var __eval = eval; 66 | if (P_A_C_K_E_R.detect(str)) { 67 | try { 68 | eval = function(s) { // jshint ignore:line 69 | unpacked_source += s; 70 | return unpacked_source; 71 | }; // jshint ignore:line 72 | __eval(str); 73 | if (typeof unpacked_source === 'string' && unpacked_source) { 74 | str = unpacked_source; 75 | } 76 | } catch (e) { 77 | // well, it failed. we'll just return the original, instead of crashing on user. 78 | } 79 | } 80 | eval = __eval; // jshint ignore:line 81 | return str; 82 | }, 83 | 84 | run_tests: function(sanity_test) { 85 | var t = sanity_test || new SanityTest(); 86 | 87 | var pk1 = "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,{}))"; 88 | var unpk1 = 'var a=1'; 89 | var pk2 = "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,'foo||b'.split('|'),0,{}))"; 90 | var unpk2 = 'foo b=1'; 91 | var pk_broken = "eval(function(p,a,c,k,e,r){BORKBORK;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,{}))"; 92 | var pk3 = "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,{}))"; 93 | var unpk3 = 'var a=1{}))'; 94 | 95 | t.test_function(P_A_C_K_E_R.detect, "P_A_C_K_E_R.detect"); 96 | t.expect('', false); 97 | t.expect('var a = b', false); 98 | t.test_function(P_A_C_K_E_R.unpack, "P_A_C_K_E_R.unpack"); 99 | t.expect(pk_broken, pk_broken); 100 | t.expect(pk1, unpk1); 101 | t.expect(pk2, unpk2); 102 | t.expect(pk3, unpk3); 103 | t.expect("function test (){alert ('This is a test!')}; " + 104 | "eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String))" + 105 | "{while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function" + 106 | "(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp(" + 107 | "'\\b'+e(c)+'\\b','g'),k[c]);return p}('0 2=\\\'{Íâ–+›ï;ã†Ù¥#\\\'',3,3," + 108 | "'var||a'.split('|'),0,{}))", 109 | "function test (){alert ('This is a test!')}; var a='{Íâ–+›ï;ã†Ù¥#'"); 110 | 111 | 112 | var filler = '\nfiller\n'; 113 | t.expect(filler + pk1 + "\n" + pk_broken + filler + pk2 + filler, filler + unpk1 + "\n" + pk_broken + filler + unpk2 + filler); 114 | 115 | return t; 116 | } 117 | 118 | 119 | }; 120 | -------------------------------------------------------------------------------- /tools/template/beautify-html.wrapper.js: -------------------------------------------------------------------------------- 1 | /* AUTO-GENERATED. DO NOT MODIFY. */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | Style HTML 30 | --------------- 31 | 32 | Written by Nochum Sossonko, (nsossonko@hotmail.com) 33 | 34 | Based on code initially developed by: Einar Lielmanis, 35 | https://beautifier.io/ 36 | 37 | Usage: 38 | style_html(html_source); 39 | 40 | style_html(html_source, options); 41 | 42 | The options are: 43 | indent_inner_html (default false) — indent and sections, 44 | indent_size (default 4) — indentation size, 45 | indent_char (default space) — character to indent with, 46 | wrap_line_length (default 250) - maximum amount of characters per line (0 = disable) 47 | brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "none" 48 | put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line, or attempt to keep them where they are. 49 | inline (defaults to inline tags) - list of tags to be considered inline tags 50 | unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted 51 | content_unformatted (defaults to ["pre", "textarea"] tags) - list of tags, whose content shouldn't be reformatted 52 | indent_scripts (default normal) - "keep"|"separate"|"normal" 53 | preserve_newlines (default true) - whether existing line breaks before elements should be preserved 54 | Only works before elements, not inside tags or for text. 55 | max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk 56 | indent_handlebars (default false) - format and indent {{#foo}} and {{/foo}} 57 | end_with_newline (false) - end with a newline 58 | extra_liners (default [head,body,/html]) -List of tags that should have an extra newline before them. 59 | 60 | e.g. 61 | 62 | style_html(html_source, { 63 | 'indent_inner_html': false, 64 | 'indent_size': 2, 65 | 'indent_char': ' ', 66 | 'wrap_line_length': 78, 67 | 'brace_style': 'expand', 68 | 'preserve_newlines': true, 69 | 'max_preserve_newlines': 5, 70 | 'indent_handlebars': false, 71 | 'extra_liners': ['/html'] 72 | }); 73 | */ 74 | 75 | (function() { 76 | 77 | /* GENERATED_BUILD_OUTPUT */ 78 | 79 | var style_html = legacy_beautify_html; 80 | /* Footer */ 81 | if (typeof define === "function" && define.amd) { 82 | // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- ) 83 | define(["require", "./beautify", "./beautify-css"], function(requireamd) { 84 | var js_beautify = requireamd("./beautify"); 85 | var css_beautify = requireamd("./beautify-css"); 86 | 87 | return { 88 | html_beautify: function(html_source, options) { 89 | return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify); 90 | } 91 | }; 92 | }); 93 | } else if (typeof exports !== "undefined") { 94 | // Add support for CommonJS. Just put this file somewhere on your require.paths 95 | // and you will be able to `var html_beautify = require("beautify").html_beautify`. 96 | var js_beautify = require('./beautify.js'); 97 | var css_beautify = require('./beautify-css.js'); 98 | 99 | exports.html_beautify = function(html_source, options) { 100 | return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify); 101 | }; 102 | } else if (typeof window !== "undefined") { 103 | // If we're running a web page and don't have either of the above, add our one global 104 | window.html_beautify = function(html_source, options) { 105 | return style_html(html_source, options, window.js_beautify, window.css_beautify); 106 | }; 107 | } else if (typeof global !== "undefined") { 108 | // If we don't even have window, try global. 109 | global.html_beautify = function(html_source, options) { 110 | return style_html(html_source, options, global.js_beautify, global.css_beautify); 111 | }; 112 | } 113 | 114 | }()); 115 | --------------------------------------------------------------------------------