Skip to content

Commit 3492483

Browse files
committed
Add support for keeping tags unquoted.
This reduces the size of the HTML and improves readability. The current default behaviour is kept the same but an option is provided to enable the new behaviour. On a future version bump I would flip the default value.
1 parent 15d30db commit 3492483

File tree

3 files changed

+86
-8
lines changed

3 files changed

+86
-8
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ const html = render(tree, options)
8787
|:--:|:--:|:-----:|:----------|
8888
|**[`singleTags`](#singletags)**|`{Array<String\|RegExp>}`|`[]`|Specify custom single tags (self closing)|
8989
|**[`closingSingleTag`](#closingSingleTag)**|`{String}`|[`>`](#default)|Specify the single tag closing format|
90+
|**[`quoteAllAttributes`](#quoteAllAttributes)**|`{Boolean}`|[`true`](#default)|Put double quotes around all tags, even when not necessary.|
9091

9192
### `singleTags`
9293

@@ -166,6 +167,21 @@ const html = render(tree)
166167
<img></img>
167168
```
168169

170+
### `quoteAllAttributes`
171+
172+
Specify if all attributes should be quoted.
173+
174+
##### `true (Default)`
175+
176+
```html
177+
<script src="index.js"></script>
178+
```
179+
180+
##### `false`
181+
182+
```html
183+
<script src=index.js></script>
184+
```
169185

170186
[npm]: https://img.shields.io/npm/v/posthtml-render.svg
171187
[npm-url]: https://npmjs.com/package/posthtml-render

lib/index.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,23 @@ var SINGLE_TAGS = [
2222
'extend'
2323
]
2424

25-
/**
26-
* Render PostHTML Tree to HTML
25+
var ATTRIBUTE_QUOTES_REQUIRED = /[\t\n\f\r " '`=<>]/
26+
27+
/** Render PostHTML Tree to HTML
2728
*
28-
* @param {Array|Object} tree PostHTML Tree
29-
* @param {Object} options Options
29+
* @param {Array|Object} tree PostHTML Tree @param {Object} options Options
3030
*
3131
* @return {String} HTML
3232
*/
3333
function render (tree, options) {
34-
/**
35-
* Options
34+
/** Options
3635
*
3736
* @type {Object}
3837
*
3938
* @prop {Array<String|RegExp>} singleTags Custom single tags (selfClosing)
40-
* @prop {String} closingSingleTag Closing format for single tag
39+
* @prop {String} closingSingleTag Closing format for single tag @prop
40+
* @prop {Boolean} quoteAllAttributes If all attributes should be quoted.
41+
* Otherwise attributes will be unquoted when allowed.
4142
*
4243
* Formats:
4344
*
@@ -51,6 +52,10 @@ function render (tree, options) {
5152
})
5253

5354
var closingSingleTag = options.closingSingleTag
55+
var quoteAllAttributes = options.quoteAllAttributes
56+
if (typeof quoteAllAttributes === 'undefined') {
57+
quoteAllAttributes = true
58+
}
5459

5560
return html(tree)
5661

@@ -75,7 +80,11 @@ function render (tree, options) {
7580

7681
for (var key in obj) {
7782
if (typeof obj[key] === 'string') {
78-
attr += ' ' + key + '="' + obj[key].replace(/"/g, '&quot;') + '"'
83+
if (quoteAllAttributes || obj[key].match(ATTRIBUTE_QUOTES_REQUIRED)) {
84+
attr += ' ' + key + '="' + obj[key].replace(/"/g, '&quot;') + '"'
85+
} else {
86+
attr += ' ' + key + '=' + obj[key]
87+
}
7988
} else if (obj[key] === true) {
8089
attr += ' ' + key
8190
} else if (typeof obj[key] === 'number') {

test/render.test.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,5 +289,58 @@ describe('PostHTML Render', function () {
289289
expect(render(fixture, options)).to.eql(expected)
290290
})
291291
})
292+
293+
describe('quoteAllAttributes', function () {
294+
it('True', function () {
295+
var options = { quoteAllAttributes: true }
296+
297+
var fixture = { tag: 'a', attrs: { href: '/about/me/' } }
298+
var expected = '<a href="/about/me/"></a>'
299+
300+
expect(render(fixture, options)).to.eql(expected)
301+
})
302+
303+
it('False', function () {
304+
var options = { quoteAllAttributes: false }
305+
306+
var fixture = { tag: 'a', attrs: { href: '/about/me/' } }
307+
var expected = '<a href=/about/me/></a>'
308+
309+
expect(render(fixture, options)).to.eql(expected)
310+
})
311+
312+
it('Required Space', function () {
313+
var options = { quoteAllAttributes: false }
314+
315+
var fixture = { 'tag': 'p', 'attrs': { 'id': 'asd adsasd' } }
316+
var expected = '<p id="asd adsasd"></p>'
317+
318+
expect(render(fixture, options)).to.eql(expected)
319+
})
320+
321+
it('Required Tab', function () {
322+
var options = { quoteAllAttributes: false }
323+
324+
var fixture = { tag: 'a', attrs: { href: '/about-\t-characters' } }
325+
var expected = '<a href="/about-\t-characters"></a>'
326+
327+
expect(render(fixture, options)).to.eql(expected)
328+
})
329+
330+
it('Closing slash', function () {
331+
var options = {
332+
closingSingleTag: 'slash',
333+
quoteAllAttributes: false
334+
}
335+
336+
// Note that <area href=foobar/> is incorrect as that is parsed as
337+
// <area href="foobar/">.
338+
339+
var fixture = { tag: 'area', attrs: { href: 'foobar' } }
340+
var expected = '<area href=foobar />'
341+
342+
expect(render(fixture, options)).to.eql(expected)
343+
})
344+
})
292345
})
293346
})

0 commit comments

Comments
 (0)