diff --git a/packages/expression-parser/package-lock.json b/packages/expression-parser/package-lock.json index a0bf0f1..4df2da9 100644 --- a/packages/expression-parser/package-lock.json +++ b/packages/expression-parser/package-lock.json @@ -1,6 +1,6 @@ { "name": "@starptech/expression-parser", - "version": "0.8.4", + "version": "0.8.12", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1778,12 +1778,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1798,17 +1800,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -1925,7 +1930,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -1937,6 +1943,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1951,6 +1958,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1958,12 +1966,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -1982,6 +1992,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2062,7 +2073,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2074,6 +2086,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2195,6 +2208,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/packages/prettyhtml-formatter/index.js b/packages/prettyhtml-formatter/index.js index 1e92b3b..61745f0 100644 --- a/packages/prettyhtml-formatter/index.js +++ b/packages/prettyhtml-formatter/index.js @@ -114,25 +114,7 @@ function format(options) { } if (is('comment', node)) { - /** - * indent last line of comment - * e.g - * - * to - * - */ - - let commentLines = node.value.split(single) - if (commentLines.length > 1) { - commentLines[commentLines.length - 1] = - repeat(indent, level - 1) + - commentLines[commentLines.length - 1].trim() - node.value = commentLines.join(single) - } + indentComment(node, indent, level) } /** @@ -144,7 +126,7 @@ function format(options) { // clear empty script, textarea, pre, style tags if (length) { - const empty = containsOnlyEmptyTextNodes(node) + const empty = hasOnlyEmptyTextChildren(node) const isEmbeddedContent = isElement(node, 'style') || isElement(node, 'script') if (empty) { @@ -158,6 +140,9 @@ function format(options) { return visit.SKIP } + let newline = false + const willBreak = collapsed(node, children) + /** * Indent children */ @@ -166,12 +151,16 @@ function format(options) { let child = children[index] // only indent text in nodes - // root text nodes should't influence other root nodes + // root text nodes should't influence other root nodes^^ if (node.type === 'root') { break } if (is('text', child)) { + if (containsNewline(child) || willBreak) { + newline = true + } + child.value = child.value // reduce newlines to one newline // $& contains the lastMatch @@ -182,12 +171,9 @@ function format(options) { // reset result = [] index = -1 - node.children = result - let prevChild - let hasLeadingNewline = false - + let prevChild = null if (length) { // walk through children // hint: a child has no children informations we already walking through @@ -215,25 +201,14 @@ function format(options) { value: double + repeat(indent, indentLevel) }) } else if ( - !endsWithNewline(prevChild) && - beforeChildNodeAddedHook(node, children, child, index, prevChild) + beforeChildNodeAddedHook(node, children, child, index, prevChild) || + (newline && index === 0) ) { - // all template expression are indented on a ewline thats why need to check - // so that we don't add another one - if (index === 0 && checkForTemplateExpression(child.value)) { - hasLeadingNewline = true - } // only necessary because we are trying to indent tags on newlines // even when in inline context when possible if (is('text', prevChild)) { // remove trailing whitespaces and tabs because a newline is inserted before prevChild.value = prevChild.value.replace(/[ \t]+$/, '') - - // adds a leading newline because the sibling node is indented on a newline - if (index === 1 && hasLeadingNewline === false) { - prevChild.value = - single + repeat(indent, indentLevel) + prevChild.value - } } // remove leading whitespaces and tabs because a newline is inserted before if (is('text', child)) { @@ -245,15 +220,6 @@ function format(options) { value: single + repeat(indent, indentLevel) }) } - // adds a leading newline when he sibling element was inteded on a newline and when no newlines was added - else if ( - is('text', child) && - hasLeadingNewline === false && - endsWithNewline(child) && - !startsWithNewline(child) - ) { - child.value = single + repeat(indent, indentLevel) + child.value - } prevChild = child @@ -261,7 +227,7 @@ function format(options) { } } - if (afterChildNodesAddedHook(node, prevChild)) { + if (afterChildNodesAddedHook(node, prevChild) || newline) { result.push({ type: 'text', value: single + repeat(indent, level - 1) @@ -279,12 +245,36 @@ function startsWithNewline(node) { return is('text', node) && node.value && /^\s*\n/.test(node.value) } +function containsNewline(node) { + return node.value.indexOf(single) !== -1 +} + +/** + * indent last line of comment + * e.g + * + * to + * + */ +function indentComment(node, indent, level) { + let commentLines = node.value.split(single) + if (commentLines.length > 1) { + commentLines[commentLines.length - 1] = + repeat(indent, level - 1) + commentLines[commentLines.length - 1].trim() + node.value = commentLines.join(single) + } +} + function handleTemplateExpression(child, children) { const brackets = checkForTemplateExpression(child.value) if (brackets) { // dont touch nodes with single text element if ( - containsOnlyTextNodes({ + hasOnlyTextChildren({ children }) ) { @@ -300,7 +290,30 @@ function handleTemplateExpression(child, children) { } } +/** + * Check if any children will be wrapped on a newline + * @param {*} node + * @param {*} children + */ +function collapsed(node, children) { + let index = -1 + let prevChild = false + while (++index < children.length) { + let child = children[index] + if (beforeChildNodeAddedHook(node, children, child, index, prevChild)) { + return true + } + prevChild = child + } +} + function beforeChildNodeAddedHook(node, children, child, index, prev) { + // don't add newline when prev child already has one + if (endsWithNewline(prev)) { + return false + } + + // every template expression is indented on a newline if (is('text', child) && handleTemplateExpression(child, children)) { return true } @@ -310,11 +323,12 @@ function beforeChildNodeAddedHook(node, children, child, index, prev) { return true } + // embedded content is indented on newlines if (isElement(child, ['script', 'style']) && index !== 0) { return true } - // don't add newline on the first element + // don't add newline on the first element of the page const isRootElement = node.type === 'root' && index === 0 if (isRootElement) { return false @@ -336,15 +350,15 @@ function afterChildNodesAddedHook(node, prev) { /** * e.g */ - if (hasChilds && !containsOnlyTextNodes(node) && !isVoid(node)) { + if (hasChilds && !hasOnlyTextChildren(node) && !isVoid(node)) { return true } /** * e.g */ - const isPrevRawText = is('text', prev) - return hasChilds && !isVoid(node) && !isPrevRawText + const isPrevTextNode = is('text', prev) + return hasChilds && !isVoid(node) && !isPrevTextNode } function checkForTemplateExpression(value) { @@ -363,7 +377,7 @@ function checkForTemplateExpression(value) { return null } -function containsOnlyTextNodes(node) { +function hasOnlyTextChildren(node) { const children = node.children || [] if (children.length === 0) { @@ -373,7 +387,7 @@ function containsOnlyTextNodes(node) { return children.every(n => is('text', n)) } -function containsOnlyEmptyTextNodes(node) { +function hasOnlyEmptyTextChildren(node) { const children = node.children || [] if (children.length === 0) { diff --git a/packages/prettyhtml-formatter/test/fixtures/indent-multiline-text/output.html b/packages/prettyhtml-formatter/test/fixtures/indent-multiline-text/output.html index 2c30d0f..f09db94 100644 --- a/packages/prettyhtml-formatter/test/fixtures/indent-multiline-text/output.html +++ b/packages/prettyhtml-formatter/test/fixtures/indent-multiline-text/output.html @@ -1,6 +1,8 @@ -