From ee6a165425a6b47dff341fb651848ec5727d7f7e Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Tue, 15 May 2018 21:46:22 +0200 Subject: [PATCH] feat(html-tags): Add a helper to create html-tags --- index.js | 26 +++-------------- lib/html-tags.js | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 lib/html-tags.js diff --git a/index.js b/index.js index 2d79ae3a..4349d30f 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,8 @@ const path = require('path'); const SyncWaterfallHook = require('tapable').SyncWaterfallHook; const AsyncSeriesWaterfallHook = require('tapable').AsyncSeriesWaterfallHook; +const htmlTagObjectToString = require('./lib/html-tags').htmlTagObjectToString; + const childCompiler = require('./lib/compiler.js'); const prettyError = require('./lib/errors.js'); const chunkSorter = require('./lib/chunksorter.js'); @@ -549,8 +551,8 @@ class HtmlWebpackPlugin { const htmlRegExp = /(]*>)/i; const headRegExp = /(<\/head\s*>)/i; const bodyRegExp = /(<\/body\s*>)/i; - const body = assetTags.body.map(this.createHtmlTag.bind(this)); - const head = assetTags.head.map(this.createHtmlTag.bind(this)); + const body = assetTags.body.map((assetTagObject) => htmlTagObjectToString(assetTagObject, this.options.xhtml)); + const head = assetTags.head.map((assetTagObject) => htmlTagObjectToString(assetTagObject, this.options.xhtml)); if (body.length) { if (bodyRegExp.test(html)) { @@ -599,26 +601,6 @@ class HtmlWebpackPlugin { return url + (url.indexOf('?') === -1 ? '?' : '&') + hash; } - /** - * Turn a tag definition into a html string - */ - createHtmlTag (tagDefinition) { - const attributes = Object.keys(tagDefinition.attributes || {}) - .filter(attributeName => tagDefinition.attributes[attributeName] !== false) - .map(attributeName => { - if (tagDefinition.attributes[attributeName] === true) { - return attributeName; - } - return attributeName + '="' + tagDefinition.attributes[attributeName] + '"'; - }); - // Backport of 3.x void tag definition - const voidTag = tagDefinition.voidTag !== undefined ? tagDefinition.voidTag : !tagDefinition.closeTag; - const selfClosingTag = tagDefinition.voidTag !== undefined ? tagDefinition.voidTag && this.options.xhtml : tagDefinition.selfClosingTag; - return '<' + [tagDefinition.tagName].concat(attributes).join(' ') + (selfClosingTag ? '/' : '') + '>' + - (tagDefinition.innerHTML || '') + - (voidTag ? '' : ''); - } - /** * Helper to return the absolute template path with a fallback loader */ diff --git a/lib/html-tags.js b/lib/html-tags.js new file mode 100644 index 00000000..cdad0d4a --- /dev/null +++ b/lib/html-tags.js @@ -0,0 +1,73 @@ +// @ts-check +/* eslint-disable */ +/// +/* eslint-enable */ +/** + * @file + * This file provides to helper to create html as a object repesentation as + * thoses objects are easier to modify than pure string representations + * + * Usage: + * ``` + * const element = createHtmlTagObject('h1', {class: 'demo'}, 'Hello World'); + * const html = htmlTagObjectToString(element); + * console.log(html) // ->

Hello World

+ * ``` + */ + +/** + * All html tag elements which must not contain innerHTML + * @see https://www.w3.org/TR/html5/syntax.html#void-elements + */ +const voidTags = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']; + +/** + * Turn a tag definition into a html string + * @param {HtmlTagObject} tagDefinition + * A tag element according to the htmlWebpackPlugin object notation + * + * @param xhtml {boolean} + * Wether the generated html should add closing slashes to be xhtml compliant + */ +function htmlTagObjectToString (tagDefinition, xhtml) { + const attributes = Object.keys(tagDefinition.attributes || {}) + .filter(function (attributeName) { + return tagDefinition.attributes[attributeName] !== false; + }) + .map(function (attributeName) { + if (tagDefinition.attributes[attributeName] === true) { + return xhtml ? attributeName + '="' + attributeName + '"' : attributeName; + } + return attributeName + '="' + tagDefinition.attributes[attributeName] + '"'; + }); + return '<' + [tagDefinition.tagName].concat(attributes).join(' ') + (tagDefinition.voidTag && xhtml ? '/' : '') + '>' + + (tagDefinition.innerHTML || '') + + (tagDefinition.voidTag ? '' : ''); +} + +/** + * Static helper to create a tag object to be get injected into the dom + * + * @param {string} tagName + * the name of the tage e.g. 'div' + * + * @param {{[attributeName: string]: string|boolean}} [attributes] + * tag attributes e.g. `{ 'class': 'example', disabled: true }` + * + * @param {string} [innerHTML] + * + * @returns {HtmlTagObject} + */ +function createHtmlTagObject (tagName, attributes, innerHTML) { + return { + tagName: tagName, + voidTag: voidTags.indexOf(tagName) !== -1, + attributes: attributes || {}, + innerHTML: innerHTML + }; +} + +module.exports = { + createHtmlTagObject: createHtmlTagObject, + htmlTagObjectToString: htmlTagObjectToString +};