From fc60c7b6abf0bbe0ceb9de4cbfb47ecf7f461cf9 Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Mon, 27 Apr 2020 23:17:23 -0400 Subject: [PATCH 01/13] Prepare HTML content for self-closing tags `` -> `` the function ignores void elements like `` --- src/index.js | 10 ++++++++-- src/lib/html/index.js | 5 +++++ src/lib/html/prepare-html.js | 27 +++++++++++++++++++++++++++ src/lib/html/prepare-html.test.js | 9 +++++++++ src/lib/html/voidelements.json | 16 ++++++++++++++++ src/lib/index.js | 3 +++ 6 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 src/lib/html/index.js create mode 100644 src/lib/html/prepare-html.js create mode 100644 src/lib/html/prepare-html.test.js create mode 100644 src/lib/html/voidelements.json create mode 100644 src/lib/index.js diff --git a/src/index.js b/src/index.js index 0fa33ea..1053b8d 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,11 @@ const fs = require('fs'); const { performance } = require('perf_hooks'); const marked = require('marked'); +const { + html: { + prepareHTML, + }, +} = require('./lib'); require('dotenv').config(); /** @@ -210,7 +215,7 @@ const prepareImports = async folder => { }; const primeImport = (path, body) => { - cachedImports[path] = body; + cachedImports[path] = path.endsWith('.html') ? prepareHTML(body) : body; }; const getSlots = content => { @@ -313,7 +318,8 @@ const compileImport = (body, pattern) => { return body; }; -const compileTemplate = (body, slots = { default: '' }) => { +const compileTemplate = (body_, slots = { default: '' }) => { + let body = prepareHTML(body_); body = compileSlots(body, slots); if (!hasImports(body)) { diff --git a/src/lib/html/index.js b/src/lib/html/index.js new file mode 100644 index 0000000..e3843e6 --- /dev/null +++ b/src/lib/html/index.js @@ -0,0 +1,5 @@ +const prepareHTML = require('./prepare-html'); + +module.exports = { + prepareHTML +}; diff --git a/src/lib/html/prepare-html.js b/src/lib/html/prepare-html.js new file mode 100644 index 0000000..6148123 --- /dev/null +++ b/src/lib/html/prepare-html.js @@ -0,0 +1,27 @@ +const VOID_ELEMENTS = require('./voidelements.json'); + +// `` -> `` +// the function ignores void elements like `` +module.exports = (html_) => { + let html = html_ || ''; + if (!html.trim()) return html; + + (html.match(/<[^<|\/>]+\/>/g) || []) + .map((original) => { + const def = original.slice(1, -2).trim(); + const tagName = def.split(' ')[0].trim(); + + return { original, def, tagName }; + }) + .forEach(({ original, def, tagName }) => { + let newTagContent = def; + + newTagContent = VOID_ELEMENTS.includes(tagName) + ? `<${newTagContent.replace(/[\r|\n]/g, '')}>` + : `<${newTagContent}>`; + + html = html.replace(original, newTagContent); + }); + + return html; +}; diff --git a/src/lib/html/prepare-html.test.js b/src/lib/html/prepare-html.test.js new file mode 100644 index 0000000..ee4da28 --- /dev/null +++ b/src/lib/html/prepare-html.test.js @@ -0,0 +1,9 @@ +const { prepareHTML } = require('./index'); + +test('Prepare HTML for self-closing tags', () => { + const input = '
'; + const expected = '
'; + const output = prepareHTML(input); + + expect(output).toBe(expected); +}); diff --git a/src/lib/html/voidelements.json b/src/lib/html/voidelements.json new file mode 100644 index 0000000..48427f8 --- /dev/null +++ b/src/lib/html/voidelements.json @@ -0,0 +1,16 @@ +[ + "area", + "base", + "br", + "col", + "embed", + "hr", + "img", + "input", + "link", + "meta", + "param", + "source", + "track", + "wbr" +] diff --git a/src/lib/index.js b/src/lib/index.js new file mode 100644 index 0000000..f1dcfdb --- /dev/null +++ b/src/lib/index.js @@ -0,0 +1,3 @@ +const html = require('./html'); + +module.exports = { html }; From 557a7f6d104a1c944658da281c5bdc2f666c8b5f Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Tue, 28 Apr 2020 08:12:55 -0400 Subject: [PATCH 02/13] changeItemsByHTML lib ```js changeItemsByHTML({ html: '

', selector: 'p', changeItem: (node: Node) => { node.atttribs.class += "foo"; return (domutils).getOuterHTML(node); } }) ``` --- package.json | 3 ++ .../html/change-tag/change-items-by-html.js | 32 +++++++++++++++++++ .../change-tag/change-items-by-html.test.js | 28 ++++++++++++++++ src/lib/html/change-tag/index.js | 2 ++ src/lib/html/index.js | 2 ++ 5 files changed, 67 insertions(+) create mode 100644 src/lib/html/change-tag/change-items-by-html.js create mode 100644 src/lib/html/change-tag/change-items-by-html.test.js create mode 100644 src/lib/html/change-tag/index.js diff --git a/package.json b/package.json index e557083..5a6c0c0 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,10 @@ "dependencies": { "chokidar": "^2.1.5", "connect": "^3.6.6", + "css-select": "^2.1.0", + "domutils": "^2.0.0", "dotenv": "^7.0.0", + "htmlparser2": "^4.1.0", "marked": "^0.6.2", "serve-static": "^1.13.2" }, diff --git a/src/lib/html/change-tag/change-items-by-html.js b/src/lib/html/change-tag/change-items-by-html.js new file mode 100644 index 0000000..08b209d --- /dev/null +++ b/src/lib/html/change-tag/change-items-by-html.js @@ -0,0 +1,32 @@ +const { parseDOM } = require('htmlparser2'); +const { selectAll } = require('css-select'); +const domutils = require('domutils'); + +const defaultModes = { + innerHTML: domutils.getInnerHTML, + outerHTML: domutils.getOuterHTML, +}; + +/** + * Core function to change tags + * @author Gabriel Rodrigues + */ +module.exports = ({ + html, + selector, + changeItem, + mode = 'outerHTML', + modes = defaultModes, + base: base_, +}) => { + let base = html || base_ || ''; + + const nodes = parseDOM(base); + const selectedNodes = selectAll(selector, nodes); + selectedNodes.forEach((i) => { + const oldContent = modes[mode](i); + const newContent = changeItem(i, oldContent); + base = base.replace(oldContent, newContent); + }); + return base; +}; diff --git a/src/lib/html/change-tag/change-items-by-html.test.js b/src/lib/html/change-tag/change-items-by-html.test.js new file mode 100644 index 0000000..dac1c73 --- /dev/null +++ b/src/lib/html/change-tag/change-items-by-html.test.js @@ -0,0 +1,28 @@ +const { getInnerHTML, getOuterHTML } = require('domutils'); +const { changeItemsByHTML } = require('./index'); + +test('Change HTML tags', () => { + const input = + '

KEEP INNER ONLY

attrb

'; + const expected = + '
KEEP INNER ONLY

attrb

'; + + let output = input; + output = changeItemsByHTML({ + html: output, + selector: '.inner-only', + changeItem: (node) => { + return getInnerHTML(node); + }, + }); + output = changeItemsByHTML({ + html: output, + selector: '.attrb', + changeItem: (node) => { + node.attribs['data-foo'] = 'baz'; + return getOuterHTML(node); + }, + }); + + expect(output).toBe(expected); +}); diff --git a/src/lib/html/change-tag/index.js b/src/lib/html/change-tag/index.js new file mode 100644 index 0000000..455fc68 --- /dev/null +++ b/src/lib/html/change-tag/index.js @@ -0,0 +1,2 @@ +const changeItemsByHTML = require('./change-items-by-html'); +module.exports = { changeItemsByHTML }; diff --git a/src/lib/html/index.js b/src/lib/html/index.js index e3843e6..b232263 100644 --- a/src/lib/html/index.js +++ b/src/lib/html/index.js @@ -1,5 +1,7 @@ +const changeTag = require('./change-tag'); const prepareHTML = require('./prepare-html'); module.exports = { + changeTag, prepareHTML }; From aa8ac8eac8f81ab6abb25542c98b4406e5e124af Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Tue, 28 Apr 2020 08:18:43 -0400 Subject: [PATCH 03/13] changeItemsByHTMLFallback lib Extends `changeItemsByHTML`. Can match tags inside tags (e.g. ``) ```js changeItemsByHTMLFallback({ html: '

', selector: 'p', changeItem: (node: Node) => { node.atttribs.class += "foo"; return (domutils).getOuterHTML(node); } }) ``` --- .../change-items-by-html-fallback.js | 29 +++++++++++ .../change-items-by-html-fallback.test.js | 48 +++++++++++++++++++ src/lib/html/change-tag/index.js | 3 ++ 3 files changed, 80 insertions(+) create mode 100644 src/lib/html/change-tag/change-items-by-html-fallback.js create mode 100644 src/lib/html/change-tag/change-items-by-html-fallback.test.js diff --git a/src/lib/html/change-tag/change-items-by-html-fallback.js b/src/lib/html/change-tag/change-items-by-html-fallback.js new file mode 100644 index 0000000..fa19bf8 --- /dev/null +++ b/src/lib/html/change-tag/change-items-by-html-fallback.js @@ -0,0 +1,29 @@ +const changeItemsByHTML = require('./change-items-by-html'); + +/** + * bug-fix for tags inside tags (e.g. ) + * Extends `changeItemsByHTML`. + * Can match tags inside tags + * @author Gabriel Rodrigues + */ +module.exports = (options) => { + let { html, selector } = options; + + if (html.includes(``)) { + const regexpRangeTag = selector.replace('-', '\\-'); + const remaingTags = + html.match( + new RegExp( + `<${selector}[^<]*>[^<${regexpRangeTag}]*<\\/${selector}>`, + 'g' + ) + ) || []; + + const foundAs = remaingTags.join(''); + html = html.replace( + foundAs, + changeItemsByHTML(Object.assign({}, options, { html: foundAs })) + ); + } + return html; +}; diff --git a/src/lib/html/change-tag/change-items-by-html-fallback.test.js b/src/lib/html/change-tag/change-items-by-html-fallback.test.js new file mode 100644 index 0000000..c3bc046 --- /dev/null +++ b/src/lib/html/change-tag/change-items-by-html-fallback.test.js @@ -0,0 +1,48 @@ +const prepareHTML = require('../prepare-html'); +const { changeItemsByHTML, changeItemsByHTMLFallback } = require('./index'); +const getOptions = (option, html_) => + Object.assign({}, option, { html: html_ }); + +const options = [ + { + selector: 'get-title', + changeItem: () => { + return '...'; + }, + }, + { + selector: 'get-text', + changeItem: () => { + return 'text'; + }, + }, +]; + +const input = prepareHTML( + '

' +); + +test('Shoud fail to match tag with changeItemsByHTML', () => { + const expected = prepareHTML('

text

'); + + let output = input; + output = changeItemsByHTML(getOptions(options[0], output)); + output = changeItemsByHTML(getOptions(options[1], output)); + + expect(output).toBe(expected); +}); + +test('Change HTML tags with changeItemsByHTMLFallback', () => { + const expected = prepareHTML('

text

'); + + let output = input; + + // get-title + output = changeItemsByHTML(getOptions(options[0], output)); + output = changeItemsByHTMLFallback(getOptions(options[0], output)); + // get-text + output = changeItemsByHTML(getOptions(options[1], output)); + output = changeItemsByHTMLFallback(getOptions(options[1], output)); + + expect(output).toBe(expected); +}); diff --git a/src/lib/html/change-tag/index.js b/src/lib/html/change-tag/index.js index 455fc68..c1ffb54 100644 --- a/src/lib/html/change-tag/index.js +++ b/src/lib/html/change-tag/index.js @@ -1,2 +1,5 @@ const changeItemsByHTML = require('./change-items-by-html'); +const changeItemsByHTMLFallback = require('./change-items-by-html-fallback'); module.exports = { changeItemsByHTML }; + +module.exports = { changeItemsByHTML, changeItemsByHTMLFallback }; From dd44a60cdfa177cdbd6e3c75630de728ed885d98 Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Tue, 28 Apr 2020 08:24:22 -0400 Subject: [PATCH 04/13] changeTag lib Exports `main`, `changeItemsByHTML` and `changeItemsByHTMLFallback` The `main` function recives `options` used in `changeItemsByHTML+` and a callback function to change the selected node. Returns string --- src/lib/html/change-tag/index.js | 19 +++++++++++++++++-- src/lib/html/change-tag/index.test.js | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 src/lib/html/change-tag/index.test.js diff --git a/src/lib/html/change-tag/index.js b/src/lib/html/change-tag/index.js index c1ffb54..f54f175 100644 --- a/src/lib/html/change-tag/index.js +++ b/src/lib/html/change-tag/index.js @@ -1,5 +1,20 @@ const changeItemsByHTML = require('./change-items-by-html'); const changeItemsByHTMLFallback = require('./change-items-by-html-fallback'); -module.exports = { changeItemsByHTML }; -module.exports = { changeItemsByHTML, changeItemsByHTMLFallback }; +/** + * @author Gabriel Rodrigues + */ +function main(options, fn) { + const { html: html_, selector, mode = 'outerHTML' } = options; + let html = html_; + + const changeItem = (i) => fn(i); + html = changeItemsByHTML(Object.assign({}, options, { html, changeItem })); + html = changeItemsByHTMLFallback( + Object.assign({}, options, { html, changeItem }) + ); + + return html; +} + +module.exports = { main, changeItemsByHTML, changeItemsByHTMLFallback }; diff --git a/src/lib/html/change-tag/index.test.js b/src/lib/html/change-tag/index.test.js new file mode 100644 index 0000000..eac07f9 --- /dev/null +++ b/src/lib/html/change-tag/index.test.js @@ -0,0 +1,20 @@ +const { getInnerHTML, getOuterHTML } = require('domutils'); +const changeTag = require('./index'); + +test('Change HTML tags', () => { + const input = + '

KEEP INNER ONLY

attrb

'; + const expected = + '
KEEP INNER ONLY

attrb

'; + + let output = input; + output = changeTag.main({ html: output, selector: '.inner-only' }, (node) => { + return getInnerHTML(node); + }); + output = changeTag.main({ html: output, selector: '.attrb' }, (node) => { + node.attribs['data-foo'] = 'baz'; + return getOuterHTML(node); + }); + + expect(output).toBe(expected); +}); From 0f2214cbd95b3b284022acc6b63b6d3d4f0840e0 Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Tue, 28 Apr 2020 08:42:32 -0400 Subject: [PATCH 05/13] Use changeTag in compileLinks --- src/index.js | 51 ++++++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/src/index.js b/src/index.js index 1053b8d..25bed8c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,13 @@ #!/usr/bin/env node const fs = require('fs'); const { performance } = require('perf_hooks'); +const { parseDOM } = require('htmlparser2'); +const { selectAll } = require('css-select'); +const domutils = require('domutils'); const marked = require('marked'); const { html: { + changeTag, prepareHTML, }, } = require('./lib'); @@ -64,7 +68,7 @@ const patterns = { simpleDefaultSlots: //gm, complexImports: /(.*?)<\/sergey-import>/gms, simpleImports: //gm, - links: /(.*?)<\/sergey-link>/gms + link: 'sergey-link', }; /** @@ -194,7 +198,6 @@ const getKey = (key, ext = '.html', folder = '') => { return `${folder}${file}`; }; const hasImports = x => x.includes(' x.includes(' { if (!excludedFolders.includes(name)) { excludedFolders.push(name); @@ -332,44 +335,30 @@ const compileTemplate = (body_, slots = { default: '' }) => { return body; }; -const compileLinks = (body, path) => { - let m; - let copy; - - if (!hasLinks(body)) { - return body; - } +const compileLinks = (body_, path) => { + let body = body_; + body = changeTag.main({ html: body, selector: patterns.link }, (node) => { + const arrTo = ['to', 'href'].filter((i) => domutils.hasAttrib(node, i))[0]; - copy = body; - while ((m = patterns.links.exec(body)) !== null) { - if (m.index === patterns.links.lastIndex) { - patterns.links.lastIndex++; - } - - let [find, attr1 = '', to, attr2 = '', content] = m; - let replace = ''; - let attributes = [`href="${to}"`, attr1, attr2] - .map(x => x.trim()) - .filter(Boolean) - .join(' '); + const to = arrTo ? domutils.getAttributeValue(node, arrTo) : ''; + arrTo && delete node.attribs[arrTo]; const isCurrent = isCurrentPage(to, path); if (isCurrent || isParentPage(to, path)) { - if (attributes.includes('class="')) { - attributes = attributes.replace('class="', `class="${ACTIVE_CLASS} `); - } else { - attributes += ` class="${ACTIVE_CLASS}"`; - } + const currClass = domutils.getAttributeValue(node, 'class') || ''; + node.attribs['class'] = `${ACTIVE_CLASS} ${currClass.trimLeft()}`.trim(); if (isCurrent) { - attributes += ' aria-current="page"'; + node.attribs['aria-current'] = 'page'; } } - replace = `${content}`; - copy = copy.replace(find, replace); - } - body = copy; + return domutils + .getOuterHTML(node) + .replace(/^', '') + .replace(/^ Date: Tue, 28 Apr 2020 08:44:58 -0400 Subject: [PATCH 06/13] Use changeTag in compileSlots --- src/index.js | 51 ++++++++------------------------------------------- 1 file changed, 8 insertions(+), 43 deletions(-) diff --git a/src/index.js b/src/index.js index 25bed8c..e6e0600 100644 --- a/src/index.js +++ b/src/index.js @@ -62,12 +62,9 @@ const excludedFolders = [ const patterns = { whitespace: /^\s+|\s+$/g, templates: /(.*?)<\/sergey-template>/gms, - complexNamedSlots: /(.*?)<\/sergey-slot>/gms, - simpleNamedSlots: //gm, - complexDefaultSlots: /(.*?)<\/sergey-slot>/gms, - simpleDefaultSlots: //gm, complexImports: /(.*?)<\/sergey-import>/gms, simpleImports: //gm, + slot: 'sergey-slot', link: 'sergey-link', }; @@ -248,46 +245,14 @@ const getSlots = content => { return slots; }; -const compileSlots = (body, slots) => { - let m; - let copy; - - // Complex named slots - copy = body; - while ((m = patterns.complexNamedSlots.exec(body)) !== null) { - if (m.index === patterns.complexNamedSlots.lastIndex) { - patterns.complexNamedSlots.lastIndex++; - } - - const [find, name, fallback] = m; - copy = copy.replace(find, slots[name] || fallback || ''); - } - body = copy; - - // Simple named slots - while ((m = patterns.simpleNamedSlots.exec(body)) !== null) { - if (m.index === patterns.simpleNamedSlots.lastIndex) { - patterns.simpleNamedSlots.lastIndex++; - } - - const [find, name] = m; - copy = copy.replace(find, slots[name] || ''); - } - body = copy; - - // Complex Default slots - while ((m = patterns.complexDefaultSlots.exec(body)) !== null) { - if (m.index === patterns.complexDefaultSlots.lastIndex) { - patterns.complexDefaultSlots.lastIndex++; - } - - const [find, fallback] = m; - copy = copy.replace(find, slots.default || fallback || ''); - } - body = copy; +const compileSlots = (body_, slots) => { + let body = body_; - // Simple default slots - body = body.replace(patterns.simpleDefaultSlots, slots.default); + body = changeTag.main({ html: body, selector: patterns.slot }, (node) => { + const name = domutils.getAttributeValue(node, 'name') || 'default'; + const fallback = domutils.getInnerHTML(node); + return slots[name] || fallback || ''; + }); return body; }; From eb8fad81f2ea34194b6589e0799a41ee5ac4795b Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Tue, 28 Apr 2020 08:47:53 -0400 Subject: [PATCH 07/13] Use changeTag in compileImport --- src/index.js | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/index.js b/src/index.js index e6e0600..c254d86 100644 --- a/src/index.js +++ b/src/index.js @@ -62,9 +62,8 @@ const excludedFolders = [ const patterns = { whitespace: /^\s+|\s+$/g, templates: /(.*?)<\/sergey-template>/gms, - complexImports: /(.*?)<\/sergey-import>/gms, - simpleImports: //gm, slot: 'sergey-slot', + import: 'sergey-import', link: 'sergey-link', }; @@ -194,7 +193,6 @@ const getKey = (key, ext = '.html', folder = '') => { const file = key.endsWith(ext) ? key : `${key}${ext}`; return `${folder}${file}`; }; -const hasImports = x => x.includes(' { if (!excludedFolders.includes(name)) { excludedFolders.push(name); @@ -257,17 +255,14 @@ const compileSlots = (body_, slots) => { return body; }; -const compileImport = (body, pattern) => { - let m; - // Simple imports - while ((m = pattern.exec(body)) !== null) { - if (m.index === pattern.lastIndex) { - pattern.lastIndex++; - } +const compileImport = (body_) => { + let body = body_; + body = changeTag.main({ html: body, selector: patterns.import }, (node) => { + let key = domutils.getAttributeValue(node, 'src'); + let htmlAs = domutils.getAttributeValue(node, 'as') || ''; + let content = domutils.getInnerHTML(node) || ''; - let [find, key, htmlAs = '', content = ''] = m; let replace = ''; - if (htmlAs === 'markdown') { replace = formatContent( marked(cachedImports[getKey(key, '.md', CONTENT)] || '') @@ -280,8 +275,8 @@ const compileImport = (body, pattern) => { // Recurse replace = compileTemplate(replace, slots); - body = body.replace(find, replace); - } + return replace; + }); return body; }; @@ -289,14 +284,7 @@ const compileImport = (body, pattern) => { const compileTemplate = (body_, slots = { default: '' }) => { let body = prepareHTML(body_); body = compileSlots(body, slots); - - if (!hasImports(body)) { - return body; - } - - body = compileImport(body, patterns.simpleImports); - body = compileImport(body, patterns.complexImports); - + body = compileImport(body); return body; }; From 352d3e0fe328317b2f275b180ee1c4679ab9c9c1 Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Tue, 28 Apr 2020 08:50:04 -0400 Subject: [PATCH 08/13] Use node based template select in getSlots --- src/index.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/index.js b/src/index.js index c254d86..f8bbf2e 100644 --- a/src/index.js +++ b/src/index.js @@ -61,7 +61,7 @@ const excludedFolders = [ const patterns = { whitespace: /^\s+|\s+$/g, - templates: /(.*?)<\/sergey-template>/gms, + template: 'sergey-template', slot: 'sergey-slot', import: 'sergey-import', link: 'sergey-link', @@ -216,19 +216,20 @@ const primeImport = (path, body) => { cachedImports[path] = path.endsWith('.html') ? prepareHTML(body) : body; }; -const getSlots = content => { +const getSlots = (content) => { // Extract templates first const slots = { - default: formatContent(content) || '' + default: formatContent(content) || '', }; // Search content for templates - while ((m = patterns.templates.exec(content)) !== null) { - if (m.index === patterns.templates.lastIndex) { - patterns.templates.lastIndex++; - } + const nodes = parseDOM(content); + const items = selectAll(patterns.template, nodes); + items.forEach((node) => { + const find = domutils.getOuterHTML(node); + const name = domutils.getAttributeValue(node, 'name'); + const data = domutils.getInnerHTML(node); - const [find, name, data] = m; if (name !== 'default') { // Remove it from the default content slots.default = slots.default.replace(find, ''); @@ -236,7 +237,7 @@ const getSlots = content => { // Add it as a named slot slots[name] = formatContent(data); - } + }); slots.default = formatContent(slots.default); From bb3f4aac27915f25e4d2705dc86b59f914eadf28 Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Wed, 29 Apr 2020 09:03:51 -0400 Subject: [PATCH 09/13] Update html if not retuns false in changeItem --- src/lib/html/change-tag/change-items-by-html.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/html/change-tag/change-items-by-html.js b/src/lib/html/change-tag/change-items-by-html.js index 08b209d..c2830eb 100644 --- a/src/lib/html/change-tag/change-items-by-html.js +++ b/src/lib/html/change-tag/change-items-by-html.js @@ -26,7 +26,10 @@ module.exports = ({ selectedNodes.forEach((i) => { const oldContent = modes[mode](i); const newContent = changeItem(i, oldContent); + + if(newContent !== false) { base = base.replace(oldContent, newContent); + } }); return base; }; From c52274a1bd42dca9ebe7259035dee3fbc7abdb6f Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Wed, 29 Apr 2020 09:10:40 -0400 Subject: [PATCH 10/13] Fix twice changeItem execution --- package.json | 3 ++- src/lib/html/change-tag/index.js | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5a6c0c0..3ab9406 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "dotenv": "^7.0.0", "htmlparser2": "^4.1.0", "marked": "^0.6.2", - "serve-static": "^1.13.2" + "serve-static": "^1.13.2", + "uid": "^1.0.0" }, "devDependencies": { "jest": "^24.7.1" diff --git a/src/lib/html/change-tag/index.js b/src/lib/html/change-tag/index.js index f54f175..43d4a00 100644 --- a/src/lib/html/change-tag/index.js +++ b/src/lib/html/change-tag/index.js @@ -1,6 +1,13 @@ +const uid = require('uid'); +const domutils = require('domutils'); const changeItemsByHTML = require('./change-items-by-html'); const changeItemsByHTMLFallback = require('./change-items-by-html-fallback'); +const returnModes = { + outerHTML: domutils.getOuterHTML, + innerHTML: domutils.getInnerHTML, +}; + /** * @author Gabriel Rodrigues */ @@ -8,12 +15,21 @@ function main(options, fn) { const { html: html_, selector, mode = 'outerHTML' } = options; let html = html_; - const changeItem = (i) => fn(i); + const changeItemNodeId = `sergey-node_id-${uid(6)}`; + const changeItem = (node, ...args) => { + if (typeof node.attribs[changeItemNodeId] === 'undefined') { + node.attribs[changeItemNodeId] = ''; + return fn(node, ...args); + } + return false; + }; + html = changeItemsByHTML(Object.assign({}, options, { html, changeItem })); html = changeItemsByHTMLFallback( Object.assign({}, options, { html, changeItem }) ); + html = html.replace(new RegExp(` ?${changeItemNodeId}`, 'g'), ''); return html; } From 84f3921e5c15bfbeadbacb1bd5e4a00915b7761f Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Wed, 29 Apr 2020 15:50:26 -0400 Subject: [PATCH 11/13] Preparse xml to avoid code multiline definition --- src/lib/html/prepare-html.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/html/prepare-html.js b/src/lib/html/prepare-html.js index 6148123..aee2d9f 100644 --- a/src/lib/html/prepare-html.js +++ b/src/lib/html/prepare-html.js @@ -1,3 +1,5 @@ +const { parseDOM } = require('htmlparser2'); +const { getOuterHTML } = require('domutils'); const VOID_ELEMENTS = require('./voidelements.json'); // `` -> `` @@ -23,5 +25,6 @@ module.exports = (html_) => { html = html.replace(original, newTagContent); }); + html = getOuterHTML(parseDOM(html)); return html; }; From 90178b7577a89088e074b060424d3d4ac05e358e Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Wed, 29 Apr 2020 16:32:18 -0400 Subject: [PATCH 12/13] nodes lib --- src/lib/html/index.js | 4 +++- src/lib/html/nodes/get-nodes.js | 5 +++++ src/lib/html/nodes/get-nodes.test.js | 18 ++++++++++++++++++ src/lib/html/nodes/index.js | 9 +++++++++ src/lib/html/nodes/query-nodes-by-html.js | 14 ++++++++++++++ src/lib/html/nodes/query-nodes-by-html.test.js | 9 +++++++++ src/lib/html/nodes/query-nodes.js | 5 +++++ src/lib/html/nodes/query-nodes.test.js | 11 +++++++++++ 8 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/lib/html/nodes/get-nodes.js create mode 100644 src/lib/html/nodes/get-nodes.test.js create mode 100644 src/lib/html/nodes/index.js create mode 100644 src/lib/html/nodes/query-nodes-by-html.js create mode 100644 src/lib/html/nodes/query-nodes-by-html.test.js create mode 100644 src/lib/html/nodes/query-nodes.js create mode 100644 src/lib/html/nodes/query-nodes.test.js diff --git a/src/lib/html/index.js b/src/lib/html/index.js index b232263..8725b93 100644 --- a/src/lib/html/index.js +++ b/src/lib/html/index.js @@ -1,7 +1,9 @@ +const nodes = require('./nodes'); const changeTag = require('./change-tag'); const prepareHTML = require('./prepare-html'); module.exports = { + nodes, changeTag, - prepareHTML + prepareHTML, }; diff --git a/src/lib/html/nodes/get-nodes.js b/src/lib/html/nodes/get-nodes.js new file mode 100644 index 0000000..d2dbff5 --- /dev/null +++ b/src/lib/html/nodes/get-nodes.js @@ -0,0 +1,5 @@ +const { parseDOM } = require('htmlparser2'); + +const getNodes = ({ html }) => parseDOM(html); + +module.exports = getNodes; diff --git a/src/lib/html/nodes/get-nodes.test.js b/src/lib/html/nodes/get-nodes.test.js new file mode 100644 index 0000000..dfded2e --- /dev/null +++ b/src/lib/html/nodes/get-nodes.test.js @@ -0,0 +1,18 @@ +const { getNodes } = require('./index'); +const { getOuterHTML } = require('domutils'); + +test('Return HTML (XML) nodes', () => { + const input = '
first

second

'; + const output = getNodes({ html: input }).length; + const expected = 2; + + expect(output).toBe(expected); +}); + +test('Return HTML (XML) nodes', () => { + const input = '
first

second

'; + const output = getOuterHTML(getNodes({ html: input })); + const expected = input; + + expect(output).toBe(expected); +}); diff --git a/src/lib/html/nodes/index.js b/src/lib/html/nodes/index.js new file mode 100644 index 0000000..9ef2ce6 --- /dev/null +++ b/src/lib/html/nodes/index.js @@ -0,0 +1,9 @@ +const getNodes = require('./get-nodes'); +const queryNodes = require('./query-nodes'); +const queryNodesByHTML = require('./query-nodes-by-html'); + +module.exports = { + getNodes, + queryNodes, + queryNodesByHTML, +}; diff --git a/src/lib/html/nodes/query-nodes-by-html.js b/src/lib/html/nodes/query-nodes-by-html.js new file mode 100644 index 0000000..12679d0 --- /dev/null +++ b/src/lib/html/nodes/query-nodes-by-html.js @@ -0,0 +1,14 @@ +const getNodes = require('./get-nodes'); +const queryNodes = require('./query-nodes'); + +const queryNodesByHTML = ({ html, selector }) => { + const rootNodes = getNodes({ html }); + const nodes = queryNodes({ nodes: rootNodes, selector }); + + return { + rootNodes, + nodes, + }; +}; + +module.exports = queryNodesByHTML; diff --git a/src/lib/html/nodes/query-nodes-by-html.test.js b/src/lib/html/nodes/query-nodes-by-html.test.js new file mode 100644 index 0000000..02d7385 --- /dev/null +++ b/src/lib/html/nodes/query-nodes-by-html.test.js @@ -0,0 +1,9 @@ +const queryNodesByHTML = require('./query-nodes-by-html'); + +test('Get nodes from select query by using HTML', () => { + const input = '
first
second
first
third
'; + const output = queryNodesByHTML({ html: input, selector: 'div' }).nodes.length; + const expected = 3; + + expect(output).toBe(expected); +}); diff --git a/src/lib/html/nodes/query-nodes.js b/src/lib/html/nodes/query-nodes.js new file mode 100644 index 0000000..f36c27e --- /dev/null +++ b/src/lib/html/nodes/query-nodes.js @@ -0,0 +1,5 @@ +const { selectAll } = require('css-select'); + +const queryNodes = ({ nodes, selector }) => selectAll(selector, nodes); + +module.exports = queryNodes; diff --git a/src/lib/html/nodes/query-nodes.test.js b/src/lib/html/nodes/query-nodes.test.js new file mode 100644 index 0000000..2907a2c --- /dev/null +++ b/src/lib/html/nodes/query-nodes.test.js @@ -0,0 +1,11 @@ +const queryNodes = require('./query-nodes'); +const getNodes = require('./get-nodes'); + +test('Get nodes from select query', () => { + const input = '
first
second
first
third
'; + const nodes = getNodes({ html: input }); + const output = queryNodes({ nodes, selector: 'div' }).length; + const expected = 3; + + expect(output).toBe(expected); +}); From 6cd7096c3818b31ff85ff1fc4ba5d9fcf8fdb28f Mon Sep 17 00:00:00 2001 From: Gabriel Rodrigues Date: Wed, 29 Apr 2020 16:34:41 -0400 Subject: [PATCH 13/13] Use `nodes` lib --- src/index.js | 11 ++++++----- src/lib/html/change-tag/change-items-by-html.js | 8 +++----- src/lib/html/prepare-html.js | 4 ++-- src/lib/html/prepare-html.test.js | 13 +++++++++++++ 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/index.js b/src/index.js index f8bbf2e..6d3ed1b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,11 @@ #!/usr/bin/env node const fs = require('fs'); const { performance } = require('perf_hooks'); -const { parseDOM } = require('htmlparser2'); -const { selectAll } = require('css-select'); const domutils = require('domutils'); const marked = require('marked'); const { html: { + nodes: { queryNodesByHTML }, changeTag, prepareHTML, }, @@ -223,9 +222,11 @@ const getSlots = (content) => { }; // Search content for templates - const nodes = parseDOM(content); - const items = selectAll(patterns.template, nodes); - items.forEach((node) => { + const { nodes } = queryNodesByHTML({ + html: content, + selector: patterns.template, + }); + nodes.forEach((node) => { const find = domutils.getOuterHTML(node); const name = domutils.getAttributeValue(node, 'name'); const data = domutils.getInnerHTML(node); diff --git a/src/lib/html/change-tag/change-items-by-html.js b/src/lib/html/change-tag/change-items-by-html.js index c2830eb..9802638 100644 --- a/src/lib/html/change-tag/change-items-by-html.js +++ b/src/lib/html/change-tag/change-items-by-html.js @@ -1,6 +1,5 @@ -const { parseDOM } = require('htmlparser2'); -const { selectAll } = require('css-select'); const domutils = require('domutils'); +const { queryNodesByHTML } = require('../nodes'); const defaultModes = { innerHTML: domutils.getInnerHTML, @@ -21,14 +20,13 @@ module.exports = ({ }) => { let base = html || base_ || ''; - const nodes = parseDOM(base); - const selectedNodes = selectAll(selector, nodes); + const { nodes: selectedNodes } = queryNodesByHTML({ html: base, selector }); selectedNodes.forEach((i) => { const oldContent = modes[mode](i); const newContent = changeItem(i, oldContent); if(newContent !== false) { - base = base.replace(oldContent, newContent); + base = base.replace(oldContent, newContent); } }); return base; diff --git a/src/lib/html/prepare-html.js b/src/lib/html/prepare-html.js index aee2d9f..781e8e9 100644 --- a/src/lib/html/prepare-html.js +++ b/src/lib/html/prepare-html.js @@ -1,4 +1,4 @@ -const { parseDOM } = require('htmlparser2'); +const { getNodes } = require('./nodes'); const { getOuterHTML } = require('domutils'); const VOID_ELEMENTS = require('./voidelements.json'); @@ -25,6 +25,6 @@ module.exports = (html_) => { html = html.replace(original, newTagContent); }); - html = getOuterHTML(parseDOM(html)); + html = getOuterHTML(getNodes({ html })); return html; }; diff --git a/src/lib/html/prepare-html.test.js b/src/lib/html/prepare-html.test.js index ee4da28..6f859f1 100644 --- a/src/lib/html/prepare-html.test.js +++ b/src/lib/html/prepare-html.test.js @@ -7,3 +7,16 @@ test('Prepare HTML for self-closing tags', () => { expect(output).toBe(expected); }); + + +test('Prepare HTML for multiline tags', () => { + const input = `
...
`; + + const expected = '
...
'; + const output = prepareHTML(input); + + expect(output).toBe(expected); +}); +