[d24f17c] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | const repeat = require('repeat-string');
|
---|
| 4 |
|
---|
| 5 | const splitOnTags = str => str.split(/(<\/?[^>]+>)/g).filter(line => line.trim() !== '');
|
---|
| 6 | const isTag = str => /<[^>!]+>/.test(str);
|
---|
| 7 | const isClosingTag = str => /<\/+[^>]+>/.test(str);
|
---|
| 8 | const isSelfClosingTag = str => /<[^>]+\/>/.test(str);
|
---|
| 9 | const isOpeningTag = str => isTag(str) && !isClosingTag(str) && !isSelfClosingTag(str);
|
---|
| 10 |
|
---|
| 11 | module.exports = (xml, config = {}) => {
|
---|
| 12 | let { indentor, textNodesOnSameLine } = config
|
---|
| 13 | let depth = 0
|
---|
| 14 | const indicesToRemove = []
|
---|
| 15 | indentor = indentor || ' '
|
---|
| 16 |
|
---|
| 17 | const rawResult = lexer(xml).map((element, i, arr) => {
|
---|
| 18 | const { value, type } = element
|
---|
| 19 | if (type === 'ClosingTag') {
|
---|
| 20 | depth--;
|
---|
| 21 | }
|
---|
| 22 |
|
---|
| 23 | let indentation = repeat(indentor, depth)
|
---|
| 24 | let line = indentation + value;
|
---|
| 25 |
|
---|
| 26 | if (type === 'OpeningTag') {
|
---|
| 27 | depth++;
|
---|
| 28 | }
|
---|
| 29 |
|
---|
| 30 | if(textNodesOnSameLine) {
|
---|
| 31 | // Lookbehind for [OpeningTag][Text][ClosingTag]
|
---|
| 32 | const oneBefore = arr[i-1]
|
---|
| 33 | const twoBefore = arr[i-2]
|
---|
| 34 |
|
---|
| 35 | if(type === "ClosingTag" && oneBefore.type === "Text" && twoBefore.type === "OpeningTag") {
|
---|
| 36 | // collapse into a single line
|
---|
| 37 | line = `${indentation}${twoBefore.value}${oneBefore.value}${value}`
|
---|
| 38 | indicesToRemove.push(i-2, i-1)
|
---|
| 39 | }
|
---|
| 40 | }
|
---|
| 41 |
|
---|
| 42 | return line;
|
---|
| 43 | })
|
---|
| 44 |
|
---|
| 45 | indicesToRemove.forEach(idx => rawResult[idx] = null)
|
---|
| 46 |
|
---|
| 47 | return rawResult
|
---|
| 48 | .filter(val => !!val)
|
---|
| 49 | .join('\n')
|
---|
| 50 | };
|
---|
| 51 |
|
---|
| 52 | function lexer(xmlStr) {
|
---|
| 53 | const values = splitOnTags(xmlStr)
|
---|
| 54 | return values.map(value => {
|
---|
| 55 | return {
|
---|
| 56 | value,
|
---|
| 57 | type: getType(value)
|
---|
| 58 | }
|
---|
| 59 | })
|
---|
| 60 | }
|
---|
| 61 |
|
---|
| 62 | // Helpers
|
---|
| 63 |
|
---|
| 64 | function getType(str) {
|
---|
| 65 | if(isClosingTag(str)) {
|
---|
| 66 | return 'ClosingTag'
|
---|
| 67 | }
|
---|
| 68 |
|
---|
| 69 | if(isOpeningTag(str)) {
|
---|
| 70 | return 'OpeningTag'
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | if(isSelfClosingTag(str)) {
|
---|
| 74 | return 'SelfClosingTag'
|
---|
| 75 | }
|
---|
| 76 |
|
---|
| 77 | return 'Text'
|
---|
| 78 | }
|
---|