js-beautify 1.10.3 → 1.13.1

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 CHANGED
@@ -1,4 +1,64 @@
1
1
  # Changelog
2
+ ## v1.13.1
3
+
4
+ ### Description
5
+
6
+
7
+ ### Closed Issues
8
+ * Option 'max_preserve_newlines' not working on beautify_css.js CSS Beautifier ([#1863](https://github.com/beautify-web/js-beautify/issues/1863))
9
+ * React Fragment Short Syntax <></> issue ([#1854](https://github.com/beautify-web/js-beautify/issues/1854))
10
+ * add viewport meta tag to index.html ([#1843](https://github.com/beautify-web/js-beautify/pull/1843))
11
+ * Add basic smarty templating support ([#1820](https://github.com/beautify-web/js-beautify/issues/1820))
12
+ * Tagged Template literals ([#1244](https://github.com/beautify-web/js-beautify/issues/1244))
13
+
14
+
15
+ ## v1.13.0
16
+
17
+ ### Description
18
+ This release truly fixes the python cssbeautifier installation and updates cssbeautifier cli to use the same general code as jsbeautifier.
19
+
20
+ Also, as of this release Node.js 8.x is no longer guaranteed to work. Node.js 8.x LTS reached end-of-life in January 2020.
21
+ Parts of our test infrastructure no longer support Node.js 8.x. The js-beautifier should still run on Node.js 8.x, but it is not part of the CI system and so not guaranteed to work.
22
+
23
+
24
+ ### Closed Issues
25
+ * (internal) Refactor python cssbeautifier to reuse jsbeautifier CLI methods ([#1832](https://github.com/beautify-web/js-beautify/pull/1832))
26
+ * (internal) Switch from node-static to serve ([#1831](https://github.com/beautify-web/js-beautify/pull/1831))
27
+ * Fixed pip install cssbeautifier ([#1830](https://github.com/beautify-web/js-beautify/pull/1830))
28
+
29
+
30
+ ## v1.12.0
31
+
32
+ ### Description
33
+
34
+
35
+ ### Closed Issues
36
+ * Python jsbeautifier fails for special chars ([#1809](https://github.com/beautify-web/js-beautify/issues/1809))
37
+ * pip install cssbeautifier fails ([#1808](https://github.com/beautify-web/js-beautify/issues/1808))
38
+ * Add expand brace-style option to css beautifier ([#1796](https://github.com/beautify-web/js-beautify/pull/1796))
39
+ * Support nullish-coalescing ([#1794](https://github.com/beautify-web/js-beautify/issues/1794))
40
+ * Upgrade ga.js to analytics.js ([#1777](https://github.com/beautify-web/js-beautify/issues/1777))
41
+ * Newline rule not working with css-like files ([#1776](https://github.com/beautify-web/js-beautify/issues/1776))
42
+ * no new line after self closing tag ([#1718](https://github.com/beautify-web/js-beautify/issues/1718))
43
+ * HTML format, no break after <label>? ([#1365](https://github.com/beautify-web/js-beautify/issues/1365))
44
+ * Does this extension still supports applying Allman style to CSS? ([#1353](https://github.com/beautify-web/js-beautify/issues/1353))
45
+ * Add brace_style option for CSS ([#1259](https://github.com/beautify-web/js-beautify/issues/1259))
46
+
47
+
48
+ ## v1.11.0
49
+
50
+ ### Description
51
+
52
+
53
+ ### Closed Issues
54
+ * Please bump mkdirp to fix mkdirp@0.5.1 vulnerability ([#1768](https://github.com/beautify-web/js-beautify/issues/1768))
55
+ * Incorrect indentation of Handlebars inline partials ([#1756](https://github.com/beautify-web/js-beautify/issues/1756))
56
+ * Support optional-chaining ([#1727](https://github.com/beautify-web/js-beautify/issues/1727))
57
+ * Please support es module ([#1706](https://github.com/beautify-web/js-beautify/issues/1706))
58
+ * Support new js proposals: optional-chaining & pipeline-operator ([#1530](https://github.com/beautify-web/js-beautify/issues/1530))
59
+ * Optional <p> closing not implemented ([#1503](https://github.com/beautify-web/js-beautify/issues/1503))
60
+
61
+
2
62
  ## v1.10.3
3
63
 
4
64
  ### Description
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # JS Beautifier
2
- [![Build Status](https://dev.azure.com/beautifier-io/js-beautify/_apis/build/status/beautify-web.js-beautify)](https://dev.azure.com/beautifier-io/js-beautify/_build/latest?definitionId=1)
2
+ ![CI](https://github.com/beautify-web/js-beautify/workflows/CI/badge.svg)
3
3
 
4
4
  [![PyPI version](https://img.shields.io/pypi/v/jsbeautifier.svg)](https://pypi.python.org/pypi/jsbeautifier)
5
5
  [![CDNJS version](https://img.shields.io/cdnjs/v/js-beautify.svg)](https://cdnjs.com/libraries/js-beautify)
@@ -61,17 +61,17 @@ JS Beautifier is hosted on two CDN services: [cdnjs](https://cdnjs.com/libraries
61
61
 
62
62
  To pull the latest version from one of these services include one set of the script tags below in your document:
63
63
  ```html
64
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.3/beautify.js"></script>
65
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.3/beautify-css.js"></script>
66
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.3/beautify-html.js"></script>
64
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.1/beautify.js"></script>
65
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.1/beautify-css.js"></script>
66
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.1/beautify-html.js"></script>
67
67
 
68
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.3/beautify.min.js"></script>
69
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.3/beautify-css.min.js"></script>
70
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.3/beautify-html.min.js"></script>
68
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.1/beautify.min.js"></script>
69
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.1/beautify-css.min.js"></script>
70
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.1/beautify-html.min.js"></script>
71
71
 
72
- <script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.10.3/js/lib/beautify.js"></script>
73
- <script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.10.3/js/lib/beautify-css.js"></script>
74
- <script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.10.3/js/lib/beautify-html.js"></script>
72
+ <script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.13.1/js/lib/beautify.js"></script>
73
+ <script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.13.1/js/lib/beautify-css.js"></script>
74
+ <script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.13.1/js/lib/beautify-html.js"></script>
75
75
  ```
76
76
 
77
77
  Older versions are available by changing the version number.
@@ -85,7 +85,11 @@ To install the Python version of the beautifier:
85
85
  ```bash
86
86
  $ pip install jsbeautifier
87
87
  ```
88
- Unlike the JavaScript version, the Python version can only reformat JavaScript. It does not work against HTML or CSS files.
88
+ Unlike the JavaScript version, the Python version can only reformat JavaScript. It does not work against HTML or CSS files, but you can install _css-beautify_ for CSS:
89
+
90
+ ```bash
91
+ $ pip install cssbeautifier
92
+ ```
89
93
 
90
94
  # Usage
91
95
  You can beautify javascript using JS Beautifier in your web browser, or on the command-line using node.js or python.
@@ -191,7 +195,7 @@ Beautifier Options:
191
195
  -C, --comma-first Put commas at the beginning of new line instead of end
192
196
  -O, --operator-position Set operator position (before-newline|after-newline|preserve-newline) [before-newline]
193
197
  --indent-empty-lines Keep indentation on empty lines
194
- --templating List of templating languages (auto,django,erb,handlebars,php) ["auto"] auto = none in JavaScript, all in html
198
+ --templating List of templating languages (auto,django,erb,handlebars,php,smarty) ["auto"] auto = none in JavaScript, all in html
195
199
  ```
196
200
 
197
201
  Which correspond to the underscored option keys for both library interfaces
@@ -318,6 +322,7 @@ CSS Beautifier Options:
318
322
  -t, --indent-with-tabs Indent with tabs, overrides -s and -c
319
323
  -e, --eol Character(s) to use as line terminators. (default newline - "\\n")
320
324
  -n, --end-with-newline End output with newline
325
+ -b, --brace-style [collapse|expand] ["collapse"]
321
326
  -L, --selector-separator-newline Add a newline between multiple selectors
322
327
  -N, --newline-between-rules Add a newline between CSS rules
323
328
  --indent-empty-lines Keep indentation on empty lines
@@ -344,7 +349,7 @@ HTML Beautifier Options:
344
349
  --indent_scripts Sets indent level inside script tags ("normal", "keep", "separate")
345
350
  --unformatted_content_delimiter Keep text content together between this string [""]
346
351
  --indent-empty-lines Keep indentation on empty lines
347
- --templating List of templating languages (auto,none,django,erb,handlebars,php) ["auto"] auto = none in JavaScript, all in html
352
+ --templating List of templating languages (auto,none,django,erb,handlebars,php,smarty) ["auto"] auto = none in JavaScript, all in html
348
353
  ```
349
354
 
350
355
  ## Directives
@@ -399,4 +404,4 @@ Thanks also to Jason Diamond, Patrick Hof, Nochum Sossonko, Andreas Schneider, D
399
404
  Vasilevsky, Vital Batmanov, Ron Baldwin, Gabriel Harrison, Chris J. Shull,
400
405
  Mathias Bynens, Vittorio Gambaletta and others.
401
406
 
402
- (README.md: js-beautify@1.10.3)
407
+ (README.md: js-beautify@1.13.1)
@@ -1258,7 +1258,9 @@ Beautifier.prototype.handle_semicolon = function(current_token) {
1258
1258
  };
1259
1259
 
1260
1260
  Beautifier.prototype.handle_string = function(current_token) {
1261
- if (this.start_of_statement(current_token)) {
1261
+ if (current_token.text.startsWith("`") && current_token.newlines === 0 && current_token.whitespace_before === '' && (current_token.previous.text === ')' || this._flags.last_token.type === TOKEN.WORD)) {
1262
+ //Conditional for detectign backtick strings
1263
+ } else if (this.start_of_statement(current_token)) {
1262
1264
  // The conditional starts the statement if appropriate.
1263
1265
  // One difference - strings want at least a space before
1264
1266
  this._output.space_before_token = true;
@@ -1270,6 +1272,8 @@ Beautifier.prototype.handle_string = function(current_token) {
1270
1272
  if (!this.start_of_object_property()) {
1271
1273
  this.allow_wrap_or_preserved_newline(current_token);
1272
1274
  }
1275
+ } else if ((current_token.text.startsWith("`") && this._flags.last_token.type === TOKEN.END_EXPR && (current_token.previous.text === ']' || current_token.previous.text === ')') && current_token.newlines === 0)) {
1276
+ this._output.space_before_token = true;
1273
1277
  } else {
1274
1278
  this.print_newline();
1275
1279
  }
@@ -2381,10 +2385,10 @@ function Options(options, merge_child_field) {
2381
2385
 
2382
2386
  this.indent_empty_lines = this._get_boolean('indent_empty_lines');
2383
2387
 
2384
- // valid templating languages ['django', 'erb', 'handlebars', 'php']
2388
+ // valid templating languages ['django', 'erb', 'handlebars', 'php', 'smarty']
2385
2389
  // For now, 'auto' = all off for javascript, all on for html (and inline javascript).
2386
2390
  // other values ignored
2387
- this.templating = this._get_selection_list('templating', ['auto', 'none', 'django', 'erb', 'handlebars', 'php'], ['auto']);
2391
+ this.templating = this._get_selection_list('templating', ['auto', 'none', 'django', 'erb', 'handlebars', 'php', 'smarty'], ['auto']);
2388
2392
  }
2389
2393
 
2390
2394
  Options.prototype._get_array = function(name, default_value) {
@@ -2589,7 +2593,7 @@ var dot_pattern = /[^\d\.]/;
2589
2593
 
2590
2594
  var positionable_operators = (
2591
2595
  ">>> === !== " +
2592
- "<< && >= ** != == <= >> || " +
2596
+ "<< && >= ** != == <= >> || ?? |> " +
2593
2597
  "< / - + > : & % ? ^ | *").split(' ');
2594
2598
 
2595
2599
  // IMPORTANT: this must be sorted longest to shortest or tokenizing many not work.
@@ -2597,10 +2601,12 @@ var positionable_operators = (
2597
2601
  var punct =
2598
2602
  ">>>= " +
2599
2603
  "... >>= <<= === >>> !== **= " +
2600
- "=> ^= :: /= << <= == && -= >= >> != -- += ** || ++ %= &= *= |= " +
2604
+ "=> ^= :: /= << <= == && -= >= >> != -- += ** || ?? ++ %= &= *= |= |> " +
2601
2605
  "= ! ? > < : / ^ - + * & % ~ |";
2602
2606
 
2603
2607
  punct = punct.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&");
2608
+ // ?. but not if followed by a number
2609
+ punct = '\\?\\.(?!\\d) ' + punct;
2604
2610
  punct = punct.replace(/ /g, '|');
2605
2611
 
2606
2612
  var punct_pattern = new RegExp(punct);
@@ -2638,7 +2644,7 @@ var Tokenizer = function(input_string, options) {
2638
2644
  html_comment_end: pattern_reader.matching(/-->/),
2639
2645
  include: pattern_reader.starting_with(/#include/).until_after(acorn.lineBreak),
2640
2646
  shebang: pattern_reader.starting_with(/#!/).until_after(acorn.lineBreak),
2641
- xml: pattern_reader.matching(/[\s\S]*?<(\/?)([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{[\s\S]+?}))*\s*(\/?)\s*>/),
2647
+ xml: pattern_reader.matching(/[\s\S]*?<(\/?)([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\]|)(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{[\s\S]+?}))*\s*(\/?)\s*>/),
2642
2648
  single_quote: templatable.until(/['\\\n\r\u2028\u2029]/),
2643
2649
  double_quote: templatable.until(/["\\\n\r\u2028\u2029]/),
2644
2650
  template_text: templatable.until(/[`\\$]/),
@@ -2742,6 +2748,8 @@ Tokenizer.prototype._read_punctuation = function() {
2742
2748
  if (resulting_string !== '') {
2743
2749
  if (resulting_string === '=') {
2744
2750
  return this._create_token(TOKEN.EQUALS, resulting_string);
2751
+ } else if (resulting_string === '?.') {
2752
+ return this._create_token(TOKEN.DOT, resulting_string);
2745
2753
  } else {
2746
2754
  return this._create_token(TOKEN.OPERATOR, resulting_string);
2747
2755
  }
@@ -3831,7 +3839,8 @@ var template_names = {
3831
3839
  django: false,
3832
3840
  erb: false,
3833
3841
  handlebars: false,
3834
- php: false
3842
+ php: false,
3843
+ smarty: false
3835
3844
  };
3836
3845
 
3837
3846
  // This lets templates appear anywhere we would do a readUntil
@@ -3857,7 +3866,10 @@ function TemplatablePattern(input_scanner, parent) {
3857
3866
  // django coflicts with handlebars a bit.
3858
3867
  django: pattern.starting_with(/{%/).until_after(/%}/),
3859
3868
  django_value: pattern.starting_with(/{{/).until_after(/}}/),
3860
- django_comment: pattern.starting_with(/{#/).until_after(/#}/)
3869
+ django_comment: pattern.starting_with(/{#/).until_after(/#}/),
3870
+ smarty: pattern.starting_with(/{(?=[^}{\s\n])/).until_after(/[^\s\n]}/),
3871
+ smarty_comment: pattern.starting_with(/{\*/).until_after(/\*}/),
3872
+ smarty_literal: pattern.starting_with(/{literal}/).until_after(/{\/literal}/)
3861
3873
  };
3862
3874
  }
3863
3875
  TemplatablePattern.prototype = new Pattern();
@@ -3931,9 +3943,14 @@ TemplatablePattern.prototype.__set_templated_pattern = function() {
3931
3943
  }
3932
3944
  if (!this._disabled.django) {
3933
3945
  items.push(this.__patterns.django._starting_pattern.source);
3946
+ // The starting pattern for django is more complex because it has different
3947
+ // patterns for value, comment, and other sections
3934
3948
  items.push(this.__patterns.django_value._starting_pattern.source);
3935
3949
  items.push(this.__patterns.django_comment._starting_pattern.source);
3936
3950
  }
3951
+ if (!this._disabled.smarty) {
3952
+ items.push(this.__patterns.smarty._starting_pattern.source);
3953
+ }
3937
3954
 
3938
3955
  if (this._until_pattern) {
3939
3956
  items.push(this._until_pattern.source);
@@ -3979,6 +3996,17 @@ TemplatablePattern.prototype._read_template = function() {
3979
3996
  this.__patterns.django.read();
3980
3997
  }
3981
3998
  }
3999
+ if (!this._disabled.smarty) {
4000
+ // smarty cannot be enabled with django or handlebars enabled
4001
+ if (this._disabled.django && this._disabled.handlebars) {
4002
+ resulting_string = resulting_string ||
4003
+ this.__patterns.smarty_comment.read();
4004
+ resulting_string = resulting_string ||
4005
+ this.__patterns.smarty_literal.read();
4006
+ resulting_string = resulting_string ||
4007
+ this.__patterns.smarty.read();
4008
+ }
4009
+ }
3982
4010
  }
3983
4011
  return resulting_string;
3984
4012
  };
@@ -4134,13 +4162,12 @@ Beautifier.prototype.eatString = function(endChars) {
4134
4162
  // the first newline will be output
4135
4163
  Beautifier.prototype.eatWhitespace = function(allowAtLeastOneNewLine) {
4136
4164
  var result = whitespaceChar.test(this._input.peek());
4137
- var isFirstNewLine = true;
4138
-
4165
+ var newline_count = 0;
4139
4166
  while (whitespaceChar.test(this._input.peek())) {
4140
4167
  this._ch = this._input.next();
4141
4168
  if (allowAtLeastOneNewLine && this._ch === '\n') {
4142
- if (this._options.preserve_newlines || isFirstNewLine) {
4143
- isFirstNewLine = false;
4169
+ if (newline_count === 0 || newline_count < this._options.max_preserve_newlines) {
4170
+ newline_count++;
4144
4171
  this._output.add_new_line(true);
4145
4172
  }
4146
4173
  }
@@ -4334,23 +4361,34 @@ Beautifier.prototype.beautify = function() {
4334
4361
  insidePropertyValue = false;
4335
4362
  this.outdent();
4336
4363
  }
4337
- this.indent();
4338
- this._output.space_before_token = true;
4339
- this.print_string(this._ch);
4340
4364
 
4341
4365
  // when entering conditional groups, only rulesets are allowed
4342
4366
  if (enteringConditionalGroup) {
4343
4367
  enteringConditionalGroup = false;
4344
- insideRule = (this._indentLevel > this._nestedLevel);
4368
+ insideRule = (this._indentLevel >= this._nestedLevel);
4345
4369
  } else {
4346
4370
  // otherwise, declarations are also allowed
4347
- insideRule = (this._indentLevel >= this._nestedLevel);
4371
+ insideRule = (this._indentLevel >= this._nestedLevel - 1);
4348
4372
  }
4349
4373
  if (this._options.newline_between_rules && insideRule) {
4350
4374
  if (this._output.previous_line && this._output.previous_line.item(-1) !== '{') {
4351
4375
  this._output.ensure_empty_line_above('/', ',');
4352
4376
  }
4353
4377
  }
4378
+
4379
+ this._output.space_before_token = true;
4380
+
4381
+ // The difference in print_string and indent order is necessary to indent the '{' correctly
4382
+ if (this._options.brace_style === 'expand') {
4383
+ this._output.add_new_line();
4384
+ this.print_string(this._ch);
4385
+ this.indent();
4386
+ this._output.set_indent(this._indentLevel);
4387
+ } else {
4388
+ this.indent();
4389
+ this.print_string(this._ch);
4390
+ }
4391
+
4354
4392
  this.eatWhitespace(true);
4355
4393
  this._output.add_new_line();
4356
4394
  } else if (this._ch === '}') {
@@ -4558,6 +4596,16 @@ function Options(options) {
4558
4596
  var space_around_selector_separator = this._get_boolean('space_around_selector_separator');
4559
4597
  this.space_around_combinator = this._get_boolean('space_around_combinator') || space_around_selector_separator;
4560
4598
 
4599
+ var brace_style_split = this._get_selection_list('brace_style', ['collapse', 'expand', 'end-expand', 'none', 'preserve-inline']);
4600
+ this.brace_style = 'collapse';
4601
+ for (var bs = 0; bs < brace_style_split.length; bs++) {
4602
+ if (brace_style_split[bs] !== 'expand') {
4603
+ // default to collapse, as only collapse|expand is implemented for now
4604
+ this.brace_style = 'collapse';
4605
+ } else {
4606
+ this.brace_style = brace_style_split[bs];
4607
+ }
4608
+ }
4561
4609
  }
4562
4610
  Options.prototype = new BaseOptions();
4563
4611
 
@@ -4781,7 +4829,7 @@ var get_custom_beautifier_name = function(tag_check, raw_token) {
4781
4829
  // For those without a type attribute use default;
4782
4830
  if (typeAttribute.search('text/css') > -1) {
4783
4831
  result = 'css';
4784
- } else if (typeAttribute.search(/(text|application|dojo)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json|method|aspect)/) > -1) {
4832
+ } else if (typeAttribute.search(/module|((text|application|dojo)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json|method|aspect))/) > -1) {
4785
4833
  result = 'javascript';
4786
4834
  } else if (typeAttribute.search(/(text|application|dojo)\/(x-)?(html)/) > -1) {
4787
4835
  result = 'html';
@@ -5168,10 +5216,12 @@ Beautifier.prototype._handle_tag_open = function(printer, raw_token, last_tag_to
5168
5216
  var parser_token = this._get_tag_open_token(raw_token);
5169
5217
 
5170
5218
  if ((last_tag_token.is_unformatted || last_tag_token.is_content_unformatted) &&
5219
+ !last_tag_token.is_empty_element &&
5171
5220
  raw_token.type === TOKEN.TAG_OPEN && raw_token.text.indexOf('</') === 0) {
5172
5221
  // End element tags for unformatted or content_unformatted elements
5173
5222
  // are printed raw to keep any newlines inside them exactly the same.
5174
5223
  printer.add_raw_token(raw_token);
5224
+ parser_token.start_tag_token = this._tag_stack.try_pop(parser_token.tag_name);
5175
5225
  } else {
5176
5226
  printer.traverse_whitespace(raw_token);
5177
5227
  this._set_tag_position(printer, raw_token, parser_token, last_tag_token, last_token);
@@ -5227,8 +5277,13 @@ var TagOpenParserToken = function(parent, raw_token) {
5227
5277
  tag_check_match = raw_token.text.match(/^<([^\s>]*)/);
5228
5278
  this.tag_check = tag_check_match ? tag_check_match[1] : '';
5229
5279
  } else {
5230
- tag_check_match = raw_token.text.match(/^{{[#\^]?([^\s}]+)/);
5280
+ tag_check_match = raw_token.text.match(/^{{(?:[\^]|#\*?)?([^\s}]+)/);
5231
5281
  this.tag_check = tag_check_match ? tag_check_match[1] : '';
5282
+
5283
+ // handle "{{#> myPartial}}
5284
+ if (raw_token.text === '{{#>' && this.tag_check === '>' && raw_token.next !== null) {
5285
+ this.tag_check = raw_token.next.text;
5286
+ }
5232
5287
  }
5233
5288
  this.tag_check = this.tag_check.toLowerCase();
5234
5289
 
@@ -5275,12 +5330,8 @@ Beautifier.prototype._set_tag_position = function(printer, raw_token, parser_tok
5275
5330
  // and do an ending needed
5276
5331
  if (this._do_optional_end_element(parser_token)) {
5277
5332
  if (!parser_token.is_inline_element) {
5278
- if (parser_token.parent) {
5279
- parser_token.parent.multiline_content = true;
5280
- }
5281
5333
  printer.print_newline(false);
5282
5334
  }
5283
-
5284
5335
  }
5285
5336
 
5286
5337
  this._tag_stack.record_tag(parser_token); //push it on the tag stack
@@ -5317,21 +5368,28 @@ Beautifier.prototype._set_tag_position = function(printer, raw_token, parser_tok
5317
5368
  if (parser_token.tag_name === '!--' && last_token.type === TOKEN.TAG_CLOSE &&
5318
5369
  last_tag_token.is_end_tag && parser_token.text.indexOf('\n') === -1) {
5319
5370
  //Do nothing. Leave comments on same line.
5320
- } else if (!parser_token.is_inline_element && !parser_token.is_unformatted) {
5321
- printer.print_newline(false);
5322
- }
5323
- } else if (parser_token.is_unformatted || parser_token.is_content_unformatted) {
5324
- if (!parser_token.is_inline_element && !parser_token.is_unformatted) {
5325
- printer.print_newline(false);
5371
+ } else {
5372
+ if (!(parser_token.is_inline_element || parser_token.is_unformatted)) {
5373
+ printer.print_newline(false);
5374
+ }
5375
+ this._calcluate_parent_multiline(printer, parser_token);
5326
5376
  }
5327
5377
  } else if (parser_token.is_end_tag) { //this tag is a double tag so check for tag-ending
5328
- if ((parser_token.start_tag_token && parser_token.start_tag_token.multiline_content) ||
5329
- !(parser_token.is_inline_element ||
5330
- (last_tag_token.is_inline_element) ||
5331
- (last_token.type === TOKEN.TAG_CLOSE &&
5332
- parser_token.start_tag_token === last_tag_token) ||
5333
- (last_token.type === 'TK_CONTENT')
5334
- )) {
5378
+ var do_end_expand = false;
5379
+
5380
+ // deciding whether a block is multiline should not be this hard
5381
+ do_end_expand = parser_token.start_tag_token && parser_token.start_tag_token.multiline_content;
5382
+ do_end_expand = do_end_expand || (!parser_token.is_inline_element &&
5383
+ !(last_tag_token.is_inline_element || last_tag_token.is_unformatted) &&
5384
+ !(last_token.type === TOKEN.TAG_CLOSE && parser_token.start_tag_token === last_tag_token) &&
5385
+ last_token.type !== 'TK_CONTENT'
5386
+ );
5387
+
5388
+ if (parser_token.is_content_unformatted || parser_token.is_unformatted) {
5389
+ do_end_expand = false;
5390
+ }
5391
+
5392
+ if (do_end_expand) {
5335
5393
  printer.print_newline(false);
5336
5394
  }
5337
5395
  } else { // it's a start-tag
@@ -5347,17 +5405,25 @@ Beautifier.prototype._set_tag_position = function(printer, raw_token, parser_tok
5347
5405
  }
5348
5406
  }
5349
5407
 
5350
- if (!parser_token.is_inline_element && last_token.type !== 'TK_CONTENT') {
5351
- if (parser_token.parent) {
5352
- parser_token.parent.multiline_content = true;
5353
- }
5408
+ if (!(parser_token.is_inline_element || parser_token.is_unformatted) &&
5409
+ (last_token.type !== 'TK_CONTENT' || parser_token.is_content_unformatted)) {
5354
5410
  printer.print_newline(false);
5355
5411
  }
5412
+
5413
+ this._calcluate_parent_multiline(printer, parser_token);
5414
+ }
5415
+ };
5416
+
5417
+ Beautifier.prototype._calcluate_parent_multiline = function(printer, parser_token) {
5418
+ if (parser_token.parent && printer._output.just_added_newline() &&
5419
+ !((parser_token.is_inline_element || parser_token.is_unformatted) && parser_token.parent.is_inline_element)) {
5420
+ parser_token.parent.multiline_content = true;
5356
5421
  }
5357
5422
  };
5358
5423
 
5359
5424
  //To be used for <p> tag special case:
5360
- //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'];
5425
+ 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'];
5426
+ var p_parent_excludes = ['a', 'audio', 'del', 'ins', 'map', 'noscript', 'video'];
5361
5427
 
5362
5428
  Beautifier.prototype._do_optional_end_element = function(parser_token) {
5363
5429
  var result = null;
@@ -5368,7 +5434,9 @@ Beautifier.prototype._do_optional_end_element = function(parser_token) {
5368
5434
  if (parser_token.is_empty_element || !parser_token.is_start_tag || !parser_token.parent) {
5369
5435
  return;
5370
5436
 
5371
- } else if (parser_token.tag_name === 'body') {
5437
+ }
5438
+
5439
+ if (parser_token.tag_name === 'body') {
5372
5440
  // A head element’s end tag may be omitted if the head element is not immediately followed by a space character or a comment.
5373
5441
  result = result || this._tag_stack.try_pop('head');
5374
5442
 
@@ -5385,11 +5453,16 @@ Beautifier.prototype._do_optional_end_element = function(parser_token) {
5385
5453
  result = result || this._tag_stack.try_pop('dt', ['dl']);
5386
5454
  result = result || this._tag_stack.try_pop('dd', ['dl']);
5387
5455
 
5388
- //} else if (p_closers.indexOf(parser_token.tag_name) !== -1) {
5389
- //TODO: THIS IS A BUG FARM. We are not putting this into 1.8.0 as it is likely to blow up.
5390
- //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.
5391
- //result = result || this._tag_stack.try_pop('p', ['body']);
5392
5456
 
5457
+ } else if (parser_token.parent.tag_name === 'p' && p_closers.indexOf(parser_token.tag_name) !== -1) {
5458
+ // IMPORTANT: this else-if works because p_closers has no overlap with any other element we look for in this method
5459
+ // 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.
5460
+ // To do this right, this needs to be coded as an inclusion of the inverse of the exclusion above.
5461
+ // But to start with (if we ignore "autonomous custom elements") the exclusion would be fine.
5462
+ var p_parent = parser_token.parent.parent;
5463
+ if (!p_parent || p_parent_excludes.indexOf(p_parent.tag_name) === -1) {
5464
+ result = result || this._tag_stack.try_pop('p');
5465
+ }
5393
5466
  } else if (parser_token.tag_name === 'rp' || parser_token.tag_name === 'rt') {
5394
5467
  // 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.
5395
5468
  // 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.
@@ -5689,8 +5762,8 @@ Tokenizer.prototype._get_next_token = function(previous_token, open_token) { //
5689
5762
 
5690
5763
  token = token || this._read_open_handlebars(c, open_token);
5691
5764
  token = token || this._read_attribute(c, previous_token, open_token);
5692
- token = token || this._read_raw_content(c, previous_token, open_token);
5693
5765
  token = token || this._read_close(c, open_token);
5766
+ token = token || this._read_raw_content(c, previous_token, open_token);
5694
5767
  token = token || this._read_content_word(c);
5695
5768
  token = token || this._read_comment_or_cdata(c);
5696
5769
  token = token || this._read_processing(c);
@@ -5853,7 +5926,9 @@ Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token)
5853
5926
  var resulting_string = '';
5854
5927
  if (open_token && open_token.text[0] === '{') {
5855
5928
  resulting_string = this.__patterns.handlebars_raw_close.read();
5856
- } else if (previous_token.type === TOKEN.TAG_CLOSE && (previous_token.opened.text[0] === '<')) {
5929
+ } else if (previous_token.type === TOKEN.TAG_CLOSE &&
5930
+ previous_token.opened.text[0] === '<' && previous_token.text[0] !== '/') {
5931
+ // ^^ empty tag has no content
5857
5932
  var tag_name = previous_token.opened.text.substr(1).toLowerCase();
5858
5933
  if (tag_name === 'script' || tag_name === 'style') {
5859
5934
  // Script and style tags are allowed to have comments wrapping their content
@@ -5865,6 +5940,7 @@ Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token)
5865
5940
  }
5866
5941
  resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
5867
5942
  } else if (this._is_content_unformatted(tag_name)) {
5943
+
5868
5944
  resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
5869
5945
  }
5870
5946
  }