Fixed #2260 - <style> and <script> tags indentation if also indenting angular control flows

This commit is contained in:
Gergely Gyorgy Both 2024-03-29 22:08:35 +01:00
parent 40c535662b
commit d5176ef294
4 changed files with 84 additions and 47 deletions

View File

@ -132,7 +132,8 @@ TemplatablePattern.prototype.__set_templated_pattern = function() {
if (!this._disabled.php) { if (!this._disabled.php) {
items.push(this.__patterns.php._starting_pattern.source); items.push(this.__patterns.php._starting_pattern.source);
} }
if (!this._disabled.handlebars) { // Handlebars ('{{' and '}}') are also special tokens in Angular)
if (!this._disabled.handlebars || !this._disabled.angular) {
items.push(this.__patterns.handlebars._starting_pattern.source); items.push(this.__patterns.handlebars._starting_pattern.source);
} }
if (!this._disabled.erb) { if (!this._disabled.erb) {

View File

@ -291,7 +291,7 @@ Beautifier.prototype.beautify = function() {
type: '' type: ''
}; };
var last_tag_token = new TagOpenParserToken(); var last_tag_token = new TagOpenParserToken(this._options);
var printer = new Printer(this._options, baseIndentString); var printer = new Printer(this._options, baseIndentString);
var tokens = new Tokenizer(source_text, this._options).tokenize(); var tokens = new Tokenizer(source_text, this._options).tokenize();
@ -614,7 +614,7 @@ Beautifier.prototype._handle_tag_open = function(printer, raw_token, last_tag_to
return parser_token; return parser_token;
}; };
var TagOpenParserToken = function(parent, raw_token) { var TagOpenParserToken = function(options, parent, raw_token) {
this.parent = parent || null; this.parent = parent || null;
this.text = ''; this.text = '';
this.type = 'TK_TAG_OPEN'; this.type = 'TK_TAG_OPEN';
@ -681,13 +681,14 @@ var TagOpenParserToken = function(parent, raw_token) {
} }
// handlebars tags that don't start with # or ^ are single_tags, and so also start and end. // handlebars tags that don't start with # or ^ are single_tags, and so also start and end.
// if they start with # or ^, they are still considered single tags if indenting of handlebars is set to false
this.is_end_tag = this.is_end_tag || this.is_end_tag = this.is_end_tag ||
(this.tag_start_char === '{' && (this.text.length < 3 || (/[^#\^]/.test(this.text.charAt(handlebar_starts))))); (this.tag_start_char === '{' && (!options.indent_handlebars || this.text.length < 3 || (/[^#\^]/.test(this.text.charAt(handlebar_starts)))));
} }
}; };
Beautifier.prototype._get_tag_open_token = function(raw_token) { //function to get a full tag and parse its type Beautifier.prototype._get_tag_open_token = function(raw_token) { //function to get a full tag and parse its type
var parser_token = new TagOpenParserToken(this._tag_stack.get_parser_token(), raw_token); var parser_token = new TagOpenParserToken(this._options, this._tag_stack.get_parser_token(), raw_token);
parser_token.alignment_size = this._options.wrap_attributes_indent_size; parser_token.alignment_size = this._options.wrap_attributes_indent_size;

View File

@ -130,6 +130,7 @@ Tokenizer.prototype._get_next_token = function(previous_token, open_token) { //
token = token || this._read_open_handlebars(c, open_token); token = token || this._read_open_handlebars(c, open_token);
token = token || this._read_attribute(c, previous_token, open_token); token = token || this._read_attribute(c, previous_token, open_token);
token = token || this._read_close(c, open_token); token = token || this._read_close(c, open_token);
token = token || this._read_script_and_style(c, previous_token);
token = token || this._read_control_flows(c, open_token); token = token || this._read_control_flows(c, open_token);
token = token || this._read_raw_content(c, previous_token, open_token); token = token || this._read_raw_content(c, previous_token, open_token);
token = token || this._read_content_word(c, open_token); token = token || this._read_content_word(c, open_token);
@ -215,8 +216,8 @@ Tokenizer.prototype._read_open_handlebars = function(c, open_token) {
var resulting_string = null; var resulting_string = null;
var token = null; var token = null;
if (!open_token || open_token.type === TOKEN.CONTROL_FLOW_OPEN) { if (!open_token || open_token.type === TOKEN.CONTROL_FLOW_OPEN) {
if (this._options.indent_handlebars && c === '{' && this._input.peek(1) === '{') { if ((this._options.templating.includes('angular') || this._options.indent_handlebars) && c === '{' && this._input.peek(1) === '{') {
if (this._input.peek(2) === '!') { if (this._options.indent_handlebars && this._input.peek(2) === '!') {
resulting_string = this.__patterns.handlebars_comment.read(); resulting_string = this.__patterns.handlebars_comment.read();
resulting_string = resulting_string || this.__patterns.handlebars.read(); resulting_string = resulting_string || this.__patterns.handlebars.read();
token = this._create_token(TOKEN.COMMENT, resulting_string); token = this._create_token(TOKEN.COMMENT, resulting_string);
@ -232,8 +233,8 @@ Tokenizer.prototype._read_open_handlebars = function(c, open_token) {
Tokenizer.prototype._read_control_flows = function(c, open_token) { Tokenizer.prototype._read_control_flows = function(c, open_token) {
var resulting_string = ''; var resulting_string = '';
var token = null; var token = null;
// Only check for control flows if angular templating is set AND indenting is set // Only check for control flows if angular templating is set
if (!this._options.templating.includes('angular') || !this._options.indent_handlebars) { if (!this._options.templating.includes('angular')) {
return token; return token;
} }
@ -327,13 +328,8 @@ Tokenizer.prototype._is_content_unformatted = function(tag_name) {
}; };
Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token) { // jshint unused:false Tokenizer.prototype._read_script_and_style = function(c, previous_token) { // jshint unused:false
var resulting_string = ''; if (previous_token.type === TOKEN.TAG_CLOSE && previous_token.opened.text[0] === '<' && previous_token.text[0] !== '/') {
if (open_token && open_token.text[0] === '{') {
resulting_string = this.__patterns.handlebars_raw_close.read();
} else if (previous_token.type === TOKEN.TAG_CLOSE &&
previous_token.opened.text[0] === '<' && previous_token.text[0] !== '/') {
// ^^ empty tag has no content
var tag_name = previous_token.opened.text.substr(1).toLowerCase(); var tag_name = previous_token.opened.text.substr(1).toLowerCase();
if (tag_name === 'script' || tag_name === 'style') { if (tag_name === 'script' || tag_name === 'style') {
// Script and style tags are allowed to have comments wrapping their content // Script and style tags are allowed to have comments wrapping their content
@ -343,8 +339,24 @@ Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token)
token.type = TOKEN.TEXT; token.type = TOKEN.TEXT;
return token; return token;
} }
resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig')); var resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
} else if (this._is_content_unformatted(tag_name)) { if (resulting_string) {
return this._create_token(TOKEN.TEXT, resulting_string);
}
}
}
return null;
};
Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token) { // jshint unused:false
var resulting_string = '';
if (open_token && open_token.text[0] === '{') {
resulting_string = this.__patterns.handlebars_raw_close.read();
} else if (previous_token.type === TOKEN.TAG_CLOSE &&
previous_token.opened.text[0] === '<' && previous_token.text[0] !== '/') {
// ^^ empty tag has no content
var tag_name = previous_token.opened.text.substr(1).toLowerCase();
if (this._is_content_unformatted(tag_name)) {
resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig')); resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
} }
@ -371,6 +383,7 @@ Tokenizer.prototype._read_content_word = function(c, open_token) {
if (resulting_string) { if (resulting_string) {
return this._create_token(TOKEN.TEXT, resulting_string); return this._create_token(TOKEN.TEXT, resulting_string);
} }
return null;
}; };
module.exports.Tokenizer = Tokenizer; module.exports.Tokenizer = Tokenizer;

View File

@ -3916,7 +3916,7 @@ exports.test_data = {
description: "https://github.com/beautify-web/js-beautify/issues/2219", description: "https://github.com/beautify-web/js-beautify/issues/2219",
template: "^^^ $$$", template: "^^^ $$$",
options: [ options: [
{ name: "templating", value: "'angular, handlebars'" } { name: "templating", value: "'angular'" }
], ],
tests: [{ tests: [{
input: [ input: [
@ -4240,18 +4240,7 @@ exports.test_data = {
] ]
}, { }, {
comment: 'CSS @media should remain unchanged', comment: 'CSS @media should remain unchanged',
// This behavior is currently incorrect. This codifies the way it fails. unchanged: [
// unchanged: [
// '<style type="text/css">',
// ' @media only screen and (min-width:480px) {',
// ' .mj-column-per-100 {',
// ' width: 100% !important;',
// ' max-width: 100%;',
// ' }',
// ' }',
// '</style>'
// ]
input: [
'<style type="text/css">', '<style type="text/css">',
' @media only screen and (min-width:480px) {', ' @media only screen and (min-width:480px) {',
' .mj-column-per-100 {', ' .mj-column-per-100 {',
@ -4260,30 +4249,63 @@ exports.test_data = {
' }', ' }',
' }', ' }',
'</style>' '</style>'
], ]
output: [ }, {
comment: 'CSS @media, the inside of <script> tag and control flows should be indented correctly',
fragment: true,
input: [
'<head>',
'<style type="text/css">', '<style type="text/css">',
'@media only screen and (min-width:480px) {', '@media only screen and (min-width:480px) {',
' .mj-column-per-100', '.mj-column-per-100 {',
' {', 'width: 100% !important;',
' width:', 'max-width: 100%;',
' 100%',
' !important;',
' max-width:',
' 100%;',
'}', '}',
' }', '}',
'</style>' '</style>',
'<script>',
'if(someExpression) {',
'callFunc();',
'}',
'</script>',
'</head>',
'<body>',
'<div>',
'@if(someOtherExpression) {',
'Text',
'}',
'</div>',
'</body>'
],
output: [
'<head>',
' <style type="text/css">',
' @media only screen and (min-width:480px) {',
' .mj-column-per-100 {',
' width: 100% !important;',
' max-width: 100%;',
' }',
' }',
' </style>',
' <script>',
' if (someExpression) {',
' callFunc();',
' }',
' </script>',
'</head>',
'<body>',
' <div>',
' @if(someOtherExpression) {',
' Text',
' }',
' </div>',
'</body>'
] ]
}] }]
}, { }, {
name: "No indenting for angular control flow should be done if indent_handlebars is false", name: "No indenting for angular control flow should be done if angular templating is not set",
description: "https://github.com/beautify-web/js-beautify/issues/2219", description: "https://github.com/beautify-web/js-beautify/issues/2219",
template: "^^^ $$$", template: "^^^ $$$",
options: [
{ name: "templating", value: "'angular, handlebars'" },
{ name: "indent_handlebars", value: "false" }
],
tests: [{ tests: [{
unchanged: [ unchanged: [
'@if (a > b) {', '@if (a > b) {',