js-beautify 1.10.2 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/README.md +15 -10
- package/js/lib/beautifier.js +95 -42
- package/js/lib/beautifier.min.js +1 -1
- package/js/lib/beautify-css.js +26 -5
- package/js/lib/beautify-html.js +60 -32
- package/js/lib/beautify.js +9 -5
- package/js/lib/cli.js +1 -0
- package/js/lib/unpackers/p_a_c_k_e_r_unpacker.js +8 -0
- package/js/src/cli.js +1 -0
- package/js/src/css/beautifier.js +16 -5
- package/js/src/css/options.js +10 -0
- package/js/src/html/beautifier.js +54 -29
- package/js/src/html/tokenizer.js +6 -3
- package/js/src/javascript/acorn.js +2 -2
- package/js/src/javascript/tokenizer.js +7 -3
- package/js/src/unpackers/p_a_c_k_e_r_unpacker.js +8 -0
- package/package.json +12 -11
package/js/lib/beautify-css.js
CHANGED
|
@@ -1404,23 +1404,34 @@ Beautifier.prototype.beautify = function() {
|
|
|
1404
1404
|
insidePropertyValue = false;
|
|
1405
1405
|
this.outdent();
|
|
1406
1406
|
}
|
|
1407
|
-
this.indent();
|
|
1408
|
-
this._output.space_before_token = true;
|
|
1409
|
-
this.print_string(this._ch);
|
|
1410
1407
|
|
|
1411
1408
|
// when entering conditional groups, only rulesets are allowed
|
|
1412
1409
|
if (enteringConditionalGroup) {
|
|
1413
1410
|
enteringConditionalGroup = false;
|
|
1414
|
-
insideRule = (this._indentLevel
|
|
1411
|
+
insideRule = (this._indentLevel >= this._nestedLevel);
|
|
1415
1412
|
} else {
|
|
1416
1413
|
// otherwise, declarations are also allowed
|
|
1417
|
-
insideRule = (this._indentLevel >= this._nestedLevel);
|
|
1414
|
+
insideRule = (this._indentLevel >= this._nestedLevel - 1);
|
|
1418
1415
|
}
|
|
1419
1416
|
if (this._options.newline_between_rules && insideRule) {
|
|
1420
1417
|
if (this._output.previous_line && this._output.previous_line.item(-1) !== '{') {
|
|
1421
1418
|
this._output.ensure_empty_line_above('/', ',');
|
|
1422
1419
|
}
|
|
1423
1420
|
}
|
|
1421
|
+
|
|
1422
|
+
this._output.space_before_token = true;
|
|
1423
|
+
|
|
1424
|
+
// The difference in print_string and indent order is necessary to indent the '{' correctly
|
|
1425
|
+
if (this._options.brace_style === 'expand') {
|
|
1426
|
+
this._output.add_new_line();
|
|
1427
|
+
this.print_string(this._ch);
|
|
1428
|
+
this.indent();
|
|
1429
|
+
this._output.set_indent(this._indentLevel);
|
|
1430
|
+
} else {
|
|
1431
|
+
this.indent();
|
|
1432
|
+
this.print_string(this._ch);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1424
1435
|
this.eatWhitespace(true);
|
|
1425
1436
|
this._output.add_new_line();
|
|
1426
1437
|
} else if (this._ch === '}') {
|
|
@@ -1628,6 +1639,16 @@ function Options(options) {
|
|
|
1628
1639
|
var space_around_selector_separator = this._get_boolean('space_around_selector_separator');
|
|
1629
1640
|
this.space_around_combinator = this._get_boolean('space_around_combinator') || space_around_selector_separator;
|
|
1630
1641
|
|
|
1642
|
+
var brace_style_split = this._get_selection_list('brace_style', ['collapse', 'expand', 'end-expand', 'none', 'preserve-inline']);
|
|
1643
|
+
this.brace_style = 'collapse';
|
|
1644
|
+
for (var bs = 0; bs < brace_style_split.length; bs++) {
|
|
1645
|
+
if (brace_style_split[bs] !== 'expand') {
|
|
1646
|
+
// default to collapse, as only collapse|expand is implemented for now
|
|
1647
|
+
this.brace_style = 'collapse';
|
|
1648
|
+
} else {
|
|
1649
|
+
this.brace_style = brace_style_split[bs];
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1631
1652
|
}
|
|
1632
1653
|
Options.prototype = new BaseOptions();
|
|
1633
1654
|
|
package/js/lib/beautify-html.js
CHANGED
|
@@ -1983,7 +1983,7 @@ var get_custom_beautifier_name = function(tag_check, raw_token) {
|
|
|
1983
1983
|
// For those without a type attribute use default;
|
|
1984
1984
|
if (typeAttribute.search('text/css') > -1) {
|
|
1985
1985
|
result = 'css';
|
|
1986
|
-
} else if (typeAttribute.search(/(text|application|dojo)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json|method|aspect)/) > -1) {
|
|
1986
|
+
} else if (typeAttribute.search(/module|((text|application|dojo)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json|method|aspect))/) > -1) {
|
|
1987
1987
|
result = 'javascript';
|
|
1988
1988
|
} else if (typeAttribute.search(/(text|application|dojo)\/(x-)?(html)/) > -1) {
|
|
1989
1989
|
result = 'html';
|
|
@@ -2370,10 +2370,12 @@ Beautifier.prototype._handle_tag_open = function(printer, raw_token, last_tag_to
|
|
|
2370
2370
|
var parser_token = this._get_tag_open_token(raw_token);
|
|
2371
2371
|
|
|
2372
2372
|
if ((last_tag_token.is_unformatted || last_tag_token.is_content_unformatted) &&
|
|
2373
|
+
!last_tag_token.is_empty_element &&
|
|
2373
2374
|
raw_token.type === TOKEN.TAG_OPEN && raw_token.text.indexOf('</') === 0) {
|
|
2374
2375
|
// End element tags for unformatted or content_unformatted elements
|
|
2375
2376
|
// are printed raw to keep any newlines inside them exactly the same.
|
|
2376
2377
|
printer.add_raw_token(raw_token);
|
|
2378
|
+
parser_token.start_tag_token = this._tag_stack.try_pop(parser_token.tag_name);
|
|
2377
2379
|
} else {
|
|
2378
2380
|
printer.traverse_whitespace(raw_token);
|
|
2379
2381
|
this._set_tag_position(printer, raw_token, parser_token, last_tag_token, last_token);
|
|
@@ -2429,8 +2431,13 @@ var TagOpenParserToken = function(parent, raw_token) {
|
|
|
2429
2431
|
tag_check_match = raw_token.text.match(/^<([^\s>]*)/);
|
|
2430
2432
|
this.tag_check = tag_check_match ? tag_check_match[1] : '';
|
|
2431
2433
|
} else {
|
|
2432
|
-
tag_check_match = raw_token.text.match(/^{{[
|
|
2434
|
+
tag_check_match = raw_token.text.match(/^{{(?:[\^]|#\*?)?([^\s}]+)/);
|
|
2433
2435
|
this.tag_check = tag_check_match ? tag_check_match[1] : '';
|
|
2436
|
+
|
|
2437
|
+
// handle "{{#> myPartial}}
|
|
2438
|
+
if (raw_token.text === '{{#>' && this.tag_check === '>' && raw_token.next !== null) {
|
|
2439
|
+
this.tag_check = raw_token.next.text;
|
|
2440
|
+
}
|
|
2434
2441
|
}
|
|
2435
2442
|
this.tag_check = this.tag_check.toLowerCase();
|
|
2436
2443
|
|
|
@@ -2477,12 +2484,8 @@ Beautifier.prototype._set_tag_position = function(printer, raw_token, parser_tok
|
|
|
2477
2484
|
// and do an ending needed
|
|
2478
2485
|
if (this._do_optional_end_element(parser_token)) {
|
|
2479
2486
|
if (!parser_token.is_inline_element) {
|
|
2480
|
-
if (parser_token.parent) {
|
|
2481
|
-
parser_token.parent.multiline_content = true;
|
|
2482
|
-
}
|
|
2483
2487
|
printer.print_newline(false);
|
|
2484
2488
|
}
|
|
2485
|
-
|
|
2486
2489
|
}
|
|
2487
2490
|
|
|
2488
2491
|
this._tag_stack.record_tag(parser_token); //push it on the tag stack
|
|
@@ -2519,21 +2522,28 @@ Beautifier.prototype._set_tag_position = function(printer, raw_token, parser_tok
|
|
|
2519
2522
|
if (parser_token.tag_name === '!--' && last_token.type === TOKEN.TAG_CLOSE &&
|
|
2520
2523
|
last_tag_token.is_end_tag && parser_token.text.indexOf('\n') === -1) {
|
|
2521
2524
|
//Do nothing. Leave comments on same line.
|
|
2522
|
-
} else
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
printer.print_newline(false);
|
|
2525
|
+
} else {
|
|
2526
|
+
if (!(parser_token.is_inline_element || parser_token.is_unformatted)) {
|
|
2527
|
+
printer.print_newline(false);
|
|
2528
|
+
}
|
|
2529
|
+
this._calcluate_parent_multiline(printer, parser_token);
|
|
2528
2530
|
}
|
|
2529
2531
|
} else if (parser_token.is_end_tag) { //this tag is a double tag so check for tag-ending
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
)
|
|
2532
|
+
var do_end_expand = false;
|
|
2533
|
+
|
|
2534
|
+
// deciding whether a block is multiline should not be this hard
|
|
2535
|
+
do_end_expand = parser_token.start_tag_token && parser_token.start_tag_token.multiline_content;
|
|
2536
|
+
do_end_expand = do_end_expand || (!parser_token.is_inline_element &&
|
|
2537
|
+
!(last_tag_token.is_inline_element || last_tag_token.is_unformatted) &&
|
|
2538
|
+
!(last_token.type === TOKEN.TAG_CLOSE && parser_token.start_tag_token === last_tag_token) &&
|
|
2539
|
+
last_token.type !== 'TK_CONTENT'
|
|
2540
|
+
);
|
|
2541
|
+
|
|
2542
|
+
if (parser_token.is_content_unformatted || parser_token.is_unformatted) {
|
|
2543
|
+
do_end_expand = false;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
if (do_end_expand) {
|
|
2537
2547
|
printer.print_newline(false);
|
|
2538
2548
|
}
|
|
2539
2549
|
} else { // it's a start-tag
|
|
@@ -2549,17 +2559,25 @@ Beautifier.prototype._set_tag_position = function(printer, raw_token, parser_tok
|
|
|
2549
2559
|
}
|
|
2550
2560
|
}
|
|
2551
2561
|
|
|
2552
|
-
if (!parser_token.is_inline_element
|
|
2553
|
-
|
|
2554
|
-
parser_token.parent.multiline_content = true;
|
|
2555
|
-
}
|
|
2562
|
+
if (!(parser_token.is_inline_element || parser_token.is_unformatted) &&
|
|
2563
|
+
(last_token.type !== 'TK_CONTENT' || parser_token.is_content_unformatted)) {
|
|
2556
2564
|
printer.print_newline(false);
|
|
2557
2565
|
}
|
|
2566
|
+
|
|
2567
|
+
this._calcluate_parent_multiline(printer, parser_token);
|
|
2568
|
+
}
|
|
2569
|
+
};
|
|
2570
|
+
|
|
2571
|
+
Beautifier.prototype._calcluate_parent_multiline = function(printer, parser_token) {
|
|
2572
|
+
if (parser_token.parent && printer._output.just_added_newline() &&
|
|
2573
|
+
!((parser_token.is_inline_element || parser_token.is_unformatted) && parser_token.parent.is_inline_element)) {
|
|
2574
|
+
parser_token.parent.multiline_content = true;
|
|
2558
2575
|
}
|
|
2559
2576
|
};
|
|
2560
2577
|
|
|
2561
2578
|
//To be used for <p> tag special case:
|
|
2562
|
-
|
|
2579
|
+
var p_closers = ['address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hr', 'main', 'nav', 'ol', 'p', 'pre', 'section', 'table', 'ul'];
|
|
2580
|
+
var p_parent_excludes = ['a', 'audio', 'del', 'ins', 'map', 'noscript', 'video'];
|
|
2563
2581
|
|
|
2564
2582
|
Beautifier.prototype._do_optional_end_element = function(parser_token) {
|
|
2565
2583
|
var result = null;
|
|
@@ -2570,7 +2588,9 @@ Beautifier.prototype._do_optional_end_element = function(parser_token) {
|
|
|
2570
2588
|
if (parser_token.is_empty_element || !parser_token.is_start_tag || !parser_token.parent) {
|
|
2571
2589
|
return;
|
|
2572
2590
|
|
|
2573
|
-
}
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
if (parser_token.tag_name === 'body') {
|
|
2574
2594
|
// A head element’s end tag may be omitted if the head element is not immediately followed by a space character or a comment.
|
|
2575
2595
|
result = result || this._tag_stack.try_pop('head');
|
|
2576
2596
|
|
|
@@ -2587,11 +2607,16 @@ Beautifier.prototype._do_optional_end_element = function(parser_token) {
|
|
|
2587
2607
|
result = result || this._tag_stack.try_pop('dt', ['dl']);
|
|
2588
2608
|
result = result || this._tag_stack.try_pop('dd', ['dl']);
|
|
2589
2609
|
|
|
2590
|
-
//} else if (p_closers.indexOf(parser_token.tag_name) !== -1) {
|
|
2591
|
-
//TODO: THIS IS A BUG FARM. We are not putting this into 1.8.0 as it is likely to blow up.
|
|
2592
|
-
//A p element’s end tag may be omitted if the p element is immediately followed by an address, article, aside, blockquote, details, div, dl, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hr, main, nav, ol, p, pre, section, table, or ul element, or if there is no more content in the parent element and the parent element is an HTML element that is not an a, audio, del, ins, map, noscript, or video element, or an autonomous custom element.
|
|
2593
|
-
//result = result || this._tag_stack.try_pop('p', ['body']);
|
|
2594
2610
|
|
|
2611
|
+
} else if (parser_token.parent.tag_name === 'p' && p_closers.indexOf(parser_token.tag_name) !== -1) {
|
|
2612
|
+
// IMPORTANT: this else-if works because p_closers has no overlap with any other element we look for in this method
|
|
2613
|
+
// check for the parent element is an HTML element that is not an <a>, <audio>, <del>, <ins>, <map>, <noscript>, or <video> element, or an autonomous custom element.
|
|
2614
|
+
// To do this right, this needs to be coded as an inclusion of the inverse of the exclusion above.
|
|
2615
|
+
// But to start with (if we ignore "autonomous custom elements") the exclusion would be fine.
|
|
2616
|
+
var p_parent = parser_token.parent.parent;
|
|
2617
|
+
if (!p_parent || p_parent_excludes.indexOf(p_parent.tag_name) === -1) {
|
|
2618
|
+
result = result || this._tag_stack.try_pop('p');
|
|
2619
|
+
}
|
|
2595
2620
|
} else if (parser_token.tag_name === 'rp' || parser_token.tag_name === 'rt') {
|
|
2596
2621
|
// An rt element’s end tag may be omitted if the rt element is immediately followed by an rt or rp element, or if there is no more content in the parent element.
|
|
2597
2622
|
// An rp element’s end tag may be omitted if the rp element is immediately followed by an rt or rp element, or if there is no more content in the parent element.
|
|
@@ -2832,7 +2857,7 @@ var Tokenizer = function(input_string, options) {
|
|
|
2832
2857
|
word: templatable_reader.until(/[\n\r\t <]/),
|
|
2833
2858
|
single_quote: templatable_reader.until_after(/'/),
|
|
2834
2859
|
double_quote: templatable_reader.until_after(/"/),
|
|
2835
|
-
attribute: templatable_reader.until(/[\n\r\t
|
|
2860
|
+
attribute: templatable_reader.until(/[\n\r\t =>]|\/>/),
|
|
2836
2861
|
element_name: templatable_reader.until(/[\n\r\t >\/]/),
|
|
2837
2862
|
|
|
2838
2863
|
handlebars_comment: pattern_reader.starting_with(/{{!--/).until_after(/--}}/),
|
|
@@ -2891,8 +2916,8 @@ Tokenizer.prototype._get_next_token = function(previous_token, open_token) { //
|
|
|
2891
2916
|
|
|
2892
2917
|
token = token || this._read_open_handlebars(c, open_token);
|
|
2893
2918
|
token = token || this._read_attribute(c, previous_token, open_token);
|
|
2894
|
-
token = token || this._read_raw_content(c, previous_token, open_token);
|
|
2895
2919
|
token = token || this._read_close(c, open_token);
|
|
2920
|
+
token = token || this._read_raw_content(c, previous_token, open_token);
|
|
2896
2921
|
token = token || this._read_content_word(c);
|
|
2897
2922
|
token = token || this._read_comment_or_cdata(c);
|
|
2898
2923
|
token = token || this._read_processing(c);
|
|
@@ -3055,7 +3080,9 @@ Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token)
|
|
|
3055
3080
|
var resulting_string = '';
|
|
3056
3081
|
if (open_token && open_token.text[0] === '{') {
|
|
3057
3082
|
resulting_string = this.__patterns.handlebars_raw_close.read();
|
|
3058
|
-
} else if (previous_token.type === TOKEN.TAG_CLOSE &&
|
|
3083
|
+
} else if (previous_token.type === TOKEN.TAG_CLOSE &&
|
|
3084
|
+
previous_token.opened.text[0] === '<' && previous_token.text[0] !== '/') {
|
|
3085
|
+
// ^^ empty tag has no content
|
|
3059
3086
|
var tag_name = previous_token.opened.text.substr(1).toLowerCase();
|
|
3060
3087
|
if (tag_name === 'script' || tag_name === 'style') {
|
|
3061
3088
|
// Script and style tags are allowed to have comments wrapping their content
|
|
@@ -3067,6 +3094,7 @@ Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token)
|
|
|
3067
3094
|
}
|
|
3068
3095
|
resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
|
|
3069
3096
|
} else if (this._is_content_unformatted(tag_name)) {
|
|
3097
|
+
|
|
3070
3098
|
resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
|
|
3071
3099
|
}
|
|
3072
3100
|
}
|
package/js/lib/beautify.js
CHANGED
|
@@ -2197,11 +2197,11 @@ module.exports.Token = Token;
|
|
|
2197
2197
|
|
|
2198
2198
|
// acorn used char codes to squeeze the last bit of performance out
|
|
2199
2199
|
// Beautifier is okay without that, so we're using regex
|
|
2200
|
-
// permit $ (36) and @ (64). @ is used in ES7 decorators.
|
|
2200
|
+
// permit # (23), $ (36), and @ (64). @ is used in ES7 decorators.
|
|
2201
2201
|
// 65 through 91 are uppercase letters.
|
|
2202
2202
|
// permit _ (95).
|
|
2203
2203
|
// 97 through 123 are lowercase letters.
|
|
2204
|
-
var baseASCIIidentifierStartChars = "\\x24\\x40\\x41-\\x5a\\x5f\\x61-\\x7a";
|
|
2204
|
+
var baseASCIIidentifierStartChars = "\\x23\\x24\\x40\\x41-\\x5a\\x5f\\x61-\\x7a";
|
|
2205
2205
|
|
|
2206
2206
|
// inside an identifier @ is not allowed but 0-9 are.
|
|
2207
2207
|
var baseASCIIidentifierChars = "\\x24\\x30-\\x39\\x41-\\x5a\\x5f\\x61-\\x7a";
|
|
@@ -2619,7 +2619,7 @@ var dot_pattern = /[^\d\.]/;
|
|
|
2619
2619
|
|
|
2620
2620
|
var positionable_operators = (
|
|
2621
2621
|
">>> === !== " +
|
|
2622
|
-
"<< && >= ** != == <= >> || " +
|
|
2622
|
+
"<< && >= ** != == <= >> || ?? |> " +
|
|
2623
2623
|
"< / - + > : & % ? ^ | *").split(' ');
|
|
2624
2624
|
|
|
2625
2625
|
// IMPORTANT: this must be sorted longest to shortest or tokenizing many not work.
|
|
@@ -2627,10 +2627,12 @@ var positionable_operators = (
|
|
|
2627
2627
|
var punct =
|
|
2628
2628
|
">>>= " +
|
|
2629
2629
|
"... >>= <<= === >>> !== **= " +
|
|
2630
|
-
"=> ^= :: /= << <= == && -= >= >> != -- += ** || ++ %= &= *= |= " +
|
|
2630
|
+
"=> ^= :: /= << <= == && -= >= >> != -- += ** || ?? ++ %= &= *= |= |> " +
|
|
2631
2631
|
"= ! ? > < : / ^ - + * & % ~ |";
|
|
2632
2632
|
|
|
2633
2633
|
punct = punct.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&");
|
|
2634
|
+
// ?. but not if followed by a number
|
|
2635
|
+
punct = '\\?\\.(?!\\d) ' + punct;
|
|
2634
2636
|
punct = punct.replace(/ /g, '|');
|
|
2635
2637
|
|
|
2636
2638
|
var punct_pattern = new RegExp(punct);
|
|
@@ -2707,13 +2709,13 @@ Tokenizer.prototype._get_next_token = function(previous_token, open_token) { //
|
|
|
2707
2709
|
return this._create_token(TOKEN.EOF, '');
|
|
2708
2710
|
}
|
|
2709
2711
|
|
|
2712
|
+
token = token || this._read_non_javascript(c);
|
|
2710
2713
|
token = token || this._read_string(c);
|
|
2711
2714
|
token = token || this._read_word(previous_token);
|
|
2712
2715
|
token = token || this._read_singles(c);
|
|
2713
2716
|
token = token || this._read_comment(c);
|
|
2714
2717
|
token = token || this._read_regexp(c, previous_token);
|
|
2715
2718
|
token = token || this._read_xml(c, previous_token);
|
|
2716
|
-
token = token || this._read_non_javascript(c);
|
|
2717
2719
|
token = token || this._read_punctuation();
|
|
2718
2720
|
token = token || this._create_token(TOKEN.UNKNOWN, this._input.next());
|
|
2719
2721
|
|
|
@@ -2772,6 +2774,8 @@ Tokenizer.prototype._read_punctuation = function() {
|
|
|
2772
2774
|
if (resulting_string !== '') {
|
|
2773
2775
|
if (resulting_string === '=') {
|
|
2774
2776
|
return this._create_token(TOKEN.EQUALS, resulting_string);
|
|
2777
|
+
} else if (resulting_string === '?.') {
|
|
2778
|
+
return this._create_token(TOKEN.DOT, resulting_string);
|
|
2775
2779
|
} else {
|
|
2776
2780
|
return this._create_token(TOKEN.OPERATOR, resulting_string);
|
|
2777
2781
|
}
|
package/js/lib/cli.js
CHANGED
|
@@ -392,6 +392,7 @@ function usage(err) {
|
|
|
392
392
|
msg.push(' --unformatted_content_delimiter Keep text content together between this string [""]');
|
|
393
393
|
break;
|
|
394
394
|
case "css":
|
|
395
|
+
msg.push(' -b, --brace-style [collapse|expand] ["collapse"]');
|
|
395
396
|
msg.push(' -L, --selector-separator-newline Add a newline between multiple selectors.');
|
|
396
397
|
msg.push(' -N, --newline-between-rules Add a newline between CSS rules.');
|
|
397
398
|
}
|
|
@@ -100,6 +100,14 @@ var P_A_C_K_E_R = {
|
|
|
100
100
|
t.expect(pk1, unpk1);
|
|
101
101
|
t.expect(pk2, unpk2);
|
|
102
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
|
+
|
|
103
111
|
|
|
104
112
|
var filler = '\nfiller\n';
|
|
105
113
|
t.expect(filler + pk1 + "\n" + pk_broken + filler + pk2 + filler, filler + unpk1 + "\n" + pk_broken + filler + unpk2 + filler);
|
package/js/src/cli.js
CHANGED
|
@@ -392,6 +392,7 @@ function usage(err) {
|
|
|
392
392
|
msg.push(' --unformatted_content_delimiter Keep text content together between this string [""]');
|
|
393
393
|
break;
|
|
394
394
|
case "css":
|
|
395
|
+
msg.push(' -b, --brace-style [collapse|expand] ["collapse"]');
|
|
395
396
|
msg.push(' -L, --selector-separator-newline Add a newline between multiple selectors.');
|
|
396
397
|
msg.push(' -N, --newline-between-rules Add a newline between CSS rules.');
|
|
397
398
|
}
|
package/js/src/css/beautifier.js
CHANGED
|
@@ -291,23 +291,34 @@ Beautifier.prototype.beautify = function() {
|
|
|
291
291
|
insidePropertyValue = false;
|
|
292
292
|
this.outdent();
|
|
293
293
|
}
|
|
294
|
-
this.indent();
|
|
295
|
-
this._output.space_before_token = true;
|
|
296
|
-
this.print_string(this._ch);
|
|
297
294
|
|
|
298
295
|
// when entering conditional groups, only rulesets are allowed
|
|
299
296
|
if (enteringConditionalGroup) {
|
|
300
297
|
enteringConditionalGroup = false;
|
|
301
|
-
insideRule = (this._indentLevel
|
|
298
|
+
insideRule = (this._indentLevel >= this._nestedLevel);
|
|
302
299
|
} else {
|
|
303
300
|
// otherwise, declarations are also allowed
|
|
304
|
-
insideRule = (this._indentLevel >= this._nestedLevel);
|
|
301
|
+
insideRule = (this._indentLevel >= this._nestedLevel - 1);
|
|
305
302
|
}
|
|
306
303
|
if (this._options.newline_between_rules && insideRule) {
|
|
307
304
|
if (this._output.previous_line && this._output.previous_line.item(-1) !== '{') {
|
|
308
305
|
this._output.ensure_empty_line_above('/', ',');
|
|
309
306
|
}
|
|
310
307
|
}
|
|
308
|
+
|
|
309
|
+
this._output.space_before_token = true;
|
|
310
|
+
|
|
311
|
+
// The difference in print_string and indent order is necessary to indent the '{' correctly
|
|
312
|
+
if (this._options.brace_style === 'expand') {
|
|
313
|
+
this._output.add_new_line();
|
|
314
|
+
this.print_string(this._ch);
|
|
315
|
+
this.indent();
|
|
316
|
+
this._output.set_indent(this._indentLevel);
|
|
317
|
+
} else {
|
|
318
|
+
this.indent();
|
|
319
|
+
this.print_string(this._ch);
|
|
320
|
+
}
|
|
321
|
+
|
|
311
322
|
this.eatWhitespace(true);
|
|
312
323
|
this._output.add_new_line();
|
|
313
324
|
} else if (this._ch === '}') {
|
package/js/src/css/options.js
CHANGED
|
@@ -38,6 +38,16 @@ function Options(options) {
|
|
|
38
38
|
var space_around_selector_separator = this._get_boolean('space_around_selector_separator');
|
|
39
39
|
this.space_around_combinator = this._get_boolean('space_around_combinator') || space_around_selector_separator;
|
|
40
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
|
+
}
|
|
41
51
|
}
|
|
42
52
|
Options.prototype = new BaseOptions();
|
|
43
53
|
|
|
@@ -159,7 +159,7 @@ var get_custom_beautifier_name = function(tag_check, raw_token) {
|
|
|
159
159
|
// For those without a type attribute use default;
|
|
160
160
|
if (typeAttribute.search('text/css') > -1) {
|
|
161
161
|
result = 'css';
|
|
162
|
-
} else if (typeAttribute.search(/(text|application|dojo)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json|method|aspect)/) > -1) {
|
|
162
|
+
} else if (typeAttribute.search(/module|((text|application|dojo)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json|method|aspect))/) > -1) {
|
|
163
163
|
result = 'javascript';
|
|
164
164
|
} else if (typeAttribute.search(/(text|application|dojo)\/(x-)?(html)/) > -1) {
|
|
165
165
|
result = 'html';
|
|
@@ -546,10 +546,12 @@ Beautifier.prototype._handle_tag_open = function(printer, raw_token, last_tag_to
|
|
|
546
546
|
var parser_token = this._get_tag_open_token(raw_token);
|
|
547
547
|
|
|
548
548
|
if ((last_tag_token.is_unformatted || last_tag_token.is_content_unformatted) &&
|
|
549
|
+
!last_tag_token.is_empty_element &&
|
|
549
550
|
raw_token.type === TOKEN.TAG_OPEN && raw_token.text.indexOf('</') === 0) {
|
|
550
551
|
// End element tags for unformatted or content_unformatted elements
|
|
551
552
|
// are printed raw to keep any newlines inside them exactly the same.
|
|
552
553
|
printer.add_raw_token(raw_token);
|
|
554
|
+
parser_token.start_tag_token = this._tag_stack.try_pop(parser_token.tag_name);
|
|
553
555
|
} else {
|
|
554
556
|
printer.traverse_whitespace(raw_token);
|
|
555
557
|
this._set_tag_position(printer, raw_token, parser_token, last_tag_token, last_token);
|
|
@@ -605,8 +607,13 @@ var TagOpenParserToken = function(parent, raw_token) {
|
|
|
605
607
|
tag_check_match = raw_token.text.match(/^<([^\s>]*)/);
|
|
606
608
|
this.tag_check = tag_check_match ? tag_check_match[1] : '';
|
|
607
609
|
} else {
|
|
608
|
-
tag_check_match = raw_token.text.match(/^{{[
|
|
610
|
+
tag_check_match = raw_token.text.match(/^{{(?:[\^]|#\*?)?([^\s}]+)/);
|
|
609
611
|
this.tag_check = tag_check_match ? tag_check_match[1] : '';
|
|
612
|
+
|
|
613
|
+
// handle "{{#> myPartial}}
|
|
614
|
+
if (raw_token.text === '{{#>' && this.tag_check === '>' && raw_token.next !== null) {
|
|
615
|
+
this.tag_check = raw_token.next.text;
|
|
616
|
+
}
|
|
610
617
|
}
|
|
611
618
|
this.tag_check = this.tag_check.toLowerCase();
|
|
612
619
|
|
|
@@ -653,12 +660,8 @@ Beautifier.prototype._set_tag_position = function(printer, raw_token, parser_tok
|
|
|
653
660
|
// and do an ending needed
|
|
654
661
|
if (this._do_optional_end_element(parser_token)) {
|
|
655
662
|
if (!parser_token.is_inline_element) {
|
|
656
|
-
if (parser_token.parent) {
|
|
657
|
-
parser_token.parent.multiline_content = true;
|
|
658
|
-
}
|
|
659
663
|
printer.print_newline(false);
|
|
660
664
|
}
|
|
661
|
-
|
|
662
665
|
}
|
|
663
666
|
|
|
664
667
|
this._tag_stack.record_tag(parser_token); //push it on the tag stack
|
|
@@ -695,21 +698,28 @@ Beautifier.prototype._set_tag_position = function(printer, raw_token, parser_tok
|
|
|
695
698
|
if (parser_token.tag_name === '!--' && last_token.type === TOKEN.TAG_CLOSE &&
|
|
696
699
|
last_tag_token.is_end_tag && parser_token.text.indexOf('\n') === -1) {
|
|
697
700
|
//Do nothing. Leave comments on same line.
|
|
698
|
-
} else
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
printer.print_newline(false);
|
|
701
|
+
} else {
|
|
702
|
+
if (!(parser_token.is_inline_element || parser_token.is_unformatted)) {
|
|
703
|
+
printer.print_newline(false);
|
|
704
|
+
}
|
|
705
|
+
this._calcluate_parent_multiline(printer, parser_token);
|
|
704
706
|
}
|
|
705
707
|
} else if (parser_token.is_end_tag) { //this tag is a double tag so check for tag-ending
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
)
|
|
708
|
+
var do_end_expand = false;
|
|
709
|
+
|
|
710
|
+
// deciding whether a block is multiline should not be this hard
|
|
711
|
+
do_end_expand = parser_token.start_tag_token && parser_token.start_tag_token.multiline_content;
|
|
712
|
+
do_end_expand = do_end_expand || (!parser_token.is_inline_element &&
|
|
713
|
+
!(last_tag_token.is_inline_element || last_tag_token.is_unformatted) &&
|
|
714
|
+
!(last_token.type === TOKEN.TAG_CLOSE && parser_token.start_tag_token === last_tag_token) &&
|
|
715
|
+
last_token.type !== 'TK_CONTENT'
|
|
716
|
+
);
|
|
717
|
+
|
|
718
|
+
if (parser_token.is_content_unformatted || parser_token.is_unformatted) {
|
|
719
|
+
do_end_expand = false;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
if (do_end_expand) {
|
|
713
723
|
printer.print_newline(false);
|
|
714
724
|
}
|
|
715
725
|
} else { // it's a start-tag
|
|
@@ -725,17 +735,25 @@ Beautifier.prototype._set_tag_position = function(printer, raw_token, parser_tok
|
|
|
725
735
|
}
|
|
726
736
|
}
|
|
727
737
|
|
|
728
|
-
if (!parser_token.is_inline_element
|
|
729
|
-
|
|
730
|
-
parser_token.parent.multiline_content = true;
|
|
731
|
-
}
|
|
738
|
+
if (!(parser_token.is_inline_element || parser_token.is_unformatted) &&
|
|
739
|
+
(last_token.type !== 'TK_CONTENT' || parser_token.is_content_unformatted)) {
|
|
732
740
|
printer.print_newline(false);
|
|
733
741
|
}
|
|
742
|
+
|
|
743
|
+
this._calcluate_parent_multiline(printer, parser_token);
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
Beautifier.prototype._calcluate_parent_multiline = function(printer, parser_token) {
|
|
748
|
+
if (parser_token.parent && printer._output.just_added_newline() &&
|
|
749
|
+
!((parser_token.is_inline_element || parser_token.is_unformatted) && parser_token.parent.is_inline_element)) {
|
|
750
|
+
parser_token.parent.multiline_content = true;
|
|
734
751
|
}
|
|
735
752
|
};
|
|
736
753
|
|
|
737
754
|
//To be used for <p> tag special case:
|
|
738
|
-
|
|
755
|
+
var p_closers = ['address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hr', 'main', 'nav', 'ol', 'p', 'pre', 'section', 'table', 'ul'];
|
|
756
|
+
var p_parent_excludes = ['a', 'audio', 'del', 'ins', 'map', 'noscript', 'video'];
|
|
739
757
|
|
|
740
758
|
Beautifier.prototype._do_optional_end_element = function(parser_token) {
|
|
741
759
|
var result = null;
|
|
@@ -746,7 +764,9 @@ Beautifier.prototype._do_optional_end_element = function(parser_token) {
|
|
|
746
764
|
if (parser_token.is_empty_element || !parser_token.is_start_tag || !parser_token.parent) {
|
|
747
765
|
return;
|
|
748
766
|
|
|
749
|
-
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (parser_token.tag_name === 'body') {
|
|
750
770
|
// A head element’s end tag may be omitted if the head element is not immediately followed by a space character or a comment.
|
|
751
771
|
result = result || this._tag_stack.try_pop('head');
|
|
752
772
|
|
|
@@ -763,11 +783,16 @@ Beautifier.prototype._do_optional_end_element = function(parser_token) {
|
|
|
763
783
|
result = result || this._tag_stack.try_pop('dt', ['dl']);
|
|
764
784
|
result = result || this._tag_stack.try_pop('dd', ['dl']);
|
|
765
785
|
|
|
766
|
-
//} else if (p_closers.indexOf(parser_token.tag_name) !== -1) {
|
|
767
|
-
//TODO: THIS IS A BUG FARM. We are not putting this into 1.8.0 as it is likely to blow up.
|
|
768
|
-
//A p element’s end tag may be omitted if the p element is immediately followed by an address, article, aside, blockquote, details, div, dl, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hr, main, nav, ol, p, pre, section, table, or ul element, or if there is no more content in the parent element and the parent element is an HTML element that is not an a, audio, del, ins, map, noscript, or video element, or an autonomous custom element.
|
|
769
|
-
//result = result || this._tag_stack.try_pop('p', ['body']);
|
|
770
786
|
|
|
787
|
+
} else if (parser_token.parent.tag_name === 'p' && p_closers.indexOf(parser_token.tag_name) !== -1) {
|
|
788
|
+
// IMPORTANT: this else-if works because p_closers has no overlap with any other element we look for in this method
|
|
789
|
+
// check for the parent element is an HTML element that is not an <a>, <audio>, <del>, <ins>, <map>, <noscript>, or <video> element, or an autonomous custom element.
|
|
790
|
+
// To do this right, this needs to be coded as an inclusion of the inverse of the exclusion above.
|
|
791
|
+
// But to start with (if we ignore "autonomous custom elements") the exclusion would be fine.
|
|
792
|
+
var p_parent = parser_token.parent.parent;
|
|
793
|
+
if (!p_parent || p_parent_excludes.indexOf(p_parent.tag_name) === -1) {
|
|
794
|
+
result = result || this._tag_stack.try_pop('p');
|
|
795
|
+
}
|
|
771
796
|
} else if (parser_token.tag_name === 'rp' || parser_token.tag_name === 'rt') {
|
|
772
797
|
// An rt element’s end tag may be omitted if the rt element is immediately followed by an rt or rp element, or if there is no more content in the parent element.
|
|
773
798
|
// An rp element’s end tag may be omitted if the rp element is immediately followed by an rt or rp element, or if there is no more content in the parent element.
|
package/js/src/html/tokenizer.js
CHANGED
|
@@ -63,7 +63,7 @@ var Tokenizer = function(input_string, options) {
|
|
|
63
63
|
word: templatable_reader.until(/[\n\r\t <]/),
|
|
64
64
|
single_quote: templatable_reader.until_after(/'/),
|
|
65
65
|
double_quote: templatable_reader.until_after(/"/),
|
|
66
|
-
attribute: templatable_reader.until(/[\n\r\t
|
|
66
|
+
attribute: templatable_reader.until(/[\n\r\t =>]|\/>/),
|
|
67
67
|
element_name: templatable_reader.until(/[\n\r\t >\/]/),
|
|
68
68
|
|
|
69
69
|
handlebars_comment: pattern_reader.starting_with(/{{!--/).until_after(/--}}/),
|
|
@@ -122,8 +122,8 @@ Tokenizer.prototype._get_next_token = function(previous_token, open_token) { //
|
|
|
122
122
|
|
|
123
123
|
token = token || this._read_open_handlebars(c, open_token);
|
|
124
124
|
token = token || this._read_attribute(c, previous_token, open_token);
|
|
125
|
-
token = token || this._read_raw_content(c, previous_token, open_token);
|
|
126
125
|
token = token || this._read_close(c, open_token);
|
|
126
|
+
token = token || this._read_raw_content(c, previous_token, open_token);
|
|
127
127
|
token = token || this._read_content_word(c);
|
|
128
128
|
token = token || this._read_comment_or_cdata(c);
|
|
129
129
|
token = token || this._read_processing(c);
|
|
@@ -286,7 +286,9 @@ Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token)
|
|
|
286
286
|
var resulting_string = '';
|
|
287
287
|
if (open_token && open_token.text[0] === '{') {
|
|
288
288
|
resulting_string = this.__patterns.handlebars_raw_close.read();
|
|
289
|
-
} else if (previous_token.type === TOKEN.TAG_CLOSE &&
|
|
289
|
+
} else if (previous_token.type === TOKEN.TAG_CLOSE &&
|
|
290
|
+
previous_token.opened.text[0] === '<' && previous_token.text[0] !== '/') {
|
|
291
|
+
// ^^ empty tag has no content
|
|
290
292
|
var tag_name = previous_token.opened.text.substr(1).toLowerCase();
|
|
291
293
|
if (tag_name === 'script' || tag_name === 'style') {
|
|
292
294
|
// Script and style tags are allowed to have comments wrapping their content
|
|
@@ -298,6 +300,7 @@ Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token)
|
|
|
298
300
|
}
|
|
299
301
|
resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
|
|
300
302
|
} else if (this._is_content_unformatted(tag_name)) {
|
|
303
|
+
|
|
301
304
|
resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
|
|
302
305
|
}
|
|
303
306
|
}
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
|
|
18
18
|
// acorn used char codes to squeeze the last bit of performance out
|
|
19
19
|
// Beautifier is okay without that, so we're using regex
|
|
20
|
-
// permit $ (36) and @ (64). @ is used in ES7 decorators.
|
|
20
|
+
// permit # (23), $ (36), and @ (64). @ is used in ES7 decorators.
|
|
21
21
|
// 65 through 91 are uppercase letters.
|
|
22
22
|
// permit _ (95).
|
|
23
23
|
// 97 through 123 are lowercase letters.
|
|
24
|
-
var baseASCIIidentifierStartChars = "\\x24\\x40\\x41-\\x5a\\x5f\\x61-\\x7a";
|
|
24
|
+
var baseASCIIidentifierStartChars = "\\x23\\x24\\x40\\x41-\\x5a\\x5f\\x61-\\x7a";
|
|
25
25
|
|
|
26
26
|
// inside an identifier @ is not allowed but 0-9 are.
|
|
27
27
|
var baseASCIIidentifierChars = "\\x24\\x30-\\x39\\x41-\\x5a\\x5f\\x61-\\x7a";
|