Skip to content

Commit 1c938b9

Browse files
committed
Fix head opening tag omission w/o title
While typically a `title` is needed, there are cases where `<head>` can be empty, in which case, the start tag can be omitted. Closes GH-43.
1 parent a556b7d commit 1c938b9

File tree

3 files changed

+22
-17
lines changed

3 files changed

+22
-17
lines changed

lib/omission/opening.js

+12-10
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,25 @@ function html(node) {
3737
* Whether the opening tag can be omitted.
3838
*/
3939
function head(node) {
40-
const children = node.children
41-
/** @type {Array<string>} */
42-
const seen = []
43-
let index = -1
40+
/** @type {Set<string>} */
41+
const seen = new Set()
4442

45-
while (++index < children.length) {
46-
const child = children[index]
43+
// Whether `srcdoc` or not,
44+
// make sure the content model at least doesn’t have too many `base`s/`title`s.
45+
for (const child of node.children) {
4746
if (
4847
child.type === 'element' &&
49-
(child.tagName === 'title' || child.tagName === 'base')
48+
(child.tagName === 'base' || child.tagName === 'title')
5049
) {
51-
if (seen.includes(child.tagName)) return false
52-
seen.push(child.tagName)
50+
if (seen.has(child.tagName)) return false
51+
seen.add(child.tagName)
5352
}
5453
}
5554

56-
return children.length > 0
55+
// “May be omitted if the element is empty,
56+
// or if the first thing inside the head element is an element.”
57+
const child = node.children[0]
58+
return !child || child.type === 'element'
5759
}
5860

5961
/**

test/omission-closing-head.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {u} from 'unist-builder'
66

77
test('`head` (closing)', async function (t) {
88
await t.test('should omit tag without following', async function () {
9-
assert.deepEqual(toHtml(h('head'), {omitOptionalTags: true}), '<head>')
9+
assert.deepEqual(toHtml(h('head'), {omitOptionalTags: true}), '')
1010
})
1111

1212
await t.test(
@@ -16,7 +16,7 @@ test('`head` (closing)', async function (t) {
1616
toHtml(h('html', [h('head'), u('comment', 'alpha')]), {
1717
omitOptionalTags: true
1818
}),
19-
'<head></head><!--alpha-->'
19+
'</head><!--alpha-->'
2020
)
2121
}
2222
)
@@ -26,7 +26,7 @@ test('`head` (closing)', async function (t) {
2626
async function () {
2727
assert.deepEqual(
2828
toHtml(h('html', [h('head'), ' alpha']), {omitOptionalTags: true}),
29-
'<head></head> alpha'
29+
'</head> alpha'
3030
)
3131
}
3232
)
@@ -38,7 +38,7 @@ test('`head` (closing)', async function (t) {
3838
toHtml(h('html', [h('head'), u('text', 'alpha')]), {
3939
omitOptionalTags: true
4040
}),
41-
'<head>alpha'
41+
'alpha'
4242
)
4343
}
4444
)

test/omission-opening-head.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ test('`head` (opening)', async function (t) {
1111
)
1212
})
1313

14-
await t.test('should not omit tag without children', async function () {
15-
assert.deepEqual(toHtml(h('head'), {omitOptionalTags: true}), '<head>')
16-
})
14+
await t.test(
15+
'should omit tag without children (could be fine in `srcdoc`)',
16+
async function () {
17+
assert.deepEqual(toHtml(h('head'), {omitOptionalTags: true}), '')
18+
}
19+
)
1720

1821
await t.test('should omit tag with `title`', async function () {
1922
assert.deepEqual(

0 commit comments

Comments
 (0)