From ce4d7061c4e63a93e14d315d880892bc13884c6a Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Wed, 28 Mar 2018 14:18:30 +0400 Subject: [PATCH 01/10] feat: preserve line breaks, make settings optional https://github.com/Scrum/posthtml-beautify/issues/220 --- package.json | 3 +- readme.md | 14 +++- src/index.js | 218 ++++++++++++++++++++++++++++++++++++--------------- src/rules.js | 4 +- 4 files changed, 174 insertions(+), 65 deletions(-) diff --git a/package.json b/package.json index 7eec7ec..1f98ccc 100644 --- a/package.json +++ b/package.json @@ -159,7 +159,8 @@ "test/**", "tmp/**", "lib/**", - "*.{html,jpg}" + "*.{html,jpg}", + ".idea/**" ], "rules": { "pkg-main": [ diff --git a/readme.md b/readme.md index 1a3f377..be98e98 100644 --- a/readme.md +++ b/readme.md @@ -162,13 +162,25 @@ Default: Default: '\n' Description: *As value is a string symbol which is added to the end of the row* + - **useExistingLineBreaks** + Type: `Boolean` + Default: false + Description: *Preserve and use existing line breaks. Do not add new line breaks. `blankLines` and `eol` are ignored if this option set to `true`* + + - **lowerAttributeName** + Type: `Boolean` + Default: true + Description: *Control case of attribute names* + - **eof** (*end of file*) Type: `String|Boolean` Default: '\n' Description: *As value is a string symbol which is added to the end of the file and will not adds if you specify a boolean value of `false`* ### `mini` -Type: `Object` +Type: `Object|Boolean(only false)` +Description: *Describe options for tidying up html output. Can be turned off with `false` value* + Default: - **removeAttribute** diff --git a/src/index.js b/src/index.js index 286c5b9..ef512d7 100644 --- a/src/index.js +++ b/src/index.js @@ -15,29 +15,98 @@ const optionsDefault = { } }; -const clean = tree => parser(render(tree)) - .filter(node => { - return typeof node === 'object' || (typeof node === 'string' && (node.trim().length !== 0 || /doctype/gi.test(node))); - }) - .map(node => { - if (Object.prototype.hasOwnProperty.call(node, 'content')) { - node.content = clean(node.content); +const horizontalWhitespace = /[\t ]+/gm; +const verticalWhitespace = /[\r\n]+/gm; + +const getEdgeWhitespace = (node) => { + let leftWhitespace; + let rightWhitespace; + let leftLinebreaks; + let rightLinebreaks; + + if (typeof node === 'string') { + const nodeTrimmed = node.trim(); + + if (nodeTrimmed.length === 0) { + leftWhitespace = node.replace(verticalWhitespace, ''); + rightWhitespace = node.replace(verticalWhitespace, ''); + leftLinebreaks = node.replace(horizontalWhitespace, ''); + rightLinebreaks = node.replace(horizontalWhitespace, ''); + } else { + leftWhitespace = node.trimRight().replace(nodeTrimmed, ''); + rightWhitespace = node.trimLeft().replace(nodeTrimmed, ''); + leftLinebreaks = leftWhitespace.replace(horizontalWhitespace, ''); + rightLinebreaks = rightWhitespace.replace(horizontalWhitespace, ''); } - return typeof node === 'string' ? node.trim() : node; - }); + return {leftWhitespace, rightWhitespace, leftLinebreaks, rightLinebreaks}; + } else { + return false; + } -const parseConditional = tree => { +}; + +const clean = (tree, options) => { + const parsedTree = parser(render(tree)); + let previousNodeRightLinebreaks = ''; + + if (options.rules.useExistingLineBreaks) { + return parsedTree + .map(node => { + if (Object.prototype.hasOwnProperty.call(node, 'content')) { + node.content = clean(node.content, options); + } + + if (typeof node === 'string') { + const nodeTrimmed = node.trim(); + const {leftWhitespace, rightWhitespace, leftLinebreaks, rightLinebreaks} = getEdgeWhitespace(node); + let nodeCleaned; + + if (nodeTrimmed.length === 0) { + nodeCleaned = node.replace(horizontalWhitespace, '') || ' '; + } else { + nodeCleaned = + `${leftLinebreaks || (previousNodeRightLinebreaks ? '' : leftWhitespace.replace(horizontalWhitespace, ' '))}` + + `${nodeTrimmed.replace(horizontalWhitespace, ' ').replace(/([\r\n]+)[\t ]/gm, '$1')}` + + `${rightLinebreaks || rightWhitespace.replace(horizontalWhitespace, ' ')}`; + + previousNodeRightLinebreaks = rightLinebreaks; + } + + return nodeCleaned; + } else { + previousNodeRightLinebreaks = ''; + + return node; + } + }); + } else { + return parsedTree + .filter(node => { + return typeof node === 'object' || (typeof node === 'string' && (node.trim().length !== 0 || /doctype/gi.test(node))); + }) + .map(node => { + if (Object.prototype.hasOwnProperty.call(node, 'content')) { + node.content = clean(node.content, options); + } + + return typeof node === 'string' ? node.trim() : node; + }) + } + +}; + +const parseConditional = (tree, options) => { return tree.map(node => { if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'content')) { - node.content = parseConditional(node.content); + node.content = parseConditional(node.content, options); } if (typeof node === 'string' && //.test(node)) { const conditional = /^((?:<[^>]+>)?(?:)?)([\s\S]*?)()$/ .exec(node) .slice(1) - .map((node, index) => index === 1 ? {tag: 'conditional-content', content: clean(parser(node))} : node); + .map((node, index) => index === 1 ? {tag: 'conditional-content', content: clean(parser(node), options)} : node); return { tag: 'conditional', @@ -68,55 +137,80 @@ const renderConditional = tree => { }, []); }; -const indent = (tree, {rules: {indent, eol, blankLines}}) => { +const indent = (tree, {rules: {indent, eol, blankLines, useExistingLineBreaks}}) => { const indentString = typeof indent === 'number' ? ' '.repeat(indent) : '\t'; + let getIndent; + let setIndent; + + if (useExistingLineBreaks) { + getIndent = level => `${indentString.repeat(level)}`; + + setIndent = (tree, level = 0) => tree.map((node, i) => { + if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'content')) { + node.content = setIndent(node.content, ++level); + --level; + } + + if (typeof node === 'string') { + return node.replace(/([\r\n]+)/gm, function (match, p1, offset, string) { + if ((tree.length - 1 === i) && (offset + match.length === string.length)) { + --level; + } + + return `${p1}${getIndent(Math.max(level, 0))}` + }); + } + + return node; + }); + } else { + getIndent = level => `${eol}${indentString.repeat(level)}`; + + setIndent = (tree, level = 0) => tree.reduce((previousValue, node, index) => { + if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'content')) { + node.content = setIndent(node.content, ++level); + --level; + } + + if (tree.length === 1 && typeof tree[index] === 'string') { + return [...previousValue, node]; + } + + if (level === 0 && (tree.length - 1) === index && tree.length > 1) { + return [...previousValue, getIndent(level), node]; + } + + if (level === 0 && (tree.length - 1) === index && tree.length === 1) { + return [...previousValue, node]; + } + + if (level === 0 && index === 0) { + return [...previousValue, node, blankLines]; + } + + if (level === 0) { + return [...previousValue, getIndent(level), node, blankLines]; + } + + if ((tree.length - 1) === index) { + return [...previousValue, getIndent(level), node, getIndent(--level)]; + } + + if (typeof node === 'string' && //.test(node)) { + return [...previousValue, getIndent(level), node, blankLines]; + } + + if (typeof node === 'string' && //.test(node)) { + return [...previousValue, getIndent(level), node]; + } + + if (node.tag === false) { + return [...previousValue, ...node.content.slice(0, -1)]; + } - const getIndent = level => `${eol}${indentString.repeat(level)}`; - - const setIndent = (tree, level = 0) => tree.reduce((previousValue, node, index) => { - if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'content')) { - node.content = setIndent(node.content, ++level); - --level; - } - - if (tree.length === 1 && typeof tree[index] === 'string') { - return [...previousValue, node]; - } - - if (level === 0 && (tree.length - 1) === index && tree.length > 1) { - return [...previousValue, getIndent(level), node]; - } - - if (level === 0 && (tree.length - 1) === index && tree.length === 1) { - return [...previousValue, node]; - } - - if (level === 0 && index === 0) { - return [...previousValue, node, blankLines]; - } - - if (level === 0) { return [...previousValue, getIndent(level), node, blankLines]; - } - - if ((tree.length - 1) === index) { - return [...previousValue, getIndent(level), node, getIndent(--level)]; - } - - if (typeof node === 'string' && //.test(node)) { - return [...previousValue, getIndent(level), node, blankLines]; - } - - if (typeof node === 'string' && //.test(node)) { - return [...previousValue, getIndent(level), node]; - } - - if (node.tag === false) { - return [...previousValue, ...node.content.slice(0, -1)]; - } - - return [...previousValue, getIndent(level), node, blankLines]; - }, []); + }, []); + } return setIndent(tree); }; @@ -141,7 +235,7 @@ const attrsBoolean = (tree, {attrs: {boolean}}) => { return node; }); - return removeAttrValue(tree); + return boolean ? removeAttrValue(tree) : tree; }; const lowerElementName = (tree, {tags}) => { @@ -166,7 +260,7 @@ const lowerElementName = (tree, {tags}) => { return bypass(tree); }; -const lowerAttributeName = tree => { +const lowerAttributeName = (tree, {rules: {lowerAttributeName}}) => { const bypass = tree => tree.map(node => { if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'content')) { node.content = bypass(node.content); @@ -182,7 +276,7 @@ const lowerAttributeName = tree => { return node; }); - return bypass(tree); + return lowerAttributeName ? bypass(tree) : tree; }; const eof = (tree, {rules: {eof}}) => eof ? [...tree, eof] : tree; @@ -213,7 +307,7 @@ const mini = (tree, {mini}) => { return node; }); - return bypass(tree); + return mini ? bypass(tree) : tree; }; const beautify = (tree, options) => [ diff --git a/src/rules.js b/src/rules.js index bfcbacb..0e9ee91 100644 --- a/src/rules.js +++ b/src/rules.js @@ -2,5 +2,7 @@ export default { indent: 2, blankLines: '\n', eof: '\n', - eol: '\n' + eol: '\n', + useExistingLineBreaks: false, + lowerAttributeName: true }; From 6c5cede007ce2f97ebd8e406e604667f7195327a Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Thu, 29 Mar 2018 01:25:25 +0400 Subject: [PATCH 02/10] fix: edge case and refactor https://github.com/Scrum/posthtml-beautify/pull/222#issuecomment-376881724 --- readme.md | 2 +- src/index.js | 44 ++++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/readme.md b/readme.md index be98e98..0f8fc1e 100644 --- a/readme.md +++ b/readme.md @@ -165,7 +165,7 @@ Default: - **useExistingLineBreaks** Type: `Boolean` Default: false - Description: *Preserve and use existing line breaks. Do not add new line breaks. `blankLines` and `eol` are ignored if this option set to `true`* + Description: *Preserve and use existing line breaks. Do not add new line breaks (exception: absent line break before closing tag is restored if matching opening tag has line break as first child). `blankLines` and `eol` are ignored if this option set to `true`* - **lowerAttributeName** Type: `Boolean` diff --git a/src/index.js b/src/index.js index ef512d7..1d9a364 100644 --- a/src/index.js +++ b/src/index.js @@ -139,39 +139,39 @@ const renderConditional = tree => { const indent = (tree, {rules: {indent, eol, blankLines, useExistingLineBreaks}}) => { const indentString = typeof indent === 'number' ? ' '.repeat(indent) : '\t'; - let getIndent; - let setIndent; - if (useExistingLineBreaks) { - getIndent = level => `${indentString.repeat(level)}`; + const getIndent = level => `${useExistingLineBreaks ? '' : eol}${indentString.repeat(level)}`; - setIndent = (tree, level = 0) => tree.map((node, i) => { - if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'content')) { + const setIndent = (tree, level = 0) => tree.reduce((previousValue, node, index) => { + if (typeof node === 'object') { + if (Object.prototype.hasOwnProperty.call(node, 'content')) { node.content = setIndent(node.content, ++level); --level; } + if ( + useExistingLineBreaks + && (index === tree.length - 1) + && typeof tree[0] === 'string' + && getEdgeWhitespace(tree[0]).leftLinebreaks + ) { + return [...previousValue, node, `\n${getIndent(--level)}`]; + } + } + + if (useExistingLineBreaks) { if (typeof node === 'string') { - return node.replace(/([\r\n]+)/gm, function (match, p1, offset, string) { - if ((tree.length - 1 === i) && (offset + match.length === string.length)) { + return [...previousValue, node.replace(/([\r\n]+)/gm, function (match, p1, offset, string) { + if ((index === tree.length - 1) && (offset + match.length === string.length)) { --level; } return `${p1}${getIndent(Math.max(level, 0))}` - }); - } - - return node; - }); - } else { - getIndent = level => `${eol}${indentString.repeat(level)}`; - - setIndent = (tree, level = 0) => tree.reduce((previousValue, node, index) => { - if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'content')) { - node.content = setIndent(node.content, ++level); - --level; + })]; } + return [...previousValue, node]; + } else { if (tree.length === 1 && typeof tree[index] === 'string') { return [...previousValue, node]; } @@ -209,8 +209,8 @@ const indent = (tree, {rules: {indent, eol, blankLines, useExistingLineBreaks}}) } return [...previousValue, getIndent(level), node, blankLines]; - }, []); - } + } + }, []); return setIndent(tree); }; From dd5de3b8d8b639c68ce796ad644c95849987c752 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Thu, 29 Mar 2018 01:34:24 +0400 Subject: [PATCH 03/10] fix: refactor --- src/index.js | 67 +++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/src/index.js b/src/index.js index 1d9a364..1aef412 100644 --- a/src/index.js +++ b/src/index.js @@ -47,53 +47,46 @@ const getEdgeWhitespace = (node) => { }; const clean = (tree, options) => { - const parsedTree = parser(render(tree)); let previousNodeRightLinebreaks = ''; - if (options.rules.useExistingLineBreaks) { - return parsedTree - .map(node => { - if (Object.prototype.hasOwnProperty.call(node, 'content')) { - node.content = clean(node.content, options); - } - - if (typeof node === 'string') { - const nodeTrimmed = node.trim(); - const {leftWhitespace, rightWhitespace, leftLinebreaks, rightLinebreaks} = getEdgeWhitespace(node); - let nodeCleaned; + return parser(render(tree)) + .filter(node => { + return options.rules.useExistingLineBreaks + ? node + : typeof node === 'object' || (typeof node === 'string' && (node.trim().length !== 0 || /doctype/gi.test(node))); + }) + .map(node => { + if (Object.prototype.hasOwnProperty.call(node, 'content')) { + node.content = clean(node.content, options); + } - if (nodeTrimmed.length === 0) { - nodeCleaned = node.replace(horizontalWhitespace, '') || ' '; - } else { - nodeCleaned = - `${leftLinebreaks || (previousNodeRightLinebreaks ? '' : leftWhitespace.replace(horizontalWhitespace, ' '))}` + - `${nodeTrimmed.replace(horizontalWhitespace, ' ').replace(/([\r\n]+)[\t ]/gm, '$1')}` + - `${rightLinebreaks || rightWhitespace.replace(horizontalWhitespace, ' ')}`; + if (typeof node === 'string') { + if (!options.rules.useExistingLineBreaks) { + return node.trim(); + } - previousNodeRightLinebreaks = rightLinebreaks; - } + const nodeTrimmed = node.trim(); + const {leftWhitespace, rightWhitespace, leftLinebreaks, rightLinebreaks} = getEdgeWhitespace(node); + let nodeCleaned; - return nodeCleaned; + if (nodeTrimmed.length === 0) { + nodeCleaned = node.replace(horizontalWhitespace, '') || ' '; } else { - previousNodeRightLinebreaks = ''; + nodeCleaned = + `${leftLinebreaks || (previousNodeRightLinebreaks ? '' : leftWhitespace.replace(horizontalWhitespace, ' '))}` + + `${nodeTrimmed.replace(horizontalWhitespace, ' ').replace(/([\r\n]+)[\t ]/gm, '$1')}` + + `${rightLinebreaks || rightWhitespace.replace(horizontalWhitespace, ' ')}`; - return node; - } - }); - } else { - return parsedTree - .filter(node => { - return typeof node === 'object' || (typeof node === 'string' && (node.trim().length !== 0 || /doctype/gi.test(node))); - }) - .map(node => { - if (Object.prototype.hasOwnProperty.call(node, 'content')) { - node.content = clean(node.content, options); + previousNodeRightLinebreaks = rightLinebreaks; } - return typeof node === 'string' ? node.trim() : node; - }) - } + return nodeCleaned; + } else { + previousNodeRightLinebreaks = ''; + return node; + } + }); }; const parseConditional = (tree, options) => { From 97c01d5a6c433e0769d68c775cb601d2c51532e2 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Mon, 2 Apr 2018 12:17:38 +0400 Subject: [PATCH 04/10] fix: bugs and refactor --- readme.md | 2 +- src/index.js | 42 +++++++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/readme.md b/readme.md index 0f8fc1e..1179b5f 100644 --- a/readme.md +++ b/readme.md @@ -165,7 +165,7 @@ Default: - **useExistingLineBreaks** Type: `Boolean` Default: false - Description: *Preserve and use existing line breaks. Do not add new line breaks (exception: absent line break before closing tag is restored if matching opening tag has line break as first child). `blankLines` and `eol` are ignored if this option set to `true`* + Description: *Preserve existing line breaks. Don't add new line breaks (exception: adds missing line break at the start/end of a tag if it already has line break at the end/start). `blankLines` and `eol` are ignored if this option set to `true`* - **lowerAttributeName** Type: `Boolean` diff --git a/src/index.js b/src/index.js index 1aef412..eb5d989 100644 --- a/src/index.js +++ b/src/index.js @@ -136,31 +136,38 @@ const indent = (tree, {rules: {indent, eol, blankLines, useExistingLineBreaks}}) const getIndent = level => `${useExistingLineBreaks ? '' : eol}${indentString.repeat(level)}`; const setIndent = (tree, level = 0) => tree.reduce((previousValue, node, index) => { - if (typeof node === 'object') { - if (Object.prototype.hasOwnProperty.call(node, 'content')) { - node.content = setIndent(node.content, ++level); - --level; + if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'content')) { + node.content = setIndent(node.content, ++level); + --level; + } + + if (useExistingLineBreaks) { + if (typeof node === 'string') { + node = node.replace(/([\r\n]+)/gm, function (match, p1, offset, string) { + if ((index === tree.length - 1) && (offset + match.length === string.length)) { + --level; + } + + return `${p1}${getIndent(Math.max(level, 0))}` + }); } if ( - useExistingLineBreaks - && (index === tree.length - 1) + (index === tree.length - 1) && typeof tree[0] === 'string' && getEdgeWhitespace(tree[0]).leftLinebreaks + && ((typeof node === 'string' && node.trim() && !getEdgeWhitespace(node).rightLinebreaks) || typeof node === 'object') ) { - return [...previousValue, node, `\n${getIndent(--level)}`]; + return [ ...previousValue, node, `\n${getIndent(--level)}` ]; } - } - - if (useExistingLineBreaks) { - if (typeof node === 'string') { - return [...previousValue, node.replace(/([\r\n]+)/gm, function (match, p1, offset, string) { - if ((index === tree.length - 1) && (offset + match.length === string.length)) { - --level; - } - return `${p1}${getIndent(Math.max(level, 0))}` - })]; + if ( + (index === 0) + && typeof tree[tree.length - 1] === 'string' + && getEdgeWhitespace(tree[tree.length - 1]).rightLinebreaks + && ((typeof node === 'string' && node.trim() && !getEdgeWhitespace(node).leftLinebreaks) || typeof node === 'object') + ) { + return [ ...previousValue, `\n${getIndent(typeof node === 'string' ? ++level : level)}`, node]; } return [...previousValue, node]; @@ -242,6 +249,7 @@ const lowerElementName = (tree, {tags}) => { if ( typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'tag') && + typeof node.tag === 'string' && tags.includes(node.tag.toLowerCase()) ) { node.tag = node.tag.toLowerCase(); From e2c0372566feacd66c02ad50f80e2a12fb7dc4e2 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Mon, 2 Apr 2018 12:18:39 +0400 Subject: [PATCH 05/10] test: add tests --- test/expected/output-with-line-breaks.html | 47 +++++++++++++++++++ .../output-with-unbalanced-line-breaks.html | 28 +++++++++++ test/fixtures/input-with-line-breaks.html | 47 +++++++++++++++++++ .../input-with-unbalanced-line-breaks.html | 23 +++++++++ test/test-plugin.js | 12 +++++ 5 files changed, 157 insertions(+) create mode 100644 test/expected/output-with-line-breaks.html create mode 100644 test/expected/output-with-unbalanced-line-breaks.html create mode 100644 test/fixtures/input-with-line-breaks.html create mode 100644 test/fixtures/input-with-unbalanced-line-breaks.html diff --git a/test/expected/output-with-line-breaks.html b/test/expected/output-with-line-breaks.html new file mode 100644 index 0000000..0d9e73d --- /dev/null +++ b/test/expected/output-with-line-breaks.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + +
NameDescription
A + Description of A +
BDescription of B
+ +
+

