Skip to content

Commit bd672af

Browse files
committed
Fix SVG handling
1 parent ab7376d commit bd672af

File tree

15 files changed

+90
-26
lines changed

15 files changed

+90
-26
lines changed
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<!DOCTYPE html>
1+
<!DOCTYPE html>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<!DOCTYPE html SYSTEM "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"><html><head></head><body></body></html>
1+
<!DOCTYPE html SYSTEM "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"><html><head></head><body></body></html>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<!--?xml version="1.0"?--><html><head></head><body></body></html>
1+
<!--?xml version="1.0"?--><html><head></head><body></body></html>

src/__fixtures__/doctype/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<!DOCTYPE html>
1+
<!DOCTYPE html>
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<body><br />text<br />
22
<img />text
3-
</body>
3+
</body>

src/__fixtures__/element-void/index.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66

77
<p>this<br />and that</p>
88

9-
<svg><path><circle><g><rect></rect></g></circle></path></svg>
9+
<svg><path><circle><g><rect/></g></circle></path></svg>
1010

11-
<svg><path></path><circle></circle><g></g><rect></rect></svg>
11+
<svg><path/><circle/><g/><rect/></svg>
1212

1313
<math><mglyph><mspace><malignmark></malignmark></mspace></mglyph></math>
1414

1515
<math><mglyph></mglyph><mspace></mspace><malignmark></malignmark></math>
16-
</body>
16+
</body>

src/__fixtures__/simple/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
<!DOCTYPE html><html><head><title>Hello!</title></head><body><h1 id="world">World!<!--after-->
2-
</h1></body></html>
2+
</h1></body></html>
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<li><svg class="icon" aria-label="Verified" viewBox="0 0 16 16" width="18" height="18" role="img"><path fill="currentcolor" fill-rule="evenodd" d="M15.67 7.066l-1.08-1.34a1.5 1.5 0 01-.309-.77l-.19-1.698a1.51 1.51 0 00-1.329-1.33l-1.699-.19c-.3-.03-.56-.159-.78-.329L8.945.33a1.504 1.504 0 00-1.878 0l-1.34 1.08a1.5 1.5 0 01-.77.31l-1.698.19c-.7.08-1.25.63-1.33 1.329l-.19 1.699c-.03.3-.159.56-.329.78L.33 7.055a1.504 1.504 0 000 1.878l1.08 1.34c.17.22.28.48.31.77l.19 1.698c.08.7.63 1.25 1.329 1.33l1.699.19c.3.03.56.159.78.329l1.339 1.08c.55.439 1.329.439 1.878 0l1.34-1.08c.22-.17.48-.28.77-.31l1.698-.19c.7-.08 1.25-.63 1.33-1.329l.19-1.699c.03-.3.159-.56.329-.78l1.08-1.339a1.504 1.504 0 000-1.878zM6.5 12.01L3 8.51l1.5-1.5 2 2 5-5L13 5.56l-6.5 6.45z"/></svg></li>
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"type": "element",
3+
"tagName": "li",
4+
"properties": {},
5+
"children": [
6+
{
7+
"type": "element",
8+
"tagName": "svg",
9+
"properties": {
10+
"className": [
11+
"icon"
12+
],
13+
"ariaLabel": "Verified",
14+
"viewBox": [
15+
0,
16+
0,
17+
16,
18+
16
19+
],
20+
"width": 18,
21+
"height": 18,
22+
"role": "img"
23+
},
24+
"children": [
25+
{
26+
"type": "element",
27+
"tagName": "path",
28+
"properties": {
29+
"fill": "currentcolor",
30+
"fillRule": "evenodd",
31+
"d": "M15.67 7.066l-1.08-1.34a1.5 1.5 0 01-.309-.77l-.19-1.698a1.51 1.51 0 00-1.329-1.33l-1.699-.19c-.3-.03-.56-.159-.78-.329L8.945.33a1.504 1.504 0 00-1.878 0l-1.34 1.08a1.5 1.5 0 01-.77.31l-1.698.19c-.7.08-1.25.63-1.33 1.329l-.19 1.699c-.03.3-.159.56-.329.78L.33 7.055a1.504 1.504 0 000 1.878l1.08 1.34c.17.22.28.48.31.77l.19 1.698c.08.7.63 1.25 1.329 1.33l1.699.19c.3.03.56.159.78.329l1.339 1.08c.55.439 1.329.439 1.878 0l1.34-1.08c.22-.17.48-.28.77-.31l1.698-.19c.7-.08 1.25-.63 1.33-1.329l.19-1.699c.03-.3.159-.56.329-.78l1.08-1.339a1.504 1.504 0 000-1.878zM6.5 12.01L3 8.51l1.5-1.5 2 2 5-5L13 5.56l-6.5 6.45z"
32+
},
33+
"children": []
34+
}
35+
]
36+
}
37+
]
38+
}

