Skip to content

Commit 8d2e389

Browse files
committed
Use w3c-xmlserializer everywhere
1 parent 4a7b040 commit 8d2e389

File tree

9 files changed

+64
-93
lines changed

9 files changed

+64
-93
lines changed

karma.conf.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const babel = require('rollup-plugin-babel');
22
const commonjs = require('rollup-plugin-commonjs');
33
const resolve = require('rollup-plugin-node-resolve');
4+
const json = require('rollup-plugin-json');
45

56
module.exports = function karmaConfig(config) {
67
config.set({
@@ -22,6 +23,7 @@ module.exports = function karmaConfig(config) {
2223
browser: true,
2324
extensions: ['.mjs', '.js', '.json', '.node'],
2425
}),
26+
json(),
2527
commonjs(),
2628
babel({
2729
presets: [

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@
5454
"rollup": "^1.0.0",
5555
"rollup-plugin-babel": "^4.0.1",
5656
"rollup-plugin-commonjs": "^10.0.0",
57-
"rollup-plugin-node-resolve": "^5.0.0"
57+
"rollup-plugin-json": "^4.0.0",
58+
"rollup-plugin-node-resolve": "^5.0.0",
59+
"w3c-xmlserializer": "^1.1.2"
5860
},
5961
"scripts": {
6062
"clean": "rm -rf dist",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<html><head></head><body></body></html>
1+
<!--?xml version="1.0"?--><html><head></head><body></body></html>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<body><br>text<br>
2-
<img>text
1+
<body><br />text<br />
2+
<img />text
33
</body>

src/__fixtures__/element-void/index.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
<body><img src="http://example.com/fav.ico" alt="foo" title="bar">
1+
<body><img src="http://example.com/fav.ico" alt="foo" title="bar" />
22

3-
<hr>
3+
<hr />
44

5-
<p>this<br>and that</p>
5+
<p>this<br />and that</p>
66

7-
<p>this<br>and that</p>
7+
<p>this<br />and that</p>
88

99
<svg><path><circle><g><rect></rect></g></circle></path></svg>
1010

src/fixtures.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'fs';
22
import path from 'path';
33
import glob from 'glob';
44

5-
import { serializeNodeToHtmlString } from './utils';
5+
import serializeNodeToHtmlString from './utils';
66
import toDOM from './index';
77

88
describe('fixtures', () => {

src/index.test.js

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import ns from 'web-namespaces';
22
import h from 'hastscript';
33
import s from 'hastscript/svg';
44

5-
import { serializeNodeToHtmlString } from './utils';
5+
import serializeNodeToHtmlString from './utils';
6+
67
import toDOM from './index';
78

89
describe('hast-util-to-dom', () => {
@@ -27,9 +28,7 @@ describe('hast-util-to-dom', () => {
2728
});
2829

2930
it('creates a root node with a doctype', () => {
30-
const doctype = '<!DOCTYPE html>';
31-
32-
let actual = serializeNodeToHtmlString(toDOM({
31+
const actual = serializeNodeToHtmlString(toDOM({
3332
type: 'root',
3433
children: [{
3534
type: 'doctype',
@@ -54,10 +53,6 @@ describe('hast-util-to-dom', () => {
5453
}],
5554
}));
5655

57-
if (actual.charAt(doctype.length) === '\n') {
58-
actual = actual.slice(0, doctype.length) + actual.slice(doctype.length + 1);
59-
}
60-
6156
expect(actual).toEqual('<!DOCTYPE html><html><head></head><body></body></html>');
6257
});
6358

@@ -114,37 +109,25 @@ describe('hast-util-to-dom', () => {
114109
{ namespace: ns.svg },
115110
));
116111

117-
expect(actual).toEqual('<g id="foo" class="bar"><circle></circle></g>');
112+
expect(actual).toEqual('<g xmlns="http://www.w3.org/2000/svg" id="foo" class="bar"><circle/></g>');
118113
});
119114

120115
it('creates an input node with some attributes', () => {
121-
let actual = serializeNodeToHtmlString(toDOM(h('input', {
116+
const actual = serializeNodeToHtmlString(toDOM(h('input', {
122117
disabled: true,
123118
value: 'foo',
124119
})));
125120

126-
actual = actual.replace(/disabled=""/, 'disabled="disabled"');
127-
128-
if (actual.slice(-3) === ' />') {
129-
actual = `${actual.slice(0, -3)}>`;
130-
}
131-
132-
expect(actual).toEqual('<input disabled="disabled" value="foo">');
121+
expect(actual).toEqual('<input disabled="" value="foo" />');
133122
});
134123

135124
it('creates an checkbox where `checked` must be set as a property', () => {
136-
let actual = serializeNodeToHtmlString(toDOM(h('input', {
125+
const actual = serializeNodeToHtmlString(toDOM(h('input', {
137126
type: 'checkbox',
138127
checked: true,
139128
})));
140129

141-
actual = actual.replace(/checked=""/, 'checked="checked"');
142-
143-
if (actual.slice(-3) === ' />') {
144-
actual = `${actual.slice(0, -3)}>`;
145-
}
146-
147-
expect(actual).toEqual('<input type="checkbox" checked="checked">');
130+
expect(actual).toEqual('<input type="checkbox" checked="" />');
148131
});
149132

150133
it('handles falsey booleans correctly', () => {
@@ -168,16 +151,11 @@ describe('hast-util-to-dom', () => {
168151

169152
it('handles comma-separated attributes correctly', () => {
170153
const img = 'data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=';
171-
172-
let actual = serializeNodeToHtmlString(toDOM(h('img', {
154+
const actual = serializeNodeToHtmlString(toDOM(h('img', {
173155
srcSet: [`${img} 1x`, `${img} 2x`],
174156
})));
175157

176-
if (actual.slice(-3) === ' />') {
177-
actual = `${actual.slice(0, -3)}>`;
178-
}
179-
180-
expect(actual).toEqual(`<img srcset="${img} 1x, ${img} 2x">`);
158+
expect(actual).toEqual(`<img srcset="${img} 1x, ${img} 2x" />`);
181159
});
182160

183161
it('creates a doctype node', () => {
@@ -268,12 +246,12 @@ describe('hast-util-to-dom', () => {
268246
expect(actual).toEqual('<title>Hi</title><h2>Hello world!</h2>');
269247
});
270248

271-
it('should support an inferred namespace', () => {
249+
it('should support a given namespace', () => {
272250
const actual = serializeNodeToHtmlString(
273251
toDOM({ type: 'root', children: [h('html')] }, { namespace: 'http://example.com' }),
274252
);
275253

276-
expect(actual).toEqual('<html></html>');
254+
expect(actual).toEqual('<html xmlns="http://example.com"/>');
277255
});
278256

279257
describe('booleanish property', () => {

src/utils.js

Lines changed: 11 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,15 @@
1-
export function serializeNodeToString(node) {
2-
if (typeof XMLSerializer !== 'undefined') {
3-
return new XMLSerializer().serializeToString(node);
4-
}
1+
import ns from 'web-namespaces';
2+
// eslint-disable-next-line import/no-extraneous-dependencies
3+
import { XMLSerializer as Serializer } from 'w3c-xmlserializer';
54

6-
if (!node) {
7-
return '';
8-
}
5+
const XMLSerializer = Serializer.interface;
96

10-
if (node.outerHTML != null) {
11-
return node.outerHTML;
12-
}
7+
export default function serializeNodeToHtmlString(node) {
8+
const serialized = new XMLSerializer().serializeToString(node);
139

14-
if (node instanceof Document) {
15-
return `${serializeNodeToString(node.doctype)}${serializeNodeToString(node.documentElement)}`;
16-
}
17-
18-
if (node instanceof DocumentType) {
19-
const docTypeAttrs = [
20-
node.name,
21-
node.publicId ? 'PUBLIC' : '',
22-
node.systemId && !node.publicId ? 'SYSTEM' : '',
23-
node.publicId ? `"${node.publicId}"` : '',
24-
node.systemId ? `"${node.systemId}"` : '',
25-
].filter(v => v).join(' ');
26-
27-
return `<!DOCTYPE ${docTypeAttrs}>`;
28-
}
29-
30-
const el = document.createElement('body');
31-
32-
el.appendChild(node);
33-
34-
return el.innerHTML;
35-
}
36-
37-
export function serializeNodeToHtmlString(node) {
38-
const serialized = serializeNodeToString(node);
39-
40-
// XMLSerializer puts xmlns on documentElement
41-
const { documentElement: ownerDocumentElement } = node.ownerDocument || node;
42-
const { namespaceURI: ownerNamespaceURI } = ownerDocumentElement || {};
43-
const { namespaceURI } = node.documentElement || node;
44-
45-
if (ownerNamespaceURI) {
46-
if (namespaceURI === ownerNamespaceURI) {
47-
return serialized.replace(` xmlns="${ownerNamespaceURI}"`, '');
48-
}
49-
50-
return serialized.replace(new RegExp(` xmlns="${ownerNamespaceURI}"`, 'g'), '');
51-
}
52-
53-
return serialized;
10+
// XMLSerializer puts xmlns on the main element that is not in the XML
11+
// namespace.
12+
// We’d like to inspect that, but having the HTML namespace everywhere will
13+
// get unwieldy, so remove those.
14+
return serialized.replace(new RegExp(` xmlns="${ns.html}"`, 'g'), '');
5415
}

yarn.lock

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2357,6 +2357,11 @@ estree-walker@^0.6.0:
23572357
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.0.tgz#5d865327c44a618dde5699f763891ae31f257dae"
23582358
integrity sha512-peq1RfVAVzr3PU/jL31RaOjUKLoZJpObQWJJ+LgfcxDUifyLZ1RjPQZTl0pzj2uJ45b7A7XpyppXvxdEqzo4rw==
23592359

2360+
estree-walker@^0.6.1:
2361+
version "0.6.1"
2362+
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
2363+
integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
2364+
23602365
esutils@^2.0.2:
23612366
version "2.0.2"
23622367
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
@@ -6496,6 +6501,13 @@ rollup-plugin-commonjs@^10.0.0:
64966501
resolve "^1.10.1"
64976502
rollup-pluginutils "^2.7.0"
64986503

6504+
rollup-plugin-json@^4.0.0:
6505+
version "4.0.0"
6506+
resolved "https://registry.yarnpkg.com/rollup-plugin-json/-/rollup-plugin-json-4.0.0.tgz#a18da0a4b30bf5ca1ee76ddb1422afbb84ae2b9e"
6507+
integrity sha512-hgb8N7Cgfw5SZAkb3jf0QXii6QX/FOkiIq2M7BAQIEydjHvTyxXHQiIzZaTFgx1GK0cRCHOCBHIyEkkLdWKxow==
6508+
dependencies:
6509+
rollup-pluginutils "^2.5.0"
6510+
64996511
rollup-plugin-node-resolve@^5.0.0:
65006512
version "5.0.0"
65016513
resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.0.0.tgz#754abf4841ed4bab2241551cba0a11d04c57f290"
@@ -6515,6 +6527,13 @@ rollup-pluginutils@^2.3.0, rollup-pluginutils@^2.7.0:
65156527
estree-walker "^0.6.0"
65166528
micromatch "^3.1.10"
65176529

6530+
rollup-pluginutils@^2.5.0:
6531+
version "2.8.0"
6532+
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.0.tgz#d7ece1502958a35748a74080c7ac5e95681bcbe9"
6533+
integrity sha512-8TomM64VQH6w+13lemFHX5sZYxLCxHhf9gzdRUEFNXH3Z+0CDYy7Grzqa6YUbZc0GIrfbWoD5GXZ3o5Teqh9ew==
6534+
dependencies:
6535+
estree-walker "^0.6.1"
6536+
65186537
rollup@^1.0.0:
65196538
version "1.12.3"
65206539
resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.12.3.tgz#068b1957d5bebf6c0a758cfe42609b512add35a9"
@@ -7550,6 +7569,15 @@ w3c-hr-time@^1.0.1:
75507569
dependencies:
75517570
browser-process-hrtime "^0.1.2"
75527571

7572+
w3c-xmlserializer@^1.1.2:
7573+
version "1.1.2"
7574+
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794"
7575+
integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==
7576+
dependencies:
7577+
domexception "^1.0.1"
7578+
webidl-conversions "^4.0.2"
7579+
xml-name-validator "^3.0.0"
7580+
75537581
walker@^1.0.7, walker@~1.0.5:
75547582
version "1.0.7"
75557583
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"

0 commit comments

Comments
 (0)