@bmewburn/js-beautify 1.13.0 → 1.14.7-next1

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/README.md CHANGED
@@ -1,16 +1,12 @@
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)
1
+ <p align="center"><img src="https://raw.githubusercontent.com/beautify-web/js-beautify/7db71fc/web/wordmark-light.svg" height="200px" align="center" alt="JS Beautifier"/></p>
3
2
 
4
- [![PyPI version](https://img.shields.io/pypi/v/jsbeautifier.svg)](https://pypi.python.org/pypi/jsbeautifier)
5
- [![CDNJS version](https://img.shields.io/cdnjs/v/js-beautify.svg)](https://cdnjs.com/libraries/js-beautify)
6
- [![NPM @latest](https://img.shields.io/npm/v/js-beautify.svg)](https://www.npmjs.com/package/js-beautify)
7
- [![NPM @next](https://img.shields.io/npm/v/js-beautify/next.svg)](https://www.npmjs.com/package/js-beautify?activeTab=versions)
3
+ <p align="center"><a href="https://github.com/beautify-web/js-beautify/actions/workflows/main.yml"><img alt="CI" src="https://github.com/beautify-web/js-beautify/workflows/CI/badge.svg"/></a>&#32;<a href="https://greenkeeper.io/" target="_blank"><img alt="Greenkeeper badge" src="https://badges.greenkeeper.io/beautify-web/js-beautify.svg"/></a></p>
8
4
 
9
- [![Join the chat at https://gitter.im/beautify-web/js-beautify](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/beautify-web/js-beautify?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
10
- [![Twitter Follow](https://img.shields.io/twitter/follow/js_beautifier.svg?style=social&label=Follow)](https://twitter.com/intent/user?screen_name=js_beautifier)
5
+ <p align="center"><a href="https://pypi.python.org/pypi/jsbeautifier" target="_blank"><img alt="PyPI version" src="https://img.shields.io/pypi/v/jsbeautifier.svg"/></a>&#32;<a href="https://cdnjs.com/libraries/js-beautify" target="_blank"><img alt="CDNJS version" src="https://img.shields.io/cdnjs/v/js-beautify.svg"/></a>&#32;<a href="https://www.npmjs.com/package/js-beautify" target="_blank"><img alt="NPM @latest" src="https://img.shields.io/npm/v/js-beautify.svg"/></a>&#32;<a href="https://www.npmjs.com/package/js-beautify?activeTab=versions" target="_blank"><img alt="NPM @next" src="https://img.shields.io/npm/v/js-beautify/next.svg"/></a></p>
11
6
 
12
- [![NPM stats](https://nodei.co/npm/js-beautify.svg?downloadRank=true&downloads=true)](https://www.npmjs.org/package/js-beautify) [![Greenkeeper badge](https://badges.greenkeeper.io/beautify-web/js-beautify.svg)](https://greenkeeper.io/)
7
+ <p align="center"><a href="https://gitter.im/beautify-web/js-beautify?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge" target="_blank"><img alt="Join the chat at https://gitter.im/beautify-web/js-beautify" src="https://badges.gitter.im/Join%20Chat.svg"></a>&#32;<a href="https://twitter.com/intent/user?screen_name=js_beautifier" target="_blank"><img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/js_beautifier.svg?style=social&label=Follow"/></a></p>
13
8
 
9
+ <p align="center"><a href="https://www.npmjs.org/package/js-beautify" target="_blank"><img alt="NPM stats" src=https://nodei.co/npm/js-beautify.svg?downloadRank=true&downloads=true"/></a></p>
14
10
 
15
11
  This little beautifier will reformat and re-indent bookmarklets, ugly
16
12
  JavaScript, unpack scripts packed by Dean Edward’s popular packer,
@@ -19,17 +15,18 @@ as well as partly deobfuscate scripts processed by the npm package
19
15
 
20
16
  Open [beautifier.io](https://beautifier.io/) to try it out. Options are available via the UI.
21
17
 
18
+
22
19
  # Contributors Needed
23
20
  I'm putting this front and center above because existing owners have very limited time to work on this project currently.
24
21
  This is a popular project and widely used but it desperately needs contributors who have time to commit to fixing both
25
22
  customer facing bugs and underlying problems with the internal design and implementation.
26
23
 
27
- If you are interested, please take a look at the [CONTRIBUTING.md](https://github.com/beautify-web/js-beautify/blob/master/CONTRIBUTING.md) then fix an issue marked with the ["Good first issue"](https://github.com/beautify-web/js-beautify/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label and submit a PR. Repeat as often as possible. Thanks!
24
+ If you are interested, please take a look at the [CONTRIBUTING.md](https://github.com/beautify-web/js-beautify/blob/main/CONTRIBUTING.md) then fix an issue marked with the ["Good first issue"](https://github.com/beautify-web/js-beautify/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label and submit a PR. Repeat as often as possible. Thanks!
28
25
 
29
26
 
30
27
  # Installation
31
28
 
32
- You can install the beautifier for node.js or python.
29
+ You can install the beautifier for Node.js or Python.
33
30
 
34
31
  ## Node.js JavaScript
35
32
 
@@ -61,19 +58,29 @@ JS Beautifier is hosted on two CDN services: [cdnjs](https://cdnjs.com/libraries
61
58
 
62
59
  To pull the latest version from one of these services include one set of the script tags below in your document:
63
60
  ```html
64
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.0/beautify.js"></script>
65
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.0/beautify-css.js"></script>
66
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.0/beautify-html.js"></script>
67
-
68
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.0/beautify.min.js"></script>
69
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.0/beautify-css.min.js"></script>
70
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.0/beautify-html.min.js"></script>
61
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.7/beautify.js"></script>
62
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.7/beautify-css.js"></script>
63
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.7/beautify-html.js"></script>
71
64
 
72
- <script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.13.0/js/lib/beautify.js"></script>
73
- <script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.13.0/js/lib/beautify-css.js"></script>
74
- <script src="https://cdn.rawgit.com/beautify-web/js-beautify/v1.13.0/js/lib/beautify-html.js"></script>
65
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.7/beautify.min.js"></script>
66
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.7/beautify-css.min.js"></script>
67
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.7/beautify-html.min.js"></script>
75
68
  ```
76
69
 
70
+ Example usage of a JS tag in html:
71
+
72
+ ```html
73
+ <!DOCTYPE html>
74
+ <html lang="en">
75
+ <body>
76
+
77
+ . . .
78
+
79
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.7/beautify.min.js"></script>
80
+ <script src="script.js"></script>
81
+ </body>
82
+ </html>
83
+ ```
77
84
  Older versions are available by changing the version number.
78
85
 
79
86
  Disclaimer: These are free services, so there are [no uptime or support guarantees](https://github.com/rgrove/rawgit/wiki/Frequently-Asked-Questions#i-need-guaranteed-100-uptime-should-i-use-cdnrawgitcom).
@@ -92,13 +99,34 @@ $ pip install cssbeautifier
92
99
  ```
93
100
 
94
101
  # Usage
95
- You can beautify javascript using JS Beautifier in your web browser, or on the command-line using node.js or python.
102
+ You can beautify JavaScript using JS Beautifier in your web browser, or on the command-line using Node.js or Python.
96
103
 
97
104
  ## Web Browser
98
105
  Open [beautifier.io](https://beautifier.io/). Options are available via the UI.
99
106
 
100
107
  ## Web Library
101
- The script tags above expose three functions: `js_beautify`, `css_beautify`, and `html_beautify`.
108
+ After you embed the `<script>` tags in your `html` file, they expose three functions: `js_beautify`, `css_beautify`, and `html_beautify`
109
+
110
+ Example usage of beautifying a json string:
111
+
112
+ ```js
113
+ const options = { indent_size: 2, space_in_empty_paren: true }
114
+
115
+ const dataObj = {completed: false,id: 1,title: "delectus aut autem",userId: 1,}
116
+
117
+ const dataJson = JSON.stringify(dataObj)
118
+
119
+ js_beautify(dataJson, options)
120
+
121
+ /* OUTPUT
122
+ {
123
+ "completed": false,
124
+ "id": 1,
125
+ "title": "delectus aut autem",
126
+ "userId": 1,
127
+ }
128
+ */
129
+ ```
102
130
 
103
131
  ## Node.js JavaScript
104
132
 
@@ -108,7 +136,7 @@ When installed globally, the beautifier provides an executable `js-beautify` scr
108
136
  $ js-beautify foo.js
109
137
  ```
110
138
 
111
- To use `js-beautify` as a `node` library (after install locally), import and call the appropriate beautifier method for javascript (js), css, or html. All three method signatures are `beautify(code, options)`. `code` is the string of code to be beautified. options is an object with the settings you would like used to beautify the code.
139
+ To use `js-beautify` as a `node` library (after install locally), import and call the appropriate beautifier method for JavaScript (JS), CSS, or HTML. All three method signatures are `beautify(code, options)`. `code` is the string of code to be beautified. options is an object with the settings you would like used to beautify the code.
112
140
 
113
141
  The configuration option names are the same as the CLI names but with underscores instead of dashes. For example, `--indent-size 2 --space-in-empty-paren` would be `{ indent_size: 2, space_in_empty_paren: true }`.
114
142
 
@@ -137,7 +165,7 @@ To use `jsbeautifier` as a library is simple:
137
165
 
138
166
  ```python
139
167
  import jsbeautifier
140
- res = jsbeautifier.beautify('your javascript string')
168
+ res = jsbeautifier.beautify('your JavaScript string')
141
169
  res = jsbeautifier.beautify_file('some_file.js')
142
170
  ```
143
171
 
@@ -147,7 +175,7 @@ res = jsbeautifier.beautify_file('some_file.js')
147
175
  opts = jsbeautifier.default_options()
148
176
  opts.indent_size = 2
149
177
  opts.space_in_empty_paren = True
150
- res = jsbeautifier.beautify('some javascript', opts)
178
+ res = jsbeautifier.beautify('some JavaScript', opts)
151
179
  ```
152
180
 
153
181
  The configuration option names are the same as the CLI names but with underscores instead of dashes. The example above would be set on the command-line as `--indent-size 2 --space-in-empty-paren`.
@@ -195,7 +223,7 @@ Beautifier Options:
195
223
  -C, --comma-first Put commas at the beginning of new line instead of end
196
224
  -O, --operator-position Set operator position (before-newline|after-newline|preserve-newline) [before-newline]
197
225
  --indent-empty-lines Keep indentation on empty lines
198
- --templating List of templating languages (auto,django,erb,handlebars,php) ["auto"] auto = none in JavaScript, all in html
226
+ --templating List of templating languages (auto,django,erb,handlebars,php,smarty) ["auto"] auto = none in JavaScript, all in HTML
199
227
  ```
200
228
 
201
229
  Which correspond to the underscored option keys for both library interfaces
@@ -258,7 +286,7 @@ Configuration sources provided earlier in this stack will override later ones.
258
286
 
259
287
  The settings are a shallow tree whose values are inherited for all languages, but
260
288
  can be overridden. This works for settings passed directly to the API in either implementation.
261
- In the Javascript implementation, settings loaded from a config file, such as .jsbeautifyrc, can also use inheritance/overriding.
289
+ In the JavaScript implementation, settings loaded from a config file, such as .jsbeautifyrc, can also use inheritance/overriding.
262
290
 
263
291
  Below is an example configuration tree showing all the supported locations
264
292
  for language override nodes. We'll use `indent_size` to discuss how this configuration would behave, but any number of settings can be inherited or overridden:
@@ -349,7 +377,7 @@ HTML Beautifier Options:
349
377
  --indent_scripts Sets indent level inside script tags ("normal", "keep", "separate")
350
378
  --unformatted_content_delimiter Keep text content together between this string [""]
351
379
  --indent-empty-lines Keep indentation on empty lines
352
- --templating List of templating languages (auto,none,django,erb,handlebars,php) ["auto"] auto = none in JavaScript, all in html
380
+ --templating List of templating languages (auto,none,django,erb,handlebars,php,smarty) ["auto"] auto = none in JavaScript, all in html
353
381
  ```
354
382
 
355
383
  ## Directives
@@ -404,4 +432,4 @@ Thanks also to Jason Diamond, Patrick Hof, Nochum Sossonko, Andreas Schneider, D
404
432
  Vasilevsky, Vital Batmanov, Ron Baldwin, Gabriel Harrison, Chris J. Shull,
405
433
  Mathias Bynens, Vittorio Gambaletta and others.
406
434
 
407
- (README.md: js-beautify@1.13.0)
435
+ (README.md: js-beautify@1.14.7)
package/js/src/cli.js CHANGED
@@ -40,7 +40,6 @@ var debug = process.env.DEBUG_JSBEAUTIFY || process.env.JSBEAUTIFY_DEBUG ? funct
40
40
  var fs = require('fs'),
41
41
  cc = require('config-chain'),
42
42
  beautify = require('../index'),
43
- mkdirp = require('mkdirp'),
44
43
  nopt = require('nopt'),
45
44
  glob = require('glob');
46
45
 
@@ -64,6 +63,19 @@ nopt.typeDefs.brace_style = {
64
63
  return true;
65
64
  }
66
65
  };
66
+ nopt.typeDefs.glob = {
67
+ type: "glob",
68
+ validate: function(data, key, val) {
69
+ if (typeof val === 'string' && glob.hasMagic(val)) {
70
+ // Preserve value if it contains glob magic
71
+ data[key] = val;
72
+ return true;
73
+ } else {
74
+ // Otherwise validate it as regular path
75
+ return nopt.typeDefs.path.validate(data, key, val);
76
+ }
77
+ }
78
+ };
67
79
  var path = require('path'),
68
80
  editorconfig = require('editorconfig'),
69
81
  knownOpts = {
@@ -113,7 +125,7 @@ var path = require('path'),
113
125
  // CLI
114
126
  "version": Boolean,
115
127
  "help": Boolean,
116
- "files": [path, Array],
128
+ "files": ["glob", Array],
117
129
  "outfile": path,
118
130
  "replace": Boolean,
119
131
  "quiet": Boolean,
@@ -351,7 +363,7 @@ function usage(err) {
351
363
  ' [first newline in file, otherwise "\\n]',
352
364
  ' -n, --end-with-newline End output with newline',
353
365
  ' --indent-empty-lines Keep indentation on empty lines',
354
- ' --templating List of templating languages (auto,none,django,erb,handlebars,php) ["auto"] auto = none in JavaScript, all in html',
366
+ ' --templating List of templating languages (auto,none,django,erb,handlebars,php,smarty) ["auto"] auto = none in JavaScript, all in html',
355
367
  ' --editorconfig Use EditorConfig to set up the options'
356
368
  ];
357
369
 
@@ -485,7 +497,7 @@ function writePretty(err, pretty, outfile, config) {
485
497
  }
486
498
 
487
499
  if (outfile) {
488
- mkdirp.sync(path.dirname(outfile));
500
+ fs.mkdirSync(path.dirname(outfile), { recursive: true });
489
501
 
490
502
  if (isFileDifferent(outfile, pretty)) {
491
503
  try {
@@ -67,10 +67,10 @@ function Options(options, merge_child_field) {
67
67
 
68
68
  this.indent_empty_lines = this._get_boolean('indent_empty_lines');
69
69
 
70
- // valid templating languages ['django', 'erb', 'handlebars', 'php']
70
+ // valid templating languages ['django', 'erb', 'handlebars', 'php', 'smarty']
71
71
  // For now, 'auto' = all off for javascript, all on for html (and inline javascript).
72
72
  // other values ignored
73
- this.templating = this._get_selection_list('templating', ['auto', 'none', 'django', 'erb', 'handlebars', 'php'], ['auto']);
73
+ this.templating = this._get_selection_list('templating', ['auto', 'none', 'django', 'erb', 'handlebars', 'php', 'smarty'], ['auto']);
74
74
  }
75
75
 
76
76
  Options.prototype._get_array = function(name, default_value) {
@@ -35,7 +35,8 @@ var template_names = {
35
35
  django: false,
36
36
  erb: false,
37
37
  handlebars: false,
38
- php: false
38
+ php: false,
39
+ smarty: false
39
40
  };
40
41
 
41
42
  // This lets templates appear anywhere we would do a readUntil
@@ -56,12 +57,15 @@ function TemplatablePattern(input_scanner, parent) {
56
57
  handlebars_comment: pattern.starting_with(/{{!--/).until_after(/--}}/),
57
58
  handlebars_unescaped: pattern.starting_with(/{{{/).until_after(/}}}/),
58
59
  handlebars: pattern.starting_with(/{{/).until_after(/}}/),
59
- php: pattern.starting_with(/<\?(?:[=]|php)/).until_after(/\?>/),
60
+ php: pattern.starting_with(/<\?(?:[= ]|php)/).until_after(/\?>/),
60
61
  erb: pattern.starting_with(/<%[^%]/).until_after(/[^%]%>/),
61
62
  // django coflicts with handlebars a bit.
62
63
  django: pattern.starting_with(/{%/).until_after(/%}/),
63
64
  django_value: pattern.starting_with(/{{/).until_after(/}}/),
64
- django_comment: pattern.starting_with(/{#/).until_after(/#}/)
65
+ django_comment: pattern.starting_with(/{#/).until_after(/#}/),
66
+ smarty: pattern.starting_with(/{(?=[^}{\s\n])/).until_after(/[^\s\n]}/),
67
+ smarty_comment: pattern.starting_with(/{\*/).until_after(/\*}/),
68
+ smarty_literal: pattern.starting_with(/{literal}/).until_after(/{\/literal}/)
65
69
  };
66
70
  }
67
71
  TemplatablePattern.prototype = new Pattern();
@@ -135,9 +139,14 @@ TemplatablePattern.prototype.__set_templated_pattern = function() {
135
139
  }
136
140
  if (!this._disabled.django) {
137
141
  items.push(this.__patterns.django._starting_pattern.source);
142
+ // The starting pattern for django is more complex because it has different
143
+ // patterns for value, comment, and other sections
138
144
  items.push(this.__patterns.django_value._starting_pattern.source);
139
145
  items.push(this.__patterns.django_comment._starting_pattern.source);
140
146
  }
147
+ if (!this._disabled.smarty) {
148
+ items.push(this.__patterns.smarty._starting_pattern.source);
149
+ }
141
150
 
142
151
  if (this._until_pattern) {
143
152
  items.push(this._until_pattern.source);
@@ -183,6 +192,17 @@ TemplatablePattern.prototype._read_template = function() {
183
192
  this.__patterns.django.read();
184
193
  }
185
194
  }
195
+ if (!this._disabled.smarty) {
196
+ // smarty cannot be enabled with django or handlebars enabled
197
+ if (this._disabled.django && this._disabled.handlebars) {
198
+ resulting_string = resulting_string ||
199
+ this.__patterns.smarty_comment.read();
200
+ resulting_string = resulting_string ||
201
+ this.__patterns.smarty_literal.read();
202
+ resulting_string = resulting_string ||
203
+ this.__patterns.smarty.read();
204
+ }
205
+ }
186
206
  }
187
207
  return resulting_string;
188
208
  };
@@ -67,6 +67,10 @@ function Beautifier(source_text, options) {
67
67
  "@supports": true,
68
68
  "@document": true
69
69
  };
70
+ this.NON_SEMICOLON_NEWLINE_PROPERTY = [
71
+ "grid-template-areas",
72
+ "grid-template"
73
+ ];
70
74
 
71
75
  }
72
76
 
@@ -91,13 +95,12 @@ Beautifier.prototype.eatString = function(endChars) {
91
95
  // the first newline will be output
92
96
  Beautifier.prototype.eatWhitespace = function(allowAtLeastOneNewLine) {
93
97
  var result = whitespaceChar.test(this._input.peek());
94
- var isFirstNewLine = true;
95
-
98
+ var newline_count = 0;
96
99
  while (whitespaceChar.test(this._input.peek())) {
97
100
  this._ch = this._input.next();
98
101
  if (allowAtLeastOneNewLine && this._ch === '\n') {
99
- if (this._options.preserve_newlines || isFirstNewLine) {
100
- isFirstNewLine = false;
102
+ if (newline_count === 0 || newline_count < this._options.max_preserve_newlines) {
103
+ newline_count++;
101
104
  this._output.add_new_line(true);
102
105
  }
103
106
  }
@@ -192,7 +195,9 @@ Beautifier.prototype.beautify = function() {
192
195
  var enteringConditionalGroup = false;
193
196
  var insideAtExtend = false;
194
197
  var insideAtImport = false;
198
+ var insideScssMap = false;
195
199
  var topCharacter = this._ch;
200
+ var insideNonSemiColonValues = false;
196
201
  var whitespace;
197
202
  var isAfterSpace;
198
203
  var previous_ch;
@@ -244,7 +249,7 @@ Beautifier.prototype.beautify = function() {
244
249
 
245
250
  // Ensures any new lines following the comment are preserved
246
251
  this.eatWhitespace(true);
247
- } else if (this._ch === '@') {
252
+ } else if (this._ch === '@' || this._ch === '$') {
248
253
  this.preserveSingleSpace(isAfterSpace);
249
254
 
250
255
  // deal with less propery mixins @{...}
@@ -315,7 +320,12 @@ Beautifier.prototype.beautify = function() {
315
320
  this.indent();
316
321
  this._output.set_indent(this._indentLevel);
317
322
  } else {
318
- this.indent();
323
+ // inside mixin and first param is object
324
+ if (previous_ch === '(') {
325
+ this._output.space_before_token = false;
326
+ } else if (previous_ch !== ',') {
327
+ this.indent();
328
+ }
319
329
  this.print_string(this._ch);
320
330
  }
321
331
 
@@ -347,7 +357,21 @@ Beautifier.prototype.beautify = function() {
347
357
  this._output.add_new_line(true);
348
358
  }
349
359
  }
360
+ if (this._input.peek() === ')') {
361
+ this._output.trim(true);
362
+ if (this._options.brace_style === "expand") {
363
+ this._output.add_new_line(true);
364
+ }
365
+ }
350
366
  } else if (this._ch === ":") {
367
+
368
+ for (var i = 0; i < this.NON_SEMICOLON_NEWLINE_PROPERTY.length; i++) {
369
+ if (this._input.lookBack(this.NON_SEMICOLON_NEWLINE_PROPERTY[i])) {
370
+ insideNonSemiColonValues = true;
371
+ break;
372
+ }
373
+ }
374
+
351
375
  if ((insideRule || enteringConditionalGroup) && !(this._input.lookBack("&") || this.foundNestedPseudoClass()) && !this._input.lookBack("(") && !insideAtExtend && parenLevel === 0) {
352
376
  // 'property: value' delimiter
353
377
  // which could be in a conditional group query
@@ -376,10 +400,12 @@ Beautifier.prototype.beautify = function() {
376
400
  }
377
401
  }
378
402
  } else if (this._ch === '"' || this._ch === '\'') {
379
- this.preserveSingleSpace(isAfterSpace);
403
+ var preserveQuoteSpace = previous_ch === '"' || previous_ch === '\'';
404
+ this.preserveSingleSpace(preserveQuoteSpace || isAfterSpace);
380
405
  this.print_string(this._ch + this.eatString(this._ch));
381
406
  this.eatWhitespace(true);
382
407
  } else if (this._ch === ';') {
408
+ insideNonSemiColonValues = false;
383
409
  if (parenLevel === 0) {
384
410
  if (insidePropertyValue) {
385
411
  this.outdent();
@@ -419,22 +445,39 @@ Beautifier.prototype.beautify = function() {
419
445
  }
420
446
  }
421
447
  } else {
422
- this.preserveSingleSpace(isAfterSpace);
448
+ var space_needed = false;
449
+ if (this._input.lookBack("with")) {
450
+ // look back is not an accurate solution, we need tokens to confirm without whitespaces
451
+ space_needed = true;
452
+ }
453
+ this.preserveSingleSpace(isAfterSpace || space_needed);
423
454
  this.print_string(this._ch);
424
- this.eatWhitespace();
425
- parenLevel++;
426
- this.indent();
455
+
456
+ // handle scss/sass map
457
+ if (insidePropertyValue && previous_ch === "$" && this._options.selector_separator_newline) {
458
+ this._output.add_new_line();
459
+ insideScssMap = true;
460
+ } else {
461
+ this.eatWhitespace();
462
+ parenLevel++;
463
+ this.indent();
464
+ }
427
465
  }
428
466
  } else if (this._ch === ')') {
429
467
  if (parenLevel) {
430
468
  parenLevel--;
431
469
  this.outdent();
432
470
  }
471
+ if (insideScssMap && this._input.peek() === ";" && this._options.selector_separator_newline) {
472
+ insideScssMap = false;
473
+ this.outdent();
474
+ this._output.add_new_line();
475
+ }
433
476
  this.print_string(this._ch);
434
477
  } else if (this._ch === ',') {
435
478
  this.print_string(this._ch);
436
479
  this.eatWhitespace(true);
437
- if (this._options.selector_separator_newline && !insidePropertyValue && parenLevel === 0 && !insideAtImport) {
480
+ if (this._options.selector_separator_newline && (!insidePropertyValue || insideScssMap) && parenLevel === 0 && !insideAtImport && !insideAtExtend) {
438
481
  this._output.add_new_line();
439
482
  } else {
440
483
  this._output.space_before_token = true;
@@ -465,11 +508,16 @@ Beautifier.prototype.beautify = function() {
465
508
  this._ch = '';
466
509
  }
467
510
  } else if (this._ch === '!' && !this._input.lookBack("\\")) { // !important
468
- this.print_string(' ');
511
+ this._output.space_before_token = true;
469
512
  this.print_string(this._ch);
470
513
  } else {
471
- this.preserveSingleSpace(isAfterSpace);
514
+ var preserveAfterSpace = previous_ch === '"' || previous_ch === '\'';
515
+ this.preserveSingleSpace(preserveAfterSpace || isAfterSpace);
472
516
  this.print_string(this._ch);
517
+
518
+ if (!this._output.just_added_newline() && this._input.peek() === '\n' && insideNonSemiColonValues) {
519
+ this._output.add_new_line();
520
+ }
473
521
  }
474
522
  }
475
523
 
@@ -615,14 +615,19 @@ var TagOpenParserToken = function(parent, raw_token) {
615
615
  tag_check_match = raw_token.text.match(/^<([^\s>]*)/);
616
616
  this.tag_check = tag_check_match ? tag_check_match[1] : '';
617
617
  } else {
618
- tag_check_match = raw_token.text.match(/^{{(?:[\^]|#\*?)?([^\s}]+)/);
618
+ tag_check_match = raw_token.text.match(/^{{~?(?:[\^]|#\*?)?([^\s}]+)/);
619
619
  this.tag_check = tag_check_match ? tag_check_match[1] : '';
620
620
 
621
- // handle "{{#> myPartial}}
622
- if (raw_token.text === '{{#>' && this.tag_check === '>' && raw_token.next !== null) {
623
- this.tag_check = raw_token.next.text;
621
+ // handle "{{#> myPartial}}" or "{{~#> myPartial}}"
622
+ if ((raw_token.text.startsWith('{{#>') || raw_token.text.startsWith('{{~#>')) && this.tag_check[0] === '>') {
623
+ if (this.tag_check === '>' && raw_token.next !== null) {
624
+ this.tag_check = raw_token.next.text.split(' ')[0];
625
+ } else {
626
+ this.tag_check = raw_token.text.split('>')[1];
627
+ }
624
628
  }
625
629
  }
630
+
626
631
  this.tag_check = this.tag_check.toLowerCase();
627
632
 
628
633
  if (raw_token.type === TOKEN.COMMENT) {
@@ -634,9 +639,17 @@ var TagOpenParserToken = function(parent, raw_token) {
634
639
  this.is_end_tag = !this.is_start_tag ||
635
640
  (raw_token.closed && raw_token.closed.text === '/>');
636
641
 
642
+ // if whitespace handler ~ included (i.e. {{~#if true}}), handlebars tags start at pos 3 not pos 2
643
+ var handlebar_starts = 2;
644
+ if (this.tag_start_char === '{' && this.text.length >= 3) {
645
+ if (this.text.charAt(2) === '~') {
646
+ handlebar_starts = 3;
647
+ }
648
+ }
649
+
637
650
  // handlebars tags that don't start with # or ^ are single_tags, and so also start and end.
638
651
  this.is_end_tag = this.is_end_tag ||
639
- (this.tag_start_char === '{' && (this.text.length < 3 || (/[^#\^]/.test(this.text.charAt(2)))));
652
+ (this.tag_start_char === '{' && (this.text.length < 3 || (/[^#\^]/.test(this.text.charAt(handlebar_starts)))));
640
653
  }
641
654
  };
642
655
 
@@ -653,7 +666,7 @@ Beautifier.prototype._get_tag_open_token = function(raw_token) { //function to g
653
666
 
654
667
  parser_token.is_unformatted = !parser_token.tag_complete && in_array(parser_token.tag_check, this._options.unformatted);
655
668
  parser_token.is_content_unformatted = !parser_token.is_empty_element && in_array(parser_token.tag_check, this._options.content_unformatted);
656
- parser_token.is_inline_element = in_array(parser_token.tag_name, this._options.inline) || parser_token.tag_start_char === '{';
669
+ parser_token.is_inline_element = in_array(parser_token.tag_name, this._options.inline) || parser_token.tag_name.includes("-") || parser_token.tag_start_char === '{';
657
670
 
658
671
  return parser_token;
659
672
  };
@@ -760,7 +773,7 @@ Beautifier.prototype._calcluate_parent_multiline = function(printer, parser_toke
760
773
  };
761
774
 
762
775
  //To be used for <p> tag special case:
763
- 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'];
776
+ var p_closers = ['address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hr', 'main', 'menu', 'nav', 'ol', 'p', 'pre', 'section', 'table', 'ul'];
764
777
  var p_parent_excludes = ['a', 'audio', 'del', 'ins', 'map', 'noscript', 'video'];
765
778
 
766
779
  Beautifier.prototype._do_optional_end_element = function(parser_token) {
@@ -783,7 +796,7 @@ Beautifier.prototype._do_optional_end_element = function(parser_token) {
783
796
 
784
797
  } else if (parser_token.tag_name === 'li') {
785
798
  // An li element’s end tag may be omitted if the li element is immediately followed by another li element or if there is no more content in the parent element.
786
- result = result || this._tag_stack.try_pop('li', ['ol', 'ul']);
799
+ result = result || this._tag_stack.try_pop('li', ['ol', 'ul', 'menu']);
787
800
 
788
801
  } else if (parser_token.tag_name === 'dd' || parser_token.tag_name === 'dt') {
789
802
  // A dd element’s end tag may be omitted if the dd element is immediately followed by another dd element or a dt element, or if there is no more content in the parent element.