Skip to content

Commit c752e03

Browse files
committed
Add support for SVG
Related to wooorm/property-information#6.
1 parent aa79bdf commit c752e03

File tree

11 files changed

+350
-392
lines changed

11 files changed

+350
-392
lines changed

Diff for: index.js

+84-141
Original file line numberDiff line numberDiff line change
@@ -1,138 +1,135 @@
11
'use strict'
22

33
var xtend = require('xtend')
4+
var html = require('property-information/html')
5+
var svg = require('property-information/svg')
6+
var find = require('property-information/find')
47
var toH = require('hast-to-hyperscript')
5-
var NS = require('web-namespaces')
8+
var ns = require('web-namespaces')
69
var zwitch = require('zwitch')
7-
var mapz = require('mapz')
810

911
module.exports = transform
1012

11-
var own = {}.hasOwnProperty
12-
var one = zwitch('type')
13-
var all = mapz(one, {key: 'children', indices: false})
13+
var ignoredSpaces = ['svg', 'html']
1414

15-
var customProps = [
16-
'sourceCodeLocation',
17-
'childNodes',
18-
'content',
19-
'parentNode',
20-
'namespaceURI'
21-
]
15+
var one = zwitch('type')
2216

2317
one.handlers.root = root
2418
one.handlers.element = element
2519
one.handlers.text = text
2620
one.handlers.comment = comment
2721
one.handlers.doctype = doctype
2822

