Skip to content

Commit 579ff4d

Browse files
committed
fix nested svg namespaces
1 parent e17f2ba commit 579ff4d

File tree

8 files changed

+56
-41
lines changed

8 files changed

+56
-41
lines changed

src/compiler/parser/index.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { decodeHTML } from 'entities'
44
import { parseHTML } from './html-parser'
55
import { parseText } from './text-parser'
6-
import { hyphenate, cached, no } from 'shared/util'
6+
import { cached, no } from 'shared/util'
77
import {
88
pluckModuleFunction,
99
getAndRemoveAttr,
@@ -59,9 +59,6 @@ export function parse (
5959
expectHTML: options.expectHTML,
6060
isUnaryTag: options.isUnaryTag,
6161
start (tag, attrs, unary) {
62-
// normalize tag name
63-
tag = hyphenate(tag)
64-
6562
// check namespace.
6663
// inherit parent ns if there is one
6764
const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag)

src/core/vdom/create-element.js

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -41,38 +41,16 @@ function _createElement (
4141
// in case of component :is set to falsy value
4242
return emptyVNode()
4343
}
44-
if (typeof tag === 'string') {
45-
const namespace = config.getTagNamespace(tag)
46-
let Ctor
47-
if (config.isReservedTag(tag)) {
48-
return new VNode(
49-
tag, data, normalizeChildren(children, namespace),
50-
undefined, undefined,
51-
namespace, context, host
52-
)
53-
} else if ((Ctor = resolveAsset(context.$options, 'components', tag))) {
54-
return createComponent(Ctor, data, parent, context, host, children, tag)
55-
} else {
56-
if (process.env.NODE_ENV !== 'production') {
57-
if (
58-
!namespace &&
59-
!(config.ignoredElements && config.ignoredElements.indexOf(tag) > -1) &&
60-
config.isUnknownElement(tag)
61-
) {
62-
warn(
63-
'Unknown custom element: <' + tag + '> - did you ' +
64-
'register the component correctly? For recursive components, ' +
65-
'make sure to provide the "name" option.'
66-
)
67-
}
68-
}
69-
return new VNode(
70-
tag, data, normalizeChildren(children, namespace),
71-
undefined, undefined,
72-
namespace, context, host
73-
)
74-
}
75-
} else {
76-
return createComponent(tag, data, parent, context, host, children)
44+
const Ctor = typeof tag === 'string'
45+
? resolveAsset(context.$options, 'components', tag)
46+
: tag
47+
if (Ctor) {
48+
return createComponent(Ctor, data, parent, context, host, children)
49+
} else if (typeof tag === 'string') {
50+
const ns = config.getTagNamespace(tag)
51+
return new VNode(
52+
tag, data, normalizeChildren(children, ns),
53+
undefined, undefined, ns, context, host
54+
)
7755
}
7856
}

src/core/vdom/helpers.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ export function normalizeChildren (
3636
last.text += c.text
3737
} else {
3838
// inherit parent namespace
39-
if (ns && c.tag) c.ns = ns
39+
if (ns) {
40+
applyNS(c, ns)
41+
}
4042
res.push(c)
4143
}
4244
}
@@ -49,6 +51,17 @@ function createTextVNode (val) {
4951
return new VNode(undefined, undefined, undefined, String(val))
5052
}
5153

54+
function applyNS (vnode, ns) {
55+
if (vnode.tag && !vnode.ns) {
56+
vnode.ns = ns
57+
if (vnode.children) {
58+
for (let i = 0, l = vnode.children.length; i < l; i++) {
59+
applyNS(vnode.children[i], ns)
60+
}
61+
}
62+
}
63+
}
64+
5265
export function updateListeners (
5366
on: Object,
5467
oldOn: Object,

src/core/vdom/patch.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* of making flow understand it is not worth it.
88
*/
99

10+
import config from '../config'
1011
import VNode from './vnode'
1112
import { isPrimitive, _toString, warn } from '../util/index'
1213

@@ -90,6 +91,20 @@ export function createPatchFunction (backend) {
9091
const children = vnode.children
9192
const tag = vnode.tag
9293
if (isDef(tag)) {
94+
if (process.env.NODE_ENV !== 'production') {
95+
if (
96+
!vnode.ns &&
97+
!(config.ignoredElements && config.ignoredElements.indexOf(tag) > -1) &&
98+
config.isUnknownElement(tag)
99+
) {
100+
warn(
101+
'Unknown custom element: <' + tag + '> - did you ' +
102+
'register the component correctly? For recursive components, ' +
103+
'make sure to provide the "name" option.',
104+
vnode.context
105+
)
106+
}
107+
}
93108
elm = vnode.elm = vnode.ns
94109
? nodeOps.createElementNS(vnode.ns, tag)
95110
: nodeOps.createElement(tag)

test/unit/features/options/el.spec.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ describe('Options el', () => {
4242

4343
it('svg element', () => {
4444
const parent = document.createElement('div')
45-
parent.innerHTML = '<svg><text :x="x" :y="y" :fill="color">{{ text }}</text></svg>'
45+
parent.innerHTML =
46+
'<svg>' +
47+
'<text :x="x" :y="y" :fill="color">{{ text }}</text>' +
48+
'<g><clipPath><foo></foo></clipPath></g>' +
49+
'</svg>'
4650
const vm = new Vue({
4751
el: parent.childNodes[0],
4852
data: {
@@ -57,6 +61,9 @@ describe('Options el', () => {
5761
expect(vm.$el.childNodes[0].getAttribute('y')).toBe(vm.y.toString())
5862
expect(vm.$el.childNodes[0].getAttribute('fill')).toBe(vm.color)
5963
expect(vm.$el.childNodes[0].textContent).toBe(vm.text)
64+
// nested, non-explicitly listed SVG elements
65+
expect(vm.$el.childNodes[1].childNodes[0].namespaceURI).toContain('svg')
66+
expect(vm.$el.childNodes[1].childNodes[0].childNodes[0].namespaceURI).toContain('svg')
6067
})
6168

6269
it('warn cannot find element', () => {

test/unit/modules/compiler/parser.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe('parser', () => {
4646

4747
it('camelCase element', () => {
4848
const ast = parse('<MyComponent><p>hello world</p></MyComponent>', baseOptions)
49-
expect(ast.tag).toBe('my-component')
49+
expect(ast.tag).toBe('MyComponent')
5050
expect(ast.plain).toBe(true)
5151
expect(ast.children[0].tag).toBe('p')
5252
expect(ast.children[0].plain).toBe(true)

test/unit/modules/vdom/create-element.spec.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ describe('create-element', () => {
6262
expect(vnode.ns).toBeUndefined()
6363
expect(vnode.context).toEqual(vm)
6464
expect(vnode.componentOptions).toBeUndefined()
65-
expect(`Unknown custom element: <${tag}> - did you`).toHaveBeenWarned()
6665
})
6766

6867
it('render empty vnode with falsy tag using createElement', () => {

test/unit/modules/vdom/patch/element.spec.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ describe('element', () => {
1616
expect(elm.namespaceURI).toBe('http://www.w3.org/2000/svg')
1717
})
1818

19+
it('should warn unknown element', () => {
20+
const vnode = new VNode('unknown')
21+
patch(null, vnode)
22+
expect(`Unknown custom element: <unknown>`).toHaveBeenWarned()
23+
})
24+
1925
it('should create an elements which having text content', () => {
2026
const vnode = new VNode('div', {}, [createTextVNode('hello world')])
2127
const elm = patch(null, vnode)

0 commit comments

Comments
 (0)