+ Some inline text +

+ +
+ A +
+ B +
+ D +
+ +
+ + +
+ + diff --git a/test/expected/output-with-unbalanced-line-breaks.html b/test/expected/output-with-unbalanced-line-breaks.html new file mode 100644 index 0000000..085daed --- /dev/null +++ b/test/expected/output-with-unbalanced-line-breaks.html @@ -0,0 +1,28 @@ + + + + + + + + +
    +
  • + Need balancing +
  • +
  • + Need balancing +
  • +
  • + Need balancing +
  • +
  • + Need balancing +
  • +
  • Don't need balancing
  • +
  • Don't need balancing
  • +
+ + diff --git a/test/fixtures/input-with-line-breaks.html b/test/fixtures/input-with-line-breaks.html new file mode 100644 index 0000000..1ec07ea --- /dev/null +++ b/test/fixtures/input-with-line-breaks.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + +
NameDescription
A + Description of A +
BDescription of B
+ +
+

+ Some inline text +

+ +
+ A +
+ B +
+ D +
+ +
+ + +
+ + \ No newline at end of file diff --git a/test/fixtures/input-with-unbalanced-line-breaks.html b/test/fixtures/input-with-unbalanced-line-breaks.html new file mode 100644 index 0000000..76cbafe --- /dev/null +++ b/test/fixtures/input-with-unbalanced-line-breaks.html @@ -0,0 +1,23 @@ + + + + + + + + +
    +
  • + Need balancing
  • +
  • + Need balancing
  • +
  • Need balancing +
  • +
  • Need balancing +
  • +
  • Don't need balancing
  • +
  • Don't need balancing
  • +