src/__fixtures__/svg/index.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div>
22
<svg width="230" height="120" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3-
<circle cx="60" cy="60" r="50" fill="red"></circle>
4-
<circle cx="170" cy="60" r="50" fill="green"></circle>
3+
<circle cx="60" cy="60" r="50" fill="red"/>
4+
<circle cx="170" cy="60" r="50" fill="green"/>
55
</svg>
6-
</div>
6+
</div>

src/__fixtures__/template/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
<template id="text"></template>
44

55
<template id="html"></template>
6-
</body></html>
6+
</body></html>

src/fixtures.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('fixtures', () => {
2121
let parsedExpected;
2222

2323
try {
24-
parsedExpected = fs.readFileSync(fixtureOutput).toString();
24+
parsedExpected = fs.readFileSync(fixtureOutput).toString().trim();
2525
} catch (e) {
2626
fs.writeFileSync(fixtureOutput, parsedActual);
2727
return;

src/index.js

+23-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ns from 'web-namespaces';
22
import find from 'property-information/find';
3-
import schema from 'property-information/html';
3+
import html from 'property-information/html';
4+
import svg from 'property-information/svg';
45

56
function transform(node, options) {
67
switch (node.type) {
@@ -53,7 +54,11 @@ function root(node, options) {
5354
el = doc.createElement('html');
5455
}
5556

56-
return appendAll(el, children, Object.assign({ fragment, namespace }, options));
57+
return appendAll(
58+
el,
59+
children,
60+
Object.assign({ fragment, namespace, impliedNamespace: namespace }, options),
61+
);
5762
}
5863

5964
// Create a `doctype`.
@@ -78,11 +83,21 @@ function comment(node, { doc }) {
7883
// Create an `element`.
7984
function element(node, options) {
8085
const { namespace, doc } = options;
81-
// TODO: use `g` in SVG space.
82-
const { tagName = 'div', properties = {}, children = [] } = node;
83-
const el = typeof namespace !== 'undefined'
84-
? doc.createElementNS(namespace, tagName)
85-
: doc.createElement(tagName);
86+
let impliedNamespace = options.impliedNamespace || namespace;
87+
const { tagName = impliedNamespace === ns.svg ? 'g' : 'div', properties = {}, children = [] } = node;
88+
89+
if (
90+
(impliedNamespace === null || impliedNamespace === undefined || impliedNamespace === ns.html)
91+
&& tagName === 'svg'
92+
) {
93+
impliedNamespace = ns.svg;
94+
}
95+
96+
const schema = impliedNamespace === ns.svg ? svg : html;
97+
98+
const el = impliedNamespace === null || impliedNamespace === undefined
99+
? doc.createElement(tagName)
100+
: doc.createElementNS(impliedNamespace, tagName);
86101

87102
// Add HTML attributes.
88103
const props = Object.keys(properties);
@@ -131,7 +146,7 @@ function element(node, options) {
131146
}
132147
}
133148

134-
return appendAll(el, children, options);
149+
return appendAll(el, children, { ...options, impliedNamespace });
135150
}
136151

137152
// Add all children.

src/index.test.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,20 @@ describe('hast-util-to-dom', () => {
7171
expect(actual).toEqual('<div></div>');
7272
});
7373

74-
it('creates an unknown node', () => {
74+
it('creates an unknown node in HTML', () => {
7575
const actual = serializeNodeToHtmlString(toDOM({ type: 'something-else' }));
7676

7777
expect(actual).toEqual('<div></div>');
7878
});
7979

80+
it('creates an unknown node in SVG', () => {
81+
const actual = serializeNodeToHtmlString(
82+
toDOM({ type: 'something-else' }, { namespace: ns.svg }),
83+
);
84+
85+
expect(actual).toEqual('<g/>');
86+
});
87+
8088
it('creates an unknown node (with children)', () => {
8189
const actual = serializeNodeToHtmlString(toDOM({
8290
type: 'something-else',
@@ -109,7 +117,7 @@ describe('hast-util-to-dom', () => {
109117
{ namespace: ns.svg },
110118
));
111119

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

115123
it('creates an input node with some attributes', () => {

src/utils.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ const XMLSerializer = Serializer.interface;
77
export default function serializeNodeToHtmlString(node) {
88
const serialized = new XMLSerializer().serializeToString(node);
99

10-
// XMLSerializer puts xmlns on the main element that is not in the XML
10+
// XMLSerializer puts xmlns on main” elements that are not in the XML
1111
// namespace.
1212
// We’d like to inspect that, but having the HTML namespace everywhere will
1313
// get unwieldy, so remove those.
14-
return serialized.replace(new RegExp(` xmlns="${ns.html}"`, 'g'), '');
14+
return serialized
15+
.replace(new RegExp(` xmlns="${ns.html}"`, 'g'), '')
16+
.replace(new RegExp(`(<(?:svg|g)) xmlns="${ns.svg}"`, 'g'), '$1');
1517
}

0 commit comments

Comments
 (0)