29-
/* Map of tag-names starting new namespaces. */
30-
var namespaces = {
31-
math: NS.mathml,
32-
svg: NS.svg
23+
// Transform a tree from HAST to Parse5’s AST.
24+
function transform(tree, space) {
25+
return one(tree, space === 'svg' ? svg : html)
3326
}
3427

35-
/* Map of attributes with namespaces. */
36-
var attributeSpaces = {
37-
'xlink:actuate': {prefix: 'xlink', name: 'actuate', namespace: NS.xlink},
38-
'xlink:arcrole': {prefix: 'xlink', name: 'arcrole', namespace: NS.xlink},
39-
'xlink:href': {prefix: 'xlink', name: 'href', namespace: NS.xlink},
40-
'xlink:role': {prefix: 'xlink', name: 'role', namespace: NS.xlink},
41-
'xlink:show': {prefix: 'xlink', name: 'show', namespace: NS.xlink},
42-
'xlink:title': {prefix: 'xlink', name: 'title', namespace: NS.xlink},
43-
'xlink:type': {prefix: 'xlink', name: 'type', namespace: NS.xlink},
44-
'xml:base': {prefix: 'xml', name: 'base', namespace: NS.xml},
45-
'xml:lang': {prefix: 'xml', name: 'lang', namespace: NS.xml},
46-
'xml:space': {prefix: 'xml', name: 'space', namespace: NS.xml},
47-
xmlns: {prefix: '', name: 'xmlns', namespace: NS.xmlns},
48-
'xmlns:xlink': {prefix: 'xmlns', name: 'xlink', namespace: NS.xmlns}
28+
function root(node, schema) {
29+
var data = node.data || {}
30+
var mode = data.quirksMode ? 'quirks' : 'no-quirks'
31+
32+
return patch(node, {nodeName: '#document', mode: mode}, schema)
4933
}
5034

51-
/* Transform a tree from HAST to Parse5’s AST. */
52-
function transform(tree) {
53-
return patch(one(tree), null, NS.html)
35+
function fragment(node, schema) {
36+
return patch(node, {nodeName: '#document-fragment'}, schema)
5437
}
5538

56-
function root(node) {
57-
var data = node.data || {}
58-
var qs = own.call(data, 'quirksMode') ? Boolean(data.quirksMode) : false
39+
function doctype(node, schema) {
40+
return patch(
41+
node,
42+
{
43+
nodeName: '#documentType',
44+
name: node.name,
45+
publicId: node.public || '',
46+
systemId: node.system || ''
47+
},
48+
schema
49+
)
50+
}
5951

60-
return {
61-
nodeName: '#document',
62-
mode: qs ? 'quirks' : 'no-quirks',
63-
childNodes: all(node)
64-
}
52+
function text(node, schema) {
53+
return patch(node, {nodeName: '#text', value: node.value}, schema)
54+
}
55+
56+
function comment(node, schema) {
57+
return patch(node, {nodeName: '#comment', data: node.value}, schema)
6558
}
6659

67-
function element(node) {
68-
var shallow = xtend(node)
60+
function element(node, schema) {
61+
var space = schema.space
62+
var shallow = xtend(node, {children: []})
6963

70-
shallow.children = []
64+
return toH(h, shallow, {space: space})
7165

72-
return toH(function(name, attrs) {
66+
function h(name, attrs) {
7367
var values = []
74-
var content
68+
var p5
7569
var value
7670
var key
71+
var info
72+
var pos
7773

7874
for (key in attrs) {
75+
info = find(schema, key)
7976
value = {name: key, value: attrs[key]}
8077

81-
if (own.call(attributeSpaces, key)) {
82-
value = xtend(value, attributeSpaces[key])
78+
if (info.space && ignoredSpaces.indexOf(info.space) === -1) {
79+
pos = key.indexOf(':')
80+
81+
if (pos === -1) {
82+
value.prefix = ''
83+
} else {
84+
value.name = key.slice(pos + 1)
85+
value.prefix = key.slice(0, pos)
86+
}
87+
value.namespace = ns[info.space]
8388
}
8489

8590
values.push(value)
8691
}
8792

93+
p5 = patch(node, {nodeName: name, tagName: name, attrs: values}, schema)
94+
8895
if (name === 'template') {
89-
content = transform(shallow.content)
90-
delete content.mode
91-
content.nodeName = '#document-fragment'
96+
p5.content = fragment(shallow.content, schema)
9297
}
9398

94-
return wrap(
95-
node,
96-
{
97-
nodeName: node.tagName,
98-
tagName: node.tagName,
99-
attrs: values,
100-
childNodes: node.children ? all(node) : []
101-
},
102-
content
103-
)
104-
}, shallow)
99+
return p5
100+
}
105101
}
106102

107-
function doctype(node) {
108-
return wrap(node, {
109-
nodeName: '#documentType',
110-
name: node.name,
111-
publicId: node.public || '',
112-
systemId: node.system || ''
113-
})
114-
}
103+
// Patch specific properties.
104+
function patch(node, p5, parentSchema) {
105+
var schema = parentSchema
106+
var position = node.position
107+
var children = node.children
108+
var childNodes = []
109+
var length = children ? children.length : 0
110+
var index = -1
111+
var child
112+
113+
if (node.type === 'element') {
114+
if (schema.space === 'html' && node.tagName === 'svg') {
115+
schema = svg
116+
}
115117

116-
function text(node) {
117-
return wrap(node, {
118-
nodeName: '#text',
119-
value: node.value
120-
})
121-
}
118+
p5.namespaceURI = ns[schema.space]
119+
}
122120

123-
function comment(node) {
124-
return wrap(node, {
125-
nodeName: '#comment',
126-
data: node.value
127-
})
128-
}
121+
while (++index < length) {
122+
child = one(children[index], schema)
123+
child.parentNode = p5
124+
childNodes[index] = child
125+
}
129126

130-
/* Patch position. */
131-
function wrap(node, ast, content) {
132-
var position = node.position
127+
if (node.type === 'element' || node.type === 'root') {
128+
p5.childNodes = childNodes
129+
}
133130

134131
if (position && position.start && position.end) {
135-
ast.sourceCodeLocation = {
132+
p5.sourceCodeLocation = {
136133
startLine: position.start.line,
137134
startCol: position.start.column,
138135
startOffset: position.start.offset,
@@ -142,59 +139,5 @@ function wrap(node, ast, content) {
142139
}
143140
}
144141

145-
if (content) {
146-
ast.content = content
147-
}
148-
149-
return ast
150-
}
151-
152-
/* Patch a tree recursively, by adding namespaces
153-
* and parent references where needed. */
154-
function patch(node, parent, ns) {
155-
var location = node.sourceCodeLocation
156-
var children = node.childNodes
157-
var name = node.tagName
158-
var replacement = {}
159-
var length
160-
var index
161-
var key
162-
163-
for (key in node) {
164-
if (customProps.indexOf(key) === -1) {
165-
replacement[key] = node[key]
166-
}
167-
}
168-
169-
if (own.call(namespaces, name)) {
170-
ns = namespaces[name]
171-
}
172-
173-
if (own.call(replacement, 'tagName')) {
174-
replacement.namespaceURI = ns
175-
}
176-
177-
if (children) {
178-
replacement.childNodes = children
179-
length = children.length
180-
index = -1
181-
182-
while (++index < length) {
183-
children[index] = patch(children[index], replacement, ns)
184-
}
185-
}
186-
187-
if (name === 'template') {
188-
replacement.content = patch(node.content, null, ns)
189-
}
190-
191-
if (parent) {
192-
replacement.parentNode = parent
193-
}
194-
195-
if (location) {
196-
replacement.sourceCodeLocation = location
197-
}
198-
199-
return replacement
142+
return p5
200143
}

Diff for: package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@
1919
"index.js"
2020
],
2121
"dependencies": {
22-
"hast-to-hyperscript": "^4.0.0",
23-
"mapz": "^1.0.0",
22+
"hast-to-hyperscript": "^5.0.0",
23+
"property-information": "^4.0.0",
2424
"web-namespaces": "^1.0.0",
2525
"xtend": "^4.0.1",
2626
"zwitch": "^1.0.0"
2727
},
2828
"devDependencies": {
2929
"browserify": "^16.0.0",
3030
"esmangle": "^1.0.1",
31+
"json-stringify-safe": "^5.0.1",
3132
"nyc": "^12.0.0",
3233
"parse5": "^5.0.0",
3334
"prettier": "^1.13.5",

Diff for: test/comment.js

+4-11
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,12 @@ var parse5 = require('parse5')
55
var toParse5 = require('..')
66

77
test('comment', function(t) {
8-
var node = parse5.parseFragment('<!--Alpha-->')
8+
var actual = toParse5({type: 'comment', value: 'Alpha'})
9+
var expected = parse5.parseFragment('<!--Alpha-->').childNodes[0]
910

10-
node = node.childNodes[0]
11-
delete node.parentNode
11+
delete expected.parentNode
1212

13-
t.deepEqual(
14-
toParse5({
15-
type: 'comment',
16-
value: 'Alpha'
17-
}),
18-
node,
19-
'should transform comments'
20-
)
13+
t.deepEqual(actual, expected, 'should transform comments')
2114

2215
t.end()
2316
})

Diff for: test/doctype.js

+21-24
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,33 @@ var parse5 = require('parse5')
55
var toParse5 = require('..')
66

77
test('doctype', function(t) {
8-
var node = parse5.parse(
9-
'<!DOCTYPE html SYSTEM "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd">'
10-
)
11-
12-
node = node.childNodes[0]
13-
delete node.parentNode
14-
15-
t.deepEqual(
16-
toParse5({
8+
t.test('should transform a doctype (legacy)', function(st) {
9+
var actual = toParse5({
1710
type: 'doctype',
1811
name: 'html',
1912
system: 'http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd'
20-
}),
21-
node,
22-
'should transform a doctype (legacy)'
23-
)
13+
})
14+
var expected = parse5.parse(
15+
'<!DOCTYPE html SYSTEM "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd">'
16+
).childNodes[0]
2417

25-
node = parse5.parse('<!doctype html>')
18+
delete expected.parentNode
2619

27-
node = node.childNodes[0]
28-
delete node.parentNode
20+
st.deepEqual(actual, expected)
2921

30-
t.deepEqual(
31-
toParse5({
32-
type: 'doctype',
33-
name: 'html'
34-
}),
35-
node,
36-
'should transform a doctype (modern)'
37-
)
22+
st.end()
23+
})
24+
25+
t.test('should transform a doctype (modern)', function(st) {
26+
var actual = toParse5({type: 'doctype', name: 'html'})
27+
var expected = parse5.parse('<!doctypehtml>').childNodes[0]
28+
29+
delete expected.parentNode
30+
31+
st.deepEqual(actual, expected)
32+
33+
st.end()
34+
})
3835

3936
t.end()
4037
})

0 commit comments

Comments
 (0)