+ + \ No newline at end of file diff --git a/test/test-plugin.js b/test/test-plugin.js index 3d1f3fd..09dff55 100644 --- a/test/test-plugin.js +++ b/test/test-plugin.js @@ -126,3 +126,15 @@ test('processing with plugin beautify and include should return equal html which const fixtures = (await processing(await read('test/fixtures/input-conditional-comment.html'), [require('posthtml-include')(), beautify()])).html; t.deepEqual(expected, fixtures); }); + +test('processing with plugin beautify should keep existing line breaks if instructed', async t => { + const expected = await read('test/expected/output-with-line-breaks.html'); + const fixtures = (await processing(await read('test/fixtures/input-with-line-breaks.html'), [beautify({rules: {useExistingLineBreaks: true}})])).html; + t.deepEqual(expected, fixtures); +}); + +test('processing with plugin beautify and `useExistingLineBreaks: true` should insert missing line break as a last child of a tag if there is a line break as a first child of this tag', async t => { + const expected = await read('test/expected/output-with-unbalanced-line-breaks.html'); + const fixtures = (await processing(await read('test/fixtures/input-with-unbalanced-line-breaks.html'), [beautify({rules: {useExistingLineBreaks: true}})])).html; + t.deepEqual(expected, fixtures); +}); From d155cb6a9babc323f45ec3827df698ccf274f095 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Mon, 2 Apr 2018 14:08:21 +0400 Subject: [PATCH 06/10] fix: don't balance level 0 --- src/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index eb5d989..071cd88 100644 --- a/src/index.js +++ b/src/index.js @@ -153,7 +153,8 @@ const indent = (tree, {rules: {indent, eol, blankLines, useExistingLineBreaks}}) } if ( - (index === tree.length - 1) + level > 0 + && (index === tree.length - 1) && typeof tree[0] === 'string' && getEdgeWhitespace(tree[0]).leftLinebreaks && ((typeof node === 'string' && node.trim() && !getEdgeWhitespace(node).rightLinebreaks) || typeof node === 'object') @@ -162,7 +163,8 @@ const indent = (tree, {rules: {indent, eol, blankLines, useExistingLineBreaks}}) } if ( - (index === 0) + level > 0 + && (index === 0) && typeof tree[tree.length - 1] === 'string' && getEdgeWhitespace(tree[tree.length - 1]).rightLinebreaks && ((typeof node === 'string' && node.trim() && !getEdgeWhitespace(node).leftLinebreaks) || typeof node === 'object') From cae18af78bfa66da67a77cab2fc472e4132bd007 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Mon, 9 Apr 2018 11:36:40 +0400 Subject: [PATCH 07/10] test: clarify test description --- test/test-plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-plugin.js b/test/test-plugin.js index 09dff55..5112abf 100644 --- a/test/test-plugin.js +++ b/test/test-plugin.js @@ -133,7 +133,7 @@ test('processing with plugin beautify should keep existing line breaks if instru t.deepEqual(expected, fixtures); }); -test('processing with plugin beautify and `useExistingLineBreaks: true` should insert missing line break as a last child of a tag if there is a line break as a first child of this tag', async t => { +test('processing with plugin beautify and `useExistingLineBreaks: true` should insert missing line break as a first/last child of a tag if there is a line break as a last/first child of this tag', async t => { const expected = await read('test/expected/output-with-unbalanced-line-breaks.html'); const fixtures = (await processing(await read('test/fixtures/input-with-unbalanced-line-breaks.html'), [beautify({rules: {useExistingLineBreaks: true}})])).html; t.deepEqual(expected, fixtures); From e11b14331753425f6f542b0d517484bedeb56590 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Tue, 1 May 2018 16:30:45 +0400 Subject: [PATCH 08/10] fix: clarify vertical whitespace regexp --- src/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 071cd88..2fd41d6 100644 --- a/src/index.js +++ b/src/index.js @@ -16,7 +16,7 @@ const optionsDefault = { }; const horizontalWhitespace = /[\t ]+/gm; -const verticalWhitespace = /[\r\n]+/gm; +const verticalWhitespace = /[\r\n\v\f]+/gm; const getEdgeWhitespace = (node) => { let leftWhitespace; @@ -74,7 +74,7 @@ const clean = (tree, options) => { } else { nodeCleaned = `${leftLinebreaks || (previousNodeRightLinebreaks ? '' : leftWhitespace.replace(horizontalWhitespace, ' '))}` + - `${nodeTrimmed.replace(horizontalWhitespace, ' ').replace(/([\r\n]+)[\t ]/gm, '$1')}` + + `${nodeTrimmed.replace(horizontalWhitespace, ' ').replace(/([\r\n\v\f]+)[\t ]/gm, '$1')}` + `${rightLinebreaks || rightWhitespace.replace(horizontalWhitespace, ' ')}`; previousNodeRightLinebreaks = rightLinebreaks; @@ -143,7 +143,7 @@ const indent = (tree, {rules: {indent, eol, blankLines, useExistingLineBreaks}}) if (useExistingLineBreaks) { if (typeof node === 'string') { - node = node.replace(/([\r\n]+)/gm, function (match, p1, offset, string) { + node = node.replace(/([\r\n\v\f]+)/gm, function (match, p1, offset, string) { if ((index === tree.length - 1) && (offset + match.length === string.length)) { --level; } From 80930e1c913b10d5ce37180c8620071ce6ee056f Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Tue, 1 May 2018 16:49:25 +0400 Subject: [PATCH 09/10] fix: m regexp flag is not needed --- src/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 2fd41d6..a64b90d 100644 --- a/src/index.js +++ b/src/index.js @@ -15,8 +15,8 @@ const optionsDefault = { } }; -const horizontalWhitespace = /[\t ]+/gm; -const verticalWhitespace = /[\r\n\v\f]+/gm; +const horizontalWhitespace = /[\t ]+/g; +const verticalWhitespace = /[\r\n\v\f]+/g; const getEdgeWhitespace = (node) => { let leftWhitespace; @@ -74,7 +74,7 @@ const clean = (tree, options) => { } else { nodeCleaned = `${leftLinebreaks || (previousNodeRightLinebreaks ? '' : leftWhitespace.replace(horizontalWhitespace, ' '))}` + - `${nodeTrimmed.replace(horizontalWhitespace, ' ').replace(/([\r\n\v\f]+)[\t ]/gm, '$1')}` + + `${nodeTrimmed.replace(horizontalWhitespace, ' ').replace(/([\r\n\v\f]+)[\t ]/g, '$1')}` + `${rightLinebreaks || rightWhitespace.replace(horizontalWhitespace, ' ')}`; previousNodeRightLinebreaks = rightLinebreaks; @@ -143,7 +143,7 @@ const indent = (tree, {rules: {indent, eol, blankLines, useExistingLineBreaks}}) if (useExistingLineBreaks) { if (typeof node === 'string') { - node = node.replace(/([\r\n\v\f]+)/gm, function (match, p1, offset, string) { + node = node.replace(/([\r\n\v\f]+)/g, function (match, p1, offset, string) { if ((index === tree.length - 1) && (offset + match.length === string.length)) { --level; } From e8642b1b0a1d30d1602b13e1a21018e5ac8a9276 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Wed, 2 May 2018 00:29:47 +0400 Subject: [PATCH 10/10] fix: get rid of non-standard methods --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index a64b90d..386be52 100644 --- a/src/index.js +++ b/src/index.js @@ -33,8 +33,8 @@ const getEdgeWhitespace = (node) => { leftLinebreaks = node.replace(horizontalWhitespace, ''); rightLinebreaks = node.replace(horizontalWhitespace, ''); } else { - leftWhitespace = node.trimRight().replace(nodeTrimmed, ''); - rightWhitespace = node.trimLeft().replace(nodeTrimmed, ''); + leftWhitespace = node.replace(/\s+$/,'').replace(nodeTrimmed, ''); + rightWhitespace = node.replace(/^\s+/,'').replace(nodeTrimmed, ''); leftLinebreaks = leftWhitespace.replace(horizontalWhitespace, ''); rightLinebreaks = rightWhitespace.replace(horizontalWhitespace, ''); }