Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

It's official: python/jsbeautifier.py

Also, move some things around.
  • Loading branch information...
commit ff33268f3aa5f48a69ee1ad25f5631fe87a39960 1 parent 0dd8105
@einars einars authored
View
0  beautify-cl.js → attic/beautify-cl/beautify-cl.js
File renamed without changes
View
0  bin/beautify_js → attic/bin/beautify_js
File renamed without changes
View
0  opera-userscript/beautifier.js → attic/opera-userscript/beautifier.js
File renamed without changes
View
0  opera-userscript/make_opera_userscript.sh → attic/opera-userscript/make_opera_userscript.sh
File renamed without changes
View
0  qtscript/jsbeautify.cpp → attic/qtscript/jsbeautify.cpp
File renamed without changes
View
0  qtscript/jsbeautify.pro → attic/qtscript/jsbeautify.pro
File renamed without changes
View
0  qtscript/jsbeautify.qrc → attic/qtscript/jsbeautify.qrc
File renamed without changes
View
0  qtscript/readme.txt → attic/qtscript/readme.txt
File renamed without changes
View
2  attic/readme.txt
@@ -0,0 +1,2 @@
+Here lay the scripts that were useful at a time, now they are kind of
+obsoleted by the python script, but you may still find them useful.
View
0  unmaintained/bbedit/jsBeautify_BBED.scpt → attic/unmaintained/bbedit/jsBeautify_BBED.scpt
File renamed without changes
View
0  unmaintained/c-sharp/JSBeautify.cs → attic/unmaintained/c-sharp/JSBeautify.cs
File renamed without changes
View
0  v8/README.txt → attic/v8/README.txt
File renamed without changes
View
0  v8/beautify.h → attic/v8/beautify.h
File renamed without changes
View
0  v8/jsbeautify.cpp → attic/v8/jsbeautify.cpp
File renamed without changes
View
11 index.html
@@ -25,8 +25,8 @@
document.getElementById('tabsize').value = tabsize;
}
if (brace_style) {
- if (brace_style===true)
- brace_style = "expand";
+ if (brace_style===true)
+ brace_style = "expand";
document.getElementById('brace-style').value = brace_style;
}
@@ -324,8 +324,7 @@
href="http://github.com/einars/js-beautify">github</a>, and you can download the beautifier for
local use (<a href="http://github.com/einars/js-beautify/zipball/master">zip</a>, <a href="http://github.com/einars/js-beautify/tarball/master">tar.gz</a>) as well.</p>
<h2>Formatting from command-line</h2>
- <p>A stand-alone python version of the beautifier is being made as you read this. <em>I should really sit down and finish this.</em></p>
- <p>Until then, you can use provided beautify-cl.js script, using <a href="http://www.mozilla.org/rhino/">Rhino javascript engine</a>, use <a href="https://github.com/rhoney/jsBeautifier.net">.net version</a>, or take a look around the <a href="https://github.com/einars/js-beautify/">github</a> — there are various wrappers available.</p>
+ <p>For the sake of the glittery unicorns, finally it is here: <a style="color: #c33" href="python/jsbeautifier.py">jsbeautifier.py</a>, enjoy! You can also use it as a library module.</p>
<h2>Support</h2>
<p>The beautifier is — and always will be — completely free and open, so donating is a wonderful thing to do and it will probably make you feel good and warm inside.
<form style="float: left; margin-left: 10px" action="https://www.paypal.com/cgi-bin/webscr" method="post">
@@ -337,6 +336,7 @@
<p style="float:left; padding-top: 2px; margin-left: 3px; "><a href="http://flattr.com/thing/94550/jsbeautifier-org" target="_blank"><img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr" /></a></p>
<div style="clear:both; height: 1px; overflow:hidden;">&nbsp;</div>
<p>If you find some problems with the generated javascript, adapt the script for your favorite editor, or want to say "hi", my email is <a href="mailto:einar@jsbeautifier.org">einar@jsbeautifier.org</a>.</p>
+ <p style="color: #363"><i>Are you doing something neat using the beautifier? Tell me about that, and I'll include a link here!</i></p>
</div>
@@ -345,13 +345,12 @@
<p>Tom Rix has made this into an awesome javascript-beautifying Chrome extension. You can read more about it and install it here: <a href="https://github.com/rixth/jsbeautify-for-chrome">https://github.com/rixth/jsbeautify-for-chrome</a>
<h2>Safari extension</h2>
<p>Sandro Padin has written an extension for the Safari browser. Visit its page and download the extension here: <a href="http://spadin.github.com/js-beautify-safari-extension">http://spadin.github.com/js-beautify-safari-extension</a>
- <h2>Opera addon</h2>
+ <h2>Opera extension</h2>
<p>Dither converted this into an addon for Opera called "readable javascript" — you can install it from <a href=https://addons.opera.com/addons/extensions/details/readable-javascript/1.0-1/"">addons.opera.com</a>, or visit <a href="https://github.com/Dither/readable-javascript">its github project page</a>.</p>
<h3><a href="http://fiddler2.com/">Fiddler</a></h3>
<p>This popular web debugging proxy for Windows has a <a href="http://fiddler2.com/Fiddler2/extensions.asp">Javascript Formatter addon</a> (based on this beautifier) which can reformat javascript on the fly.</p>
<h3>Other editors</h3>
<p>Fabio Nagao has written some <a href="http://github.com/nagaozen/gedit-tunnings/">tips for the gEdit users,</a> among them he tells how to integrate the beautifier into this editor. Infocatcher wrote <a href="http://akelpad.sourceforge.net/forum/viewtopic.php?p=11246#11246">an extension</a> for <a href="http://akelpad.sourceforge.net/en/index.php">AkelPad</a>, a small text editor for Windows.</p>
- <p style="color: #363"><i>Are you doing something neat using the beautifier? Tell me about that, and I'll include a link here!</i></p>
<p>If you're writing javascript code, <a href="http://jslint.com/">JSLint</a> is a really fine piece of software, too. You don't have to follow its recommendations blindly, but understanding what it says about your code can greatly improve your skills.</p>
</div>
</div>
View
2  license.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2009 Einar Lielmanis
+Copyright (c) 2009 - 2011, Einar Lielmanis
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
View
1,074 python/jsbeautifier.py
@@ -0,0 +1,1074 @@
+#!/usr/bin/env python
+
+import sys
+import getopt
+import re
+
+#
+# Originally written by Einar Lielmanis et al.,
+# Conversion to python by Einar Lielmanis, einar@jsbeautifier.org,
+# MIT licence, enjoy.
+#
+# Python is not my native language, feel free to push things around.
+#
+# import jsbeautifier
+# res = jsbeautifier.beautify('your javascript string')
+# res = jsbeautifier.beautify_file('some_file.js')
+#
+# or, specifying options:
+#
+# opts = jsbeautifier.default_options()
+# opts.indent_size = 2
+# res = jsbeautifier.beautify('some javascript', opts)
+#
+#
+# Here are the available options: (read source)
+
+
+class BeautifierOptions:
+ def __init__(self):
+ self.indent_size = 4
+ self.indent_char = ' '
+ self.preserve_newlines = True
+ self.max_preserve_newlines = 10.
+ self.space_after_anon_function = False
+ self.brace_style = 'collapse'
+ self.keep_array_indentation = False
+ self.indent_level = 0
+
+
+
+ def __repr__(self):
+ return \
+"""indent_size = %d
+indent_char = [%s]
+preserve_newlines = %s
+max_preserve_newlines = %d
+space_after_anon_function = %s
+brace_style = %s
+keep_array_indentation = %s
+indent_level = %d
+""" % ( self.indent_size,
+ self.indent_char,
+ self.preserve_newlines,
+ self.max_preserve_newlines,
+ self.space_after_anon_function,
+ self.brace_style,
+ self.keep_array_indentation,
+ self.indent_level)
+
+
+class BeautifierFlags:
+ def __init__(self, mode):
+ self.previous_mode = 'BLOCK'
+ self.mode = mode
+ self.var_line = False
+ self.var_line_tainted = False
+ self.var_line_reindented = False
+ self.in_html_comment = False
+ self.if_line = False
+ self.in_case = False
+ self.eat_next_space = False
+ self.indentation_baseline = -1
+ self.indentation_level = 0
+
+
+
+
+
+
+def default_options():
+ return BeautifierOptions()
+
+class Beautifier:
+
+ def __init__(self, opts = default_options() ):
+
+ self.opts = opts
+ self.blank_state()
+
+ def blank_state(self):
+
+ # internal flags
+ self.flags = BeautifierFlags('BLOCK')
+ self.flag_store = []
+ self.wanted_newline = False
+ self.just_added_newline = False
+ self.do_block_just_closed = False
+
+
+ self.indent_string = self.opts.indent_char * self.opts.indent_size
+ self.last_word = '' # last TK_WORD seen
+ self.last_type = 'TK_START_EXPR' # last token type
+ self.last_text = '' # last token text
+ self.last_last_text = '' # pre-last token text
+
+ self.input = None
+ self.output = [] # formatted javascript gets built here
+
+ self.whitespace = ["\n", "\r", "\t", " "]
+ self.wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'
+ self.digits = '0123456789'
+ self.punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::'.split(' ');
+
+
+ # Words which always should start on a new line
+ self.line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',')
+ self.set_mode('BLOCK')
+
+ global parser_pos
+ parser_pos = 0
+
+
+ def beautify(self, s, opts = None ):
+
+ if opts != None:
+ self.opts = opts
+
+
+ if self.opts.brace_style not in ['expand', 'collapse', 'end-expand']:
+ raise(Exception('opts.brace_style must be "expand", "collapse" or "end-expand".'))
+
+ self.blank_state()
+
+ self.input = s
+
+ parser_pos = 0
+ while True:
+ token_text, token_type = self.get_next_token()
+ # print (token_text, token_type, self.flags.mode)
+ if token_type == 'TK_EOF':
+ break
+
+ handlers = {
+ 'TK_START_EXPR': self.handle_start_expr,
+ 'TK_END_EXPR': self.handle_end_expr,
+ 'TK_START_BLOCK': self.handle_start_block,
+ 'TK_END_BLOCK': self.handle_end_block,
+ 'TK_WORD': self.handle_word,
+ 'TK_SEMICOLON': self.handle_semicolon,
+ 'TK_STRING': self.handle_string,
+ 'TK_EQUALS': self.handle_equals,
+ 'TK_OPERATOR': self.handle_operator,
+ 'TK_BLOCK_COMMENT': self.handle_block_comment,
+ 'TK_INLINE_COMMENT': self.handle_inline_comment,
+ 'TK_COMMENT': self.handle_comment,
+ 'TK_UNKNOWN': self.handle_unknown,
+ }
+
+ handlers[token_type](token_text)
+
+ self.last_last_text = self.last_text
+ self.last_type = token_type
+ self.last_text = token_text
+
+ return re.sub('[\n ]+$', '', ''.join(self.output))
+
+
+ def trim_output(self, eat_newlines = False):
+ while len(self.output) \
+ and (
+ self.output[-1] == ' '\
+ or self.output[-1] == self.indent_string \
+ or (eat_newlines and self.output[-1] in ['\n', '\r'])):
+ self.output = self.output[:-1]
+
+
+ def is_array(self, mode):
+ return mode in ['[EXPRESSION]', '[INDENDED-EXPRESSION]']
+
+
+ def is_expression(self, mode):
+ return mode in ['[EXPRESSION]', '[INDENDED-EXPRESSION]', '(EXPRESSION)']
+
+
+ def print_newline(self, ignore_repeated = True):
+
+ self.flags.eat_next_space = False;
+
+ if self.opts.keep_array_indentation and self.is_array(self.flags.mode):
+ return
+
+ self.flags.if_line = False;
+ self.trim_output();
+
+ if len(self.output) == 0:
+ # no newline on start of file
+ return
+
+ if self.output[-1] != '\n' or not ignore_repeated:
+ self.just_added_newline = True
+ self.output.append('\n')
+
+ for i in range(self.flags.indentation_level):
+ self.output.append(self.indent_string)
+
+ if self.flags.var_line and self.flags.var_line_reindented:
+ if self.opts.indent_char == ' ':
+ # var_line always pushes 4 spaces, so that the variables would be one under another
+ self.output.append(' ')
+ else:
+ self.output.append(self.indent_string)
+
+
+ def print(self, s):
+ if s == ' ':
+ # make sure only single space gets drawn
+ if self.flags.eat_next_space:
+ self.flags.eat_next_space = False
+ elif len(self.output) and self.output[-1] not in [' ', '\n', self.indent_string]:
+ self.output.append(' ')
+ else:
+ self.just_added_newline = False
+ self.flags.eat_next_space = False
+ self.output.append(s)
+
+
+ def indent(self):
+ self.flags.indentation_level = self.flags.indentation_level + 1
+
+
+ def remove_indent(self):
+ if len(self.output) and self.output[-1] == self.indent_string:
+ self.output = self.output[:-1]
+
+
+ def set_mode(self, mode):
+
+ prev = BeautifierFlags('BLOCK')
+
+ if self.flags:
+ self.flag_store.append(self.flags)
+ prev = self.flags
+
+ self.flags = BeautifierFlags(mode)
+
+ if len(self.flag_store) == 1:
+ self.flags.indentation_level = self.opts.indent_level
+ else:
+ self.flags.indentation_level = prev.indentation_level
+ if prev.var_line and prev.var_line_reindented:
+ self.flags.indentation_level = self.flags.indentation_level + 1
+ self.flags.previous_mode = prev.mode
+
+
+ def restore_mode(self):
+ self.do_block_just_closed = self.flags.mode == 'DO_BLOCK'
+ if len(self.flag_store) > 0:
+ self.flags = self.flag_store[-1]
+ self.flag_store = self.flag_store[:-1]
+
+
+ def is_ternary_op(self):
+ level = 0
+ colon_count = 0
+ for i in range(len(self.output) - 1, -1, -1):
+ c = self.output[i]
+ if c == ':':
+ if level == 0:
+ colon_count = colon_count + 1
+ elif c == '?':
+ if level == 0:
+ if colon_count == 0:
+ return True
+ else:
+ colon_count = colon_count - 1
+ elif c == '{':
+ if level == 0:
+ return False
+ level = level - 1
+ elif c in '([':
+ level = level - 1
+ elif c in ')]}':
+ level = level + 1
+ return False
+
+ def get_next_token(self):
+
+ global parser_pos
+
+ self.n_newlines = 0
+
+ if parser_pos >= len(self.input):
+ return '', 'TK_EOF'
+
+ self.wanted_newline = False;
+ c = self.input[parser_pos]
+ parser_pos += 1
+
+ keep_whitespace = self.opts.keep_array_indentation and self.is_array(self.flags.mode)
+
+ if keep_whitespace:
+ # slight mess to allow nice preservation of array indentation and reindent that correctly
+ # first time when we get to the arrays:
+ # var a = [
+ # ....'something'
+ # we make note of whitespace_count = 4 into flags.indentation_baseline
+ # so we know that 4 whitespaces in original source match indent_level of reindented source
+ #
+ # and afterwards, when we get to
+ # 'something,
+ # .......'something else'
+ # we know that this should be indented to indent_level + (7 - indentation_baseline) spaces
+
+ whitespace_count = 0
+ while c in self.whitespace:
+ if c == '\n':
+ self.trim_output()
+ self.output.append('\n')
+ self.just_added_newline = True
+ whitespace_count = 0
+ elif c == '\t':
+ whitespace_count += 4
+ elif c == '\r':
+ pass
+ else:
+ whitespace_count += 1
+
+ if parser_pos >= len(self.input):
+ return '', 'TK_EOF'
+
+ c = self.input[parser_pos]
+ parser_pos += 1
+
+ if self.flags.indentation_baseline == -1:
+
+ self.flags.indentation_baseline = whitespace_count
+
+ if self.just_added_newline:
+ for i in range(self.flags.indentation_level + 1):
+ self.output.append(self.indent_string)
+
+ if self.flags.indentation_baseline != -1:
+ for i in range(whitespace_count - self.flags.indentation_baseline):
+ self.output.append(' ')
+
+ else: # not keep_whitespace
+ while c in self.whitespace:
+ if c == '\n':
+ if self.opts.max_preserve_newlines == 0 or self.opts.max_preserve_newlines > self.n_newlines:
+ self.n_newlines += 1
+
+ if parser_pos >= len(self.input):
+ return '', 'TK_EOF'
+
+ c = self.input[parser_pos]
+ parser_pos += 1
+
+ if self.opts.preserve_newlines and self.n_newlines > 1:
+ for i in range(self.n_newlines):
+ self.print_newline(i == 0)
+ self.just_added_newline = True
+
+ self.wanted_newline = self.n_newlines > 0
+
+
+ if c in self.wordchar:
+ if parser_pos < len(self.input):
+ while self.input[parser_pos] in self.wordchar:
+ c = c + self.input[parser_pos]
+ parser_pos += 1
+ if parser_pos == len(self.input):
+ break
+
+ # small and surprisingly unugly hack for 1E-10 representation
+ if parser_pos != len(self.input) and self.input[parser_pos] in '+-' \
+ and re.match('^[0-9]+[Ee]$', c):
+
+ sign = self.input[parser_pos]
+ parser_pos += 1
+ t = self.get_next_token()
+ c += sign + t[0]
+ return c, 'TK_WORD'
+
+ if c == 'in': # in is an operator, need to hack
+ return c, 'TK_OPERATOR'
+
+ if self.wanted_newline and \
+ self.last_type != 'TK_OPERATOR' and\
+ not self.flags.if_line and \
+ (self.opts.preserve_newlines or self.last_text != 'var'):
+
+ self.print_newline()
+
+ return c, 'TK_WORD'
+
+ if c in '([':
+ return c, 'TK_START_EXPR'
+
+ if c in ')]':
+ return c, 'TK_END_EXPR'
+
+ if c == '{':
+ return c, 'TK_START_BLOCK'
+
+ if c == '}':
+ return c, 'TK_END_BLOCK'
+
+ if c == ';':
+ return c, 'TK_SEMICOLON'
+
+ if c == '/':
+ comment = ''
+ inline_comment = True
+ comment_mode = 'TK_INLINE_COMMENT'
+ if self.input[parser_pos] == '*': # peek /* .. */ comment
+ parser_pos += 1
+ if parser_pos < len(self.input):
+ while not (self.input[parser_pos] == '*' and \
+ parser_pos + 1 < len(self.input) and \
+ self.input[parser_pos + 1] == '/')\
+ and parser_pos < len(self.input):
+ c = self.input[parser_pos]
+ comment += c
+ if c in '\r\n':
+ comment_mode = 'TK_BLOCK_COMMENT'
+ parser_pos += 1
+ if parser_pos >= len(self.input):
+ break
+ parser_pos += 2
+ return '/*' + comment + '*/', comment_mode
+ if self.input[parser_pos] == '/': # peek // comment
+ comment = c
+ while self.input[parser_pos] not in '\r\n':
+ comment += self.input[parser_pos]
+ parser_pos += 1
+ if parser_pos >= len(self.input):
+ break
+ parser_pos += 1
+ if self.wanted_newline:
+ self.print_newline()
+ return comment, 'TK_COMMENT'
+
+
+
+ if c == "'" or c == '"' or \
+ (c == '/' and ((self.last_type == 'TK_WORD' and self.last_text in ['return', 'do']) or \
+ (self.last_type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR',
+ 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON']))):
+ sep = c
+ esc = False
+ resulting_string = c
+ in_char_class = False
+
+ if parser_pos < len(self.input):
+ if sep == '/':
+ # handle regexp
+ in_char_class = False
+ while esc or in_char_class or self.input[parser_pos] != sep:
+ resulting_string += self.input[parser_pos]
+ if not esc:
+ esc = self.input[parser_pos] == '\\'
+ if self.input[parser_pos] == '[':
+ in_char_class = True
+ elif self.input[parser_pos] == ']':
+ in_char_class = False
+ else:
+ esc = False
+ parser_pos += 1
+ if parser_pos >= len(self.input):
+ # incomplete regex when end-of-file reached
+ # bail out with what has received so far
+ return resulting_string, 'TK_STRING'
+ else:
+ # handle string
+ while esc or self.input[parser_pos] != sep:
+ resulting_string += self.input[parser_pos]
+ if not esc:
+ esc = self.input[parser_pos] == '\\'
+ else:
+ esc = False
+ parser_pos += 1
+ if parser_pos >= len(self.input):
+ # incomplete string when end-of-file reached
+ # bail out with what has received so far
+ return resulting_string, 'TK_STRING'
+
+
+ parser_pos += 1
+ resulting_string += sep
+ if sep == '/':
+ # regexps may have modifiers /regexp/MOD, so fetch those too
+ while parser_pos < len(self.input) and self.input[parser_pos] in self.wordchar:
+ resulting_string += self.input[parser_pos]
+ parser_pos += 1
+ return resulting_string, 'TK_STRING'
+
+ if c == '#':
+
+ # she-bang
+ if len(self.output) == 0 and len(self.input) > 1 and self.input[parser_pos] == '!':
+ resulting_string = c
+ while parser_pos < len(self.input) and c != '\n':
+ c = self.input[parser_pos]
+ resulting_string += c
+ parser_pos += 1
+ self.output.append(resulting_string.strip() + "\n")
+ self.print_newline()
+ return self.get_next_token()
+
+
+ # Spidermonkey-specific sharp variables for circular references
+ # https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
+ # http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
+ sharp = '#'
+ if parser_pos < len(self.input) and self.input[parser_pos] in self.digits:
+ while True:
+ c = self.input[parser_pos]
+ sharp += c
+ parser_pos += 1
+ if parser_pos >= len(self.input) or c == '#' or c == '=':
+ break
+ if c == '#' or parser_pos >= len(self.input):
+ pass
+ elif self.input[parser_pos] == '[' and self.input[parser_pos + 1] == ']':
+ sharp += '[]'
+ parser_pos += 2
+ elif self.input[parser_pos] == '{' and self.input[parser_pos + 1] == '}':
+ sharp += '{}'
+ parser_pos += 2
+ return sharp, 'TK_WORD'
+
+ if c == '<' and self.input[parser_pos - 1 : parser_pos + 3] == '<!--':
+ parser_pos += 3
+ self.flags.in_html_comment = True
+ return '<!--', 'TK_COMMENT'
+
+ if c == '-' and self.flags.in_html_comment and self.input[parser_pos - 1 : parser_pos + 2] == '-->':
+ self.flags.in_html_comment = False
+ parser_pos += 2
+ if self.wanted_newline:
+ self.print_newline()
+ return '-->', 'TK_COMMENT'
+
+ if c in self.punct:
+ while parser_pos < len(self.input) and c + self.input[parser_pos] in self.punct:
+ c += self.input[parser_pos]
+ parser_pos += 1
+ if parser_pos >= len(self.input):
+ break
+ if c == '=':
+ return c, 'TK_EQUALS'
+ else:
+ return c, 'TK_OPERATOR'
+ return c, 'TK_UNKNOWN'
+
+
+
+ def handle_start_expr(self, token_text):
+ if token_text == '[':
+ if self.last_type == 'TK_WORD' or self.last_text == ')':
+ if self.last_text in self.line_starters:
+ self.print(' ')
+ self.set_mode('(EXPRESSION)')
+ self.print(token_text)
+ return
+
+ if self.flags.mode in ['[EXPRESSION]', '[INDENTED-EXPRESSION]']:
+ if self.last_last_text == ']' and self.last_text == ',':
+ # ], [ goes to a new line
+ if self.flags.mode == '[EXPRESSION]':
+ self.flags.mode = '[INDENTED-EXPRESSION]'
+ if not self.opts.keep_array_indentation:
+ self.indent()
+ self.set_mode('[EXPRESSION]')
+ if not self.opts.keep_array_indentation:
+ self.print_newline()
+ elif self.last_text == '[':
+ if self.flags.mode == '[EXPRESSION]':
+ self.flags.mode = '[INDENTED-EXPRESSION]'
+ if not self.opts.keep_array_indentation:
+ self.indent()
+ self.set_mode('[EXPRESSION]')
+
+ if not self.opts.keep_array_indentation:
+ self.print_newline()
+ else:
+ self.set_mode('[EXPRESSION]')
+ else:
+ self.set_mode('[EXPRESSION]')
+ else:
+ self.set_mode('(EXPRESSION)')
+
+
+ if self.last_text == ';' or self.last_type == 'TK_START_BLOCK':
+ self.print_newline()
+ elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK'] or self.last_text == '.':
+ # do nothing on (( and )( and ][ and ]( and .(
+ pass
+ elif self.last_type not in ['TK_WORD', 'TK_OPERATOR']:
+ self.print(' ')
+ elif self.last_word == 'function':
+ # function() vs function ()
+ if self.opts.space_after_anon_function:
+ self.print(' ')
+ elif self.last_text in self.line_starters or self.last_text == 'catch':
+ self.print(' ')
+
+ self.print(token_text)
+
+
+ def handle_end_expr(self, token_text):
+ if token_text == ']':
+ if self.opts.keep_array_indentation:
+ if self.last_text == '}':
+ self.remove_indent()
+ self.print(token_text)
+ self.restore_mode()
+ return
+ else:
+ if self.flags.mode == '[INDENTED-EXPRESSION]':
+ if self.last_text == ']':
+ self.restore_mode()
+ self.print_newline()
+ self.print(token_text)
+ return
+ self.restore_mode()
+ self.print(token_text)
+
+
+ def handle_start_block(self, token_text):
+ if self.last_word == 'do':
+ self.set_mode('DO_BLOCK')
+ else:
+ self.set_mode('BLOCK')
+
+ if self.opts.brace_style == 'expand':
+ if self.last_type != 'TK_OPERATOR':
+ if self.last_text in ['return', '=']:
+ self.print(' ')
+ else:
+ self.print_newline(True)
+
+ self.print(token_text)
+ self.indent()
+ else:
+ if self.last_type not in ['TK_OPERATOR', 'TK_START_EXPR']:
+ if self.last_type == 'TK_START_BLOCK':
+ self.print_newline()
+ else:
+ self.print(' ')
+ else:
+ # if TK_OPERATOR or TK_START_EXPR
+ if self.is_array(self.flags.previous_mode) and self.last_text == ',':
+ if self.last_last_text == '}':
+ self.print(' ')
+ else:
+ self.print_newline()
+ self.indent()
+ self.print(token_text)
+
+
+
+
+
+ def handle_end_block(self, token_text):
+ self.restore_mode()
+ if self.opts.brace_style == 'expand':
+ if self.last_text != '{':
+ self.print_newline()
+ else:
+ if self.last_type == 'TK_START_BLOCK':
+ if self.just_added_newline:
+ self.remove_indent()
+ else:
+ # {}
+ self.trim_output()
+ else:
+ if self.is_array(self.flags.mode) and self.opts.keep_array_indentation:
+ self.opts.keep_array_indentation = False
+ self.print_newline()
+ self.opts.keep_array_indentation = True
+ else:
+ self.print_newline()
+
+ self.print(token_text)
+
+
+ def handle_word(self, token_text):
+ if self.do_block_just_closed:
+ self.print(' ')
+ self.print(token_text)
+ self.print(' ')
+ self.do_block_just_closed = False
+ return
+
+ if token_text == 'function':
+ if (self.just_added_newline or self.last_text == ';') and self.last_text != '{':
+ # make sure there is a nice clean space of at least one blank line
+ # before a new function definition
+ have_newlines = self.n_newlines
+ if not self.just_added_newline:
+ have_newlines = 0
+ if not self.opts.preserve_newlines:
+ have_newlines = 1
+ for i in range(2 - have_newlines):
+ self.print_newline(False)
+
+ if token_text in ['case', 'default']:
+ if self.last_text == ':':
+ self.remove_indent()
+ else:
+ self.flags.indentation_level -= 1
+ self.print_newline()
+ self.flags.indentation_level += 1
+ self.print(token_text)
+ self.flags.in_case = True
+ return
+
+ prefix = 'NONE'
+
+ if self.last_type == 'TK_END_BLOCK':
+ if token_text in ['else', 'catch', 'finally']:
+ prefix = 'NEWLINE'
+ else:
+ if self.opts.brace_style in ['expand', 'end-expand']:
+ prefix = 'NEWLINE'
+ else:
+ prefix = 'SPACE'
+ self.print(' ')
+ elif self.last_type == 'TK_SEMICOLON' and self.flags.mode in ['BLOCK', 'DO_BLOCK']:
+ prefix = 'NEWLINE'
+ elif self.last_type == 'TK_SEMICOLON' and self.is_expression(self.flags.mode):
+ prefix = 'SPACE'
+ elif self.last_type == 'TK_STRING':
+ prefix = 'NEWLINE'
+ elif self.last_type == 'TK_WORD':
+ if self.last_text == 'else':
+ # eat newlines between ...else *** some_op...
+ # won't preserve extra newlines in this place (if any), but don't care that much
+ self.trim_output(True);
+ prefix = 'SPACE'
+ elif self.last_type == 'TK_START_BLOCK':
+ prefix = 'NEWLINE'
+ elif self.last_type == 'TK_END_EXPR':
+ self.print(' ')
+ prefix = 'NEWLINE'
+
+ if self.flags.if_line and self.last_type == 'TK_END_EXPR':
+ self.flags.if_line = False
+
+ if token_text in self.line_starters:
+ prefix = 'NEWLINE'
+
+ if token_text in ['else', 'catch', 'finally']:
+ if self.last_type != 'TK_END_BLOCK' \
+ or self.opts.brace_style == 'expand' \
+ or self.opts.brace_style == 'end-expand':
+ self.print_newline()
+ else:
+ self.trim_output(True)
+ self.print(' ')
+ elif prefix == 'NEWLINE':
+ if token_text == 'function' and (self.last_type == 'TK_START_EXPR' or self.last_text in '=,'):
+ # no need to force newline on "function" -
+ # (function...
+ pass
+ elif self.last_text in ['return', 'throw']:
+ # no newline between return nnn
+ print(' ')
+ elif self.last_type != 'TK_END_EXPR':
+ if (self.last_type != 'TK_START_EXPR' or token_text != 'var') and self.last_text != ':':
+ # no need to force newline on VAR -
+ # for (var x = 0...
+ if token_text == 'if' and self.last_word == 'else' and self.last_text != '{':
+ self.print(' ')
+ else:
+ self.print_newline()
+ elif token_text in self.line_starters and self.last_text != ')':
+ self.print_newline()
+ elif self.is_array(self.flags.mode) and self.last_text == ',' and self.last_last_text == '}':
+ self.print_newline() # }, in lists get a newline
+ elif prefix == 'SPACE':
+ self.print(' ')
+
+
+ self.print(token_text)
+ self.last_word = token_text
+
+ if token_text == 'var':
+ self.flags.var_line = True
+ self.flags.var_line_reindented = False
+ self.flags.var_line_tainted = False
+
+
+ if token_text == 'if':
+ self.flags.if_line = True
+
+ if token_text == 'else':
+ self.flags.if_line = False
+
+
+ def handle_semicolon(self, token_text):
+ self.print(token_text)
+ self.flags.var_line = False
+ self.flags.var_line_reindented = False
+
+
+ def handle_string(self, token_text):
+ if self.last_type in ['TK_START_BLOCK', 'TK_END_BLOCK', 'TK_SEMICOLON']:
+ self.print_newline()
+ elif self.last_type == 'TK_WORD':
+ self.print(' ')
+
+ self.print(token_text)
+
+
+ def handle_equals(self, token_text):
+ if self.flags.var_line:
+ # just got an '=' in a var-line, different line breaking rules will apply
+ self.flags.var_line_tainted = True
+
+ self.print(' ')
+ self.print(token_text)
+ self.print(' ')
+
+
+ def handle_operator(self, token_text):
+ space_before = True
+ space_after = True
+
+ if self.flags.var_line and token_text == ',' and self.is_expression(self.flags.mode):
+ # do not break on comma, for ( var a = 1, b = 2
+ self.flags.var_line_tainted = False
+
+ if self.flags.var_line and token_text == ',':
+ if self.flags.var_line_tainted:
+ self.print(token_text)
+ self.flags.var_line_reindented = True
+ self.flags.var_line_tainted = False
+ self.print_newline()
+ return
+ else:
+ self.flags.var_line_tainted = False
+
+ if self.last_text in ['return', 'throw']:
+ # return had a special handling in TK_WORD
+ self.print(' ')
+ self.print(token_text)
+ return
+
+ if token_text == ':' and self.flags.in_case:
+ self.print(token_text)
+ self.print_newline()
+ self.flags.in_case = False
+ return
+
+ if token_text == '::':
+ # no spaces around the exotic namespacing syntax operator
+ self.print(token_text)
+ return
+
+ if token_text == ',':
+ if self.flags.var_line:
+ if self.flags.var_line_tainted:
+ # This never happens, as it's handled previously, right?
+ self.print(token_text)
+ self.print_newline()
+ self.flags.var_line_tainted = False
+ else:
+ self.print(token_text)
+ self.print(' ')
+ elif self.last_type == 'TK_END_BLOCK' and self.flags.mode != '(EXPRESSION)':
+ self.print(token_text)
+ if self.flags.mode == 'OBJECT' and self.last_text == '}':
+ self.print_newline()
+ else:
+ self.print(' ')
+ else:
+ if self.flags.mode == 'OBJECT':
+ self.print(token_text)
+ self.print_newline()
+ else:
+ # EXPR or DO_BLOCK
+ self.print(token_text)
+ self.print(' ')
+ # comma handled
+ return
+ elif token_text in ['--', '++', '!'] \
+ or (token_text in ['+', '-'] \
+ and self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) \
+ or self.last_text in self.line_starters:
+
+ space_before = False
+ space_after = False
+
+ if self.last_text == ';' and self.is_expression(self.flags.mode):
+ # for (;; ++i)
+ # ^^
+ space_before = True
+
+ if self.last_type == 'TK_WORD' and self.last_text in self.line_starters:
+ space_before = True
+
+ if self.flags.mode == 'BLOCK' and self.last_text in ['{', ';']:
+ # { foo: --i }
+ # foo(): --bar
+ self.print_newline()
+
+ elif token_text == '.':
+ # decimal digits or object.property
+ space_before = False
+
+ elif token_text == ':':
+ if not self.is_ternary_op():
+ self.flags.mode = 'OBJECT'
+ space_before = False
+
+ if space_before:
+ self.print(' ')
+
+ self.print(token_text)
+
+ if space_after:
+ self.print(' ')
+
+
+
+
+ def handle_block_comment(self, token_text):
+
+ lines = token_text.replace('\x0d', '').split('\x0a')
+ if token_text[:3] == '/**':
+ # javadoc: reformat and reindent
+ self.print_newline()
+ self.print(lines[0])
+ for line in lines[1:]:
+ self.print_newline()
+ self.print(' ' + line.strip())
+ else:
+ # simple block comment: leave intact
+ if len(lines) > 1:
+ # multiline comment starts on a new line
+ self.print_newline()
+ self.trim_output()
+ else:
+ # single line /* ... */ comment stays on the same line
+ self.print(' ')
+ for line in lines:
+ self.print(line)
+ self.print('\n')
+ self.print_newline()
+
+
+ def handle_inline_comment(self, token_text):
+ self.print(' ')
+ self.print(token_text)
+ if self.is_expression(self.flags.mode):
+ self.print(' ')
+ else:
+ self.print_newline()
+
+
+ def handle_comment(self, token_text):
+ if self.wanted_newline:
+ self.print_newline()
+ else:
+ self.print(' ')
+
+ self.print(token_text)
+ self.print_newline()
+
+
+ def handle_unknown(self, token_text):
+ if self.last_text in ['return', 'throw']:
+ self.print(' ')
+
+ self.print(token_text)
+
+
+def beautify(string, opts = default_options() ):
+ b = Beautifier()
+ return b.beautify(string, opts)
+
+
+def beautify_file(file_name, opts = default_options() ):
+
+ if file_name == '-': # stdin
+ f = sys.stdin
+ else:
+ f = open(file_name)
+
+ b = Beautifier()
+ return b.beautify(''.join(f.readlines()), opts)
+
+
+def usage():
+
+ print("""Javascript beautifier (http://jsbeautifier.org/)
+
+Usage: jsbeautifier.py [options] <infile>
+
+ <infile> can be "-", which means stdin.
+
+Input options:
+
+ -i, --stdin read input from stdin
+
+Output options:
+
+ -s, --indent-size=NUMBER indentation size. (default 4).
+ -c, --indent-char=CHAR character to indent with. (default space).
+ -d, --disable-preserve-newlines do not preserve existing line breaks.
+ -f, --space-after-anon-function add space between "function ()".
+ -b, --brace-style=collapse brace style (collapse, expand, end-expand)
+ -k, --keep-array-indentation keep array indentation.
+
+Rarely needed options:
+
+ -l, --indent-level=NUMBER initial indentation level. (default 0).
+
+ -h, --help, --usage prints this help statement.
+
+""");
+
+
+
+def main():
+
+ argv = sys.argv[1:]
+
+ try:
+ opts, args = getopt.getopt(argv, "s:c:dfbkil:h", ['indent-size=','indent-char=', 'disable-preserve-newlines',
+ 'space-after-anon-function', 'brace-style=',
+ 'keep-array-indentation', 'indent-level=', 'help',
+ 'usage', 'stdin'])
+ except getopt.GetoptError:
+ usage()
+ sys.exit(2)
+
+ js_options = default_options()
+
+ file = None
+ if len(args) == 1:
+ file = args[0]
+
+ for opt, arg in opts:
+ if opt in ('--indent-size', '-s'):
+ js_options.indent_size = int(arg)
+ elif opt in ('--indent-char', '-c'):
+ js_options.indent_char = arg
+ elif opt in ('--disable-preserve_newlines', '-d'):
+ js_options.preserve_newlines = False
+ elif opt in ('--space-after-anon-function', '-f'):
+ js_options.space_after_anon_function = True
+ elif opt in ('--brace-style', '-b'):
+ js_options.brace_style = arg
+ elif opt in ('--indent-level', '-l'):
+ js_options.indent_level = int(arg)
+ elif opt in ('--stdin', '-i'):
+ file = '-'
+ elif opt in ('--help', '--usage', '--h'):
+ return usage()
+
+ if file == None:
+ return usage()
+ else:
+ print(beautify_file(file, js_options))
+
+
+
+if __name__ == "__main__":
+ main()
+
+
View
424 python/test-jsbeautifier.py
@@ -0,0 +1,424 @@
+#!/usr/bin/env python
+
+import sys
+from sys import stdout
+import re
+
+from jsbeautifier import Beautifier, default_options
+
+tests_passed = 0
+
+opts = default_options()
+
+opts.indent_size = 4
+opts.indent_char = ' '
+opts.preserve_newlines = True
+opts.space_after_anon_function = True
+opts.keep_array_indentation = False
+opts.brace_style = 'collapse'
+
+
+def test_fragment(input, expectation = None):
+ global opts
+ if expectation == None:
+ expectation = input
+
+ b = Beautifier(opts)
+ res = b.beautify(input)
+ if res != expectation:
+ print("""=== TEST FAILED ===
+%s
+Input:
+------
+%s
+
+Expected:
+---------
+%s
+
+Received:
+---------
+%s
+""" % (b.opts, input, expectation, res))
+ sys.exit(-1) # bail out immediately
+
+ global tests_passed
+ tests_passed += 1
+
+def bt(input, expectation = None):
+ global opts
+ if expectation == None:
+ expectation = input
+ test_fragment(input, expectation)
+ if opts.indent_size == 4 and len(input):
+ wrapped_input = '{\n%s\nfoo=bar;}' % input
+ wrapped_expectation = '{\n%s\n foo = bar;\n}' % re.sub('^(.+)$', ' \\1', expectation, 0, re.MULTILINE)
+ test_fragment(wrapped_input, wrapped_expectation)
+
+def main():
+
+ global opts
+
+ bt('');
+ bt('return .5');
+ bt('a = 1', 'a = 1');
+ bt('a=1', 'a = 1');
+ bt("a();\n\nb();", "a();\n\nb();");
+ bt('var a = 1 var b = 2', "var a = 1\nvar b = 2");
+ bt('var a=1, b=c[d], e=6;', 'var a = 1,\n b = c[d],\n e = 6;');
+ bt('a = " 12345 "');
+ bt("a = ' 12345 '");
+ bt('if (a == 1) b = 2;', "if (a == 1) b = 2;");
+ bt('if(1){2}else{3}', "if (1) {\n 2\n} else {\n 3\n}");
+ bt('if (foo) bar();\nelse\ncar();', 'if (foo) bar();\nelse car();');
+ bt('if(1||2);', 'if (1 || 2);');
+ bt('(a==1)||(b==2)', '(a == 1) || (b == 2)');
+ bt('var a = 1 if (2) 3;', "var a = 1\nif (2) 3;");
+ bt('a = a + 1');
+ bt('a = a == 1');
+ bt('/12345[^678]*9+/.match(a)');
+ bt('a /= 5');
+ bt('a = 0.5 * 3');
+ bt('a *= 10.55');
+ bt('a < .5');
+ bt('a <= .5');
+ bt('a<.5', 'a < .5');
+ bt('a<=.5', 'a <= .5');
+ bt('a = 0xff;');
+ bt('a=0xff+4', 'a = 0xff + 4');
+ bt('a = [1, 2, 3, 4]');
+ bt('F*(g/=f)*g+b', 'F * (g /= f) * g + b');
+ bt('a.b({c:d})', "a.b({\n c: d\n})");
+ bt('a.b\n(\n{\nc:\nd\n}\n)', "a.b({\n c: d\n})");
+ bt('a=!b', 'a = !b');
+ bt('a?b:c', 'a ? b : c');
+ bt('a?1:2', 'a ? 1 : 2');
+ bt('a?(b):c', 'a ? (b) : c');
+ bt('x={a:1,b:w=="foo"?x:y,c:z}', 'x = {\n a: 1,\n b: w == "foo" ? x : y,\n c: z\n}');
+ bt('x=a?b?c?d:e:f:g;', 'x = a ? b ? c ? d : e : f : g;');
+ bt('x=a?b?c?d:{e1:1,e2:2}:f:g;', 'x = a ? b ? c ? d : {\n e1: 1,\n e2: 2\n} : f : g;');
+ bt('function void(void) {}');
+ bt('if(!a)foo();', 'if (!a) foo();');
+ bt('a=~a', 'a = ~a');
+ bt('a;/*comment*/b;', "a; /*comment*/\nb;");
+ bt('a;/* comment */b;', "a; /* comment */\nb;");
+ test_fragment('a;/*\ncomment\n*/b;', "a;\n/*\ncomment\n*/\nb;"); # simple comments don't get touched at all
+ bt('a;/**\n* javadoc\n*/b;', "a;\n/**\n * javadoc\n */\nb;");
+ bt('if(a)break;', "if (a) break;");
+ bt('if(a){break}', "if (a) {\n break\n}");
+ bt('if((a))foo();', 'if ((a)) foo();');
+ bt('for(var i=0;;)', 'for (var i = 0;;)');
+ bt('a++;', 'a++;');
+ bt('for(;;i++)', 'for (;; i++)');
+ bt('for(;;++i)', 'for (;; ++i)');
+ bt('return(1)', 'return (1)');
+ bt('try{a();}catch(b){c();}finally{d();}', "try {\n a();\n} catch (b) {\n c();\n} finally {\n d();\n}");
+ bt('(xx)()'); # magic function call
+ bt('a[1]()'); # another magic function call
+ bt('if(a){b();}else if(c) foo();', "if (a) {\n b();\n} else if (c) foo();");
+ bt('switch(x) {case 0: case 1: a(); break; default: break}', "switch (x) {\ncase 0:\ncase 1:\n a();\n break;\ndefault:\n break\n}");
+ bt('switch(x){case -1:break;case !y:break;}', 'switch (x) {\ncase -1:\n break;\ncase !y:\n break;\n}');
+ bt('a !== b');
+ bt('if (a) b(); else c();', "if (a) b();\nelse c();");
+ bt("// comment\n(function something() {})"); # typical greasemonkey start
+ bt("{\n\n x();\n\n}"); # was: duplicating newlines
+ bt('if (a in b) foo();');
+ #bt('var a, b');
+ bt('{a:1, b:2}', "{\n a: 1,\n b: 2\n}");
+ bt('a={1:[-1],2:[+1]}', 'a = {\n 1: [-1],\n 2: [+1]\n}');
+ bt('var l = {\'a\':\'1\', \'b\':\'2\'}', "var l = {\n 'a': '1',\n 'b': '2'\n}");
+ bt('if (template.user[n] in bk) foo();');
+ bt('{{}/z/}', "{\n {}\n /z/\n}");
+ bt('return 45', "return 45");
+ bt('If[1]', "If[1]");
+ bt('Then[1]', "Then[1]");
+ bt('a = 1e10', "a = 1e10");
+ bt('a = 1.3e10', "a = 1.3e10");
+ bt('a = 1.3e-10', "a = 1.3e-10");
+ bt('a = -1.3e-10', "a = -1.3e-10");
+ bt('a = 1e-10', "a = 1e-10");
+ bt('a = e - 10', "a = e - 10");
+ bt('a = 11-10', "a = 11 - 10");
+ bt("a = 1;// comment\n", "a = 1; // comment");
+ bt("a = 1; // comment\n", "a = 1; // comment");
+ bt("a = 1;\n // comment\n", "a = 1;\n// comment");
+
+ bt('o = [{a:b},{c:d}]', 'o = [{\n a: b\n}, {\n c: d\n}]');
+
+ bt("if (a) {\n do();\n}"); # was: extra space appended
+ bt("if\n(a)\nb();", "if (a) b();"); # test for proper newline removal
+
+ bt("if (a) {\n// comment\n}else{\n// comment\n}", "if (a) {\n // comment\n} else {\n // comment\n}"); # if/else statement with empty body
+ bt("if (a) {\n// comment\n// comment\n}", "if (a) {\n // comment\n // comment\n}"); # multiple comments indentation
+ bt("if (a) b() else c();", "if (a) b()\nelse c();");
+ bt("if (a) b() else if c() d();", "if (a) b()\nelse if c() d();");
+
+ bt("{}");
+ bt("{\n\n}");
+ bt("do { a(); } while ( 1 );", "do {\n a();\n} while (1);");
+ bt("do {} while (1);");
+ bt("do {\n} while (1);", "do {} while (1);");
+ bt("do {\n\n} while (1);");
+ bt("var a = x(a, b, c)");
+ bt("delete x if (a) b();", "delete x\nif (a) b();");
+ bt("delete x[x] if (a) b();", "delete x[x]\nif (a) b();");
+ bt("for(var a=1,b=2)", "for (var a = 1, b = 2)");
+ bt("for(var a=1,b=2,c=3)", "for (var a = 1, b = 2, c = 3)");
+ bt("for(var a=1,b=2,c=3;d<3;d++)", "for (var a = 1, b = 2, c = 3; d < 3; d++)");
+ bt("function x(){(a||b).c()}", "function x() {\n (a || b).c()\n}");
+ bt("function x(){return - 1}", "function x() {\n return -1\n}");
+ bt("function x(){return ! a}", "function x() {\n return !a\n}");
+
+ # a common snippet in jQuery plugins
+ bt("settings = $.extend({},defaults,settings);", "settings = $.extend({}, defaults, settings);");
+
+ bt('{xxx;}()', '{\n xxx;\n}()');
+
+ bt("a = 'a'\nb = 'b'");
+ bt("a = /reg/exp");
+ bt("a = /reg/");
+ bt('/abc/.test()');
+ bt('/abc/i.test()');
+ bt("{/abc/i.test()}", "{\n /abc/i.test()\n}");
+ bt('var x=(a)/a;', 'var x = (a) / a;');
+
+ bt('x != -1', 'x != -1');
+
+ bt('for (; s-->0;)', 'for (; s-- > 0;)');
+ bt('for (; s++>0;)', 'for (; s++ > 0;)');
+ bt('a = s++>s--;', 'a = s++ > s--;');
+ bt('a = s++>--s;', 'a = s++ > --s;');
+
+ bt('{x=#1=[]}', '{\n x = #1=[]\n}');
+ bt('{a:#1={}}', '{\n a: #1={}\n}');
+ bt('{a:#1#}', '{\n a: #1#\n}');
+
+ test_fragment('{a:1},{a:2}', '{\n a: 1\n}, {\n a: 2\n}');
+ test_fragment('var ary=[{a:1}, {a:2}];', 'var ary = [{\n a: 1\n}, {\n a: 2\n}];');
+
+ test_fragment('{a:#1', '{\n a: #1'); # incomplete
+ test_fragment('{a:#', '{\n a: #'); # incomplete
+
+ test_fragment('}}}', '}\n}\n}'); # incomplete
+
+ test_fragment('<!--\nvoid();\n// -->', '<!--\nvoid();\n// -->');
+
+ test_fragment('a=/regexp', 'a = /regexp'); # incomplete regexp
+
+ bt('{a:#1=[],b:#1#,c:#999999#}', '{\n a: #1=[],\n b: #1#,\n c: #999999#\n}');
+
+ bt("a = 1e+2");
+ bt("a = 1e-2");
+ bt("do{x()}while(a>1)", "do {\n x()\n} while (a > 1)");
+
+ bt("x(); /reg/exp.match(something)", "x();\n/reg/exp.match(something)");
+
+ test_fragment("something();(", "something();\n(");
+ test_fragment("#!she/bangs, she bangs\nf=1", "#!she/bangs, she bangs\n\nf = 1");
+ test_fragment("#!she/bangs, she bangs\n\nf=1", "#!she/bangs, she bangs\n\nf = 1");
+ test_fragment("#!she/bangs, she bangs\n\n/* comment */", "#!she/bangs, she bangs\n\n/* comment */");
+ test_fragment("#!she/bangs, she bangs\n\n\n/* comment */", "#!she/bangs, she bangs\n\n\n/* comment */");
+ test_fragment("#", "#");
+ test_fragment("#!", "#!");
+
+ bt("function namespace::something()");
+
+ test_fragment("<!--\nsomething();\n-->", "<!--\nsomething();\n-->");
+ test_fragment("<!--\nif(i<0){bla();}\n-->", "<!--\nif (i < 0) {\n bla();\n}\n-->");
+
+ test_fragment("<!--\nsomething();\n-->\n<!--\nsomething();\n-->", "<!--\nsomething();\n-->\n<!--\nsomething();\n-->");
+ test_fragment("<!--\nif(i<0){bla();}\n-->\n<!--\nif(i<0){bla();}\n-->", "<!--\nif (i < 0) {\n bla();\n}\n-->\n<!--\nif (i < 0) {\n bla();\n}\n-->");
+
+ bt('{foo();--bar;}', '{\n foo();\n --bar;\n}');
+ bt('{foo();++bar;}', '{\n foo();\n ++bar;\n}');
+ bt('{--bar;}', '{\n --bar;\n}');
+ bt('{++bar;}', '{\n ++bar;\n}');
+
+ # regexps
+ bt('a(/abc\\/\\/def/);b()', "a(/abc\\/\\/def/);\nb()");
+ bt('a(/a[b\\[\\]c]d/);b()', "a(/a[b\\[\\]c]d/);\nb()");
+ test_fragment('a(/a[b\\[', "a(/a[b\\["); # incomplete char class
+ # allow unescaped / in char classes
+ bt('a(/[a/b]/);b()', "a(/[a/b]/);\nb()");
+
+ bt('a=[[1,2],[4,5],[7,8]]', "a = [\n [1, 2],\n [4, 5],\n [7, 8]\n]");
+ bt('a=[a[1],b[4],c[d[7]]]', "a = [a[1], b[4], c[d[7]]]");
+ bt('[1,2,[3,4,[5,6],7],8]', "[1, 2, [3, 4, [5, 6], 7], 8]");
+
+ bt('[[["1","2"],["3","4"]],[["5","6","7"],["8","9","0"]],[["1","2","3"],["4","5","6","7"],["8","9","0"]]]',
+ '[\n [\n ["1", "2"],\n ["3", "4"]\n ],\n [\n ["5", "6", "7"],\n ["8", "9", "0"]\n ],\n [\n ["1", "2", "3"],\n ["4", "5", "6", "7"],\n ["8", "9", "0"]\n ]\n]');
+
+ bt('{[x()[0]];indent;}', '{\n [x()[0]];\n indent;\n}');
+
+ bt('return ++i', 'return ++i');
+ bt('return !!x', 'return !!x');
+ bt('return !x', 'return !x');
+ bt('return [1,2]', 'return [1, 2]');
+ bt('return;', 'return;');
+ bt('return\nfunc', 'return\nfunc');
+ bt('catch(e)', 'catch (e)');
+
+ bt('var a=1,b={foo:2,bar:3},c=4;', 'var a = 1,\n b = {\n foo: 2,\n bar: 3\n },\n c = 4;');
+
+ # inline comment
+ bt('function x(/*int*/ start, /*string*/ foo)', 'function x( /*int*/ start, /*string*/ foo)');
+
+ # javadoc comment
+ bt('/**\n* foo\n*/', '/**\n * foo\n */');
+ bt(' /**\n * foo\n */', '/**\n * foo\n */');
+ bt('{\n/**\n* foo\n*/\n}', '{\n /**\n * foo\n */\n}');
+
+ bt('var a,b,c=1,d,e,f=2;', 'var a, b, c = 1,\n d, e, f = 2;');
+ bt('var a,b,c=[],d,e,f=2;', 'var a, b, c = [],\n d, e, f = 2;');
+ bt('function () {\n var a, b, c, d, e = [],\n f;\n}');
+
+ bt('x();\n\nfunction(){}', 'x();\n\nfunction () {}');
+
+ bt('do/regexp/;\nwhile(1);', 'do /regexp/;\nwhile (1);'); # hmmm
+
+ bt('var a = a,\na;\nb = {\nb\n}', 'var a = a,\n a;\nb = {\n b\n}');
+
+ bt('var a = a,\n /* c */\n b;');
+ bt('var a = a,\n // c\n b;');
+
+ bt('foo.("bar");'); # weird element referencing
+
+
+ bt('if (a) a()\nelse b()\nnewline()');
+ bt('if (a) a()\nnewline()');
+
+ opts.space_after_anon_function = True;
+
+ test_fragment("// comment 1\n(function()", "// comment 1\n(function ()"); # typical greasemonkey start
+ bt("var a1, b1, c1, d1 = 0, c = function() {}, d = '';", "var a1, b1, c1, d1 = 0,\n c = function () {},\n d = '';");
+ bt('var o1=$.extend(a);function(){alert(x);}', 'var o1 = $.extend(a);\n\nfunction () {\n alert(x);\n}');
+
+ opts.space_after_anon_function = False;
+
+ test_fragment("// comment 2\n(function()", "// comment 2\n(function()"); # typical greasemonkey start
+ bt("var a2, b2, c2, d2 = 0, c = function() {}, d = '';", "var a2, b2, c2, d2 = 0,\n c = function() {},\n d = '';");
+ bt('var o2=$.extend(a);function(){alert(x);}', 'var o2 = $.extend(a);\n\nfunction() {\n alert(x);\n}');
+
+ bt('{"x":[{"a":1,"b":3},7,8,8,8,8,{"b":99},{"a":11}]}', '{\n "x": [{\n "a": 1,\n "b": 3\n },\n 7, 8, 8, 8, 8,\n {\n "b": 99\n }, {\n "a": 11\n }]\n}');
+
+ bt('{"1":{"1a":"1b"},"2"}', '{\n "1": {\n "1a": "1b"\n },\n "2"\n}');
+ bt('{a:{a:b},c}', '{\n a: {\n a: b\n },\n c\n}');
+
+ bt('{[y[a]];keep_indent;}', '{\n [y[a]];\n keep_indent;\n}');
+
+ bt('if (x) {y} else { if (x) {y}}', 'if (x) {\n y\n} else {\n if (x) {\n y\n }\n}');
+
+ bt('if (foo) one()\ntwo()\nthree()');
+ bt('if (1 + foo() && bar(baz()) / 2) one()\ntwo()\nthree()');
+ bt('if (1 + foo() && bar(baz()) / 2) one();\ntwo();\nthree();');
+
+ opts.indent_size = 1;
+ opts.indent_char = ' ';
+ bt('{ one_char() }', "{\n one_char()\n}");
+
+ bt('var a,b=1,c=2', 'var a, b = 1,\n c = 2');
+
+ opts.indent_size = 4;
+ opts.indent_char = ' ';
+ bt('{ one_char() }', "{\n one_char()\n}");
+
+ opts.indent_size = 1;
+ opts.indent_char = "\t";
+ bt('{ one_char() }', "{\n\tone_char()\n}");
+ bt('x = a ? b : c; x;', 'x = a ? b : c;\nx;');
+
+ opts.indent_size = 4;
+ opts.indent_char = ' ';
+
+ opts.preserve_newlines = False;
+ bt('var\na=dont_preserve_newlines;', 'var a = dont_preserve_newlines;');
+
+ # make sure the blank line between function definitions stays
+ # even when preserve_newlines = False
+ bt('function foo() {\n return 1;\n}\n\nfunction foo() {\n return 1;\n}');
+ bt('function foo() {\n return 1;\n}\nfunction foo() {\n return 1;\n}',
+ 'function foo() {\n return 1;\n}\n\nfunction foo() {\n return 1;\n}'
+ );
+ bt('function foo() {\n return 1;\n}\n\n\nfunction foo() {\n return 1;\n}',
+ 'function foo() {\n return 1;\n}\n\nfunction foo() {\n return 1;\n}'
+ );
+
+
+ opts.preserve_newlines = True;
+ bt('var\na=do_preserve_newlines;', 'var\na = do_preserve_newlines;');
+
+ opts.keep_array_indentation = True;
+
+ bt('var x = [{}\n]', 'var x = [{}\n]');
+ bt('var x = [{foo:bar}\n]', 'var x = [{\n foo: bar\n}\n]');
+ bt("a = ['something',\n'completely',\n'different'];\nif (x);", "a = ['something',\n 'completely',\n 'different'];\nif (x);");
+ bt("a = ['a','b','c']", "a = ['a', 'b', 'c']");
+ bt("a = ['a', 'b','c']", "a = ['a', 'b', 'c']");
+
+ bt("x = [{'a':0}]", "x = [{\n 'a': 0\n}]");
+
+ bt('{a([[a1]], {b;});}', '{\n a([[a1]], {\n b;\n });\n}');
+
+ bt('a = //comment\n/regex/;');
+
+ test_fragment('/*\n * X\n */');
+ test_fragment('/*\r\n * X\r\n */', '/*\n * X\n */');
+
+ bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a) {\n b;\n} else {\n c;\n}');
+
+
+ opts.brace_style = 'expand';
+
+ bt('var a=1,b={foo:2,bar:3},c=4;', 'var a = 1,\n b = {\n foo: 2,\n bar: 3\n },\n c = 4;');
+ bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a)\n{\n b;\n}\nelse\n{\n c;\n}');
+ test_fragment('if (foo) {', 'if (foo)\n{');
+ test_fragment('foo {', 'foo\n{');
+ test_fragment('return {', 'return {'); # return needs the brace. maybe something else as well: feel free to report.
+ # test_fragment('return\n{', 'return\n{'); # can't support this?, but that's an improbable and extreme case anyway.
+ test_fragment('return;\n{', 'return;\n{');
+
+ bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a)\n{\n b;\n}\nelse\n{\n c;\n}');
+ bt('var foo = {}');
+ bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a)\n{\n b;\n}\nelse\n{\n c;\n}');
+ test_fragment('if (foo) {', 'if (foo)\n{');
+ test_fragment('foo {', 'foo\n{');
+ test_fragment('return {', 'return {'); # return needs the brace. maybe something else as well: feel free to report.
+ # test_fragment('return\n{', 'return\n{'); # can't support this?, but that's an improbable and extreme case anyway.
+ test_fragment('return;\n{', 'return;\n{');
+
+
+ opts.brace_style = 'collapse';
+
+ bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a) {\n b;\n} else {\n c;\n}');
+ test_fragment('if (foo) {', 'if (foo) {');
+ test_fragment('foo {', 'foo {');
+ test_fragment('return {', 'return {'); # return needs the brace. maybe something else as well: feel free to report.
+ # test_fragment('return\n{', 'return\n{'); # can't support this?, but that's an improbable and extreme case anyway.
+ test_fragment('return;\n{', 'return; {');
+
+ bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a) {\n b;\n} else {\n c;\n}');
+ test_fragment('if (foo) {', 'if (foo) {');
+ test_fragment('foo {', 'foo {');
+ test_fragment('return {', 'return {'); # return needs the brace. maybe something else as well: feel free to report.
+ # test_fragment('return\n{', 'return\n{'); # can't support this?, but that's an improbable and extreme case anyway.
+ test_fragment('return;\n{', 'return; {');
+
+ opts.brace_style = "end-expand";
+
+ bt('if(1){2}else{3}', "if (1) {\n 2\n}\nelse {\n 3\n}");
+ bt('try{a();}catch(b){c();}finally{d();}', "try {\n a();\n}\ncatch (b) {\n c();\n}\nfinally {\n d();\n}");
+ bt('if(a){b();}else if(c) foo();', "if (a) {\n b();\n}\nelse if (c) foo();");
+ bt("if (a) {\n// comment\n}else{\n// comment\n}", "if (a) {\n // comment\n}\nelse {\n // comment\n}"); # if/else statement with empty body
+ bt('if (x) {y} else { if (x) {y}}', 'if (x) {\n y\n}\nelse {\n if (x) {\n y\n }\n}');
+ bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a) {\n b;\n}\nelse {\n c;\n}');
+
+
+
+ global tests_passed
+ print("All %d tests passed." % tests_passed)
+
+
+if __name__ == "__main__":
+ main()
+
+
View
32 readme.txt
@@ -7,21 +7,29 @@
javascript, unpack scripts packed by the popular Dean Edward's packer,
as well as deobfuscate scripts processed by javascriptobfuscator.com.
- You are free to use this in any way you want, in case you find this
- useful or working for you.
+ To beautify from the command-line you can use provided jsbeautifer.py
+ script/library. Run it to see its usage information; e.g
+ "./jsbeautifier.py file.js" beautifies a file, output goes to stdout.
+
+ To use jsbeautifier.py as a library is simple:
+
+ import jsbeautifier
+ res = jsbeautifier.beautify('your javascript string')
+ res = jsbeautifier.beautify_file('some_file.js')
- To beautify from the command-line you can use provided beautify-cl.js
- script, using Rhino javascript engine. See the file contents for the
- details.
+ or, to specify some options,
- Alternatively, you can use the V8-based or QtScript-based command-line
- versions, see the v8/ and qtscript/ subdirectories for the details.
-
- There is a .net wrapper (written by Rahul Singla) as well:
- https://github.com/rhoney/jsBeautifier.net
+ opts = jsbeautifier.default_options()
+ opts.indent_size = 2
+ res = jsbeautifier.beautify('some javascript', opts)
+ Note that only the html+js version supports unpacking various packers.
+
+ You are free to use this in any way you want, in case you find this
+ useful or working for you.
Written by Einar Lielmanis, <einar@jsbeautifier.org>
- Thanks to Jason Diamond, Patrick Hof, Nochum Sossonko, Andreas Schneider,
- Dave Vasilevsky, Vital Batmanov, Ron Baldwin, Gabriel Harrison and others.
+ Thanks to Jason Diamond, Patrick Hof, Nochum Sossonko, Andreas Schneider, Dave
+ Vasilevsky, Vital Batmanov, Ron Baldwin, Gabriel Harrison, Chris J. Hull and
+ others.
Please sign in to comment.
Something went wrong with that request. Please try again.