Skip to content

Commit f0d0cfd

Browse files
authored
feat(runtime-vapor): support svg and MathML (#13703)
1 parent 0f4edb4 commit f0d0cfd

File tree

36 files changed

+474
-110
lines changed

36 files changed

+474
-110
lines changed

packages/compiler-core/__tests__/parse.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import {
77
type ElementNode,
88
ElementTypes,
99
type InterpolationNode,
10-
Namespaces,
1110
NodeTypes,
1211
type Position,
1312
type TextNode,
1413
} from '../src/ast'
1514

1615
import { baseParse } from '../src/parser'
1716
import type { Program } from '@babel/types'
17+
import { Namespaces } from '@vue/shared'
1818

1919
describe('compiler: parse', () => {
2020
describe('Text', () => {

packages/compiler-core/__tests__/testUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import {
22
type ElementNode,
33
ElementTypes,
4-
Namespaces,
54
NodeTypes,
65
type Property,
76
type SimpleExpressionNode,
87
type VNodeCall,
98
locStub,
109
} from '../src'
1110
import {
11+
Namespaces,
1212
PatchFlagNames,
1313
type PatchFlags,
1414
type ShapeFlags,

packages/compiler-core/src/ast.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type PatchFlags, isString } from '@vue/shared'
1+
import { type Namespace, type PatchFlags, isString } from '@vue/shared'
22
import {
33
CREATE_BLOCK,
44
CREATE_ELEMENT_BLOCK,
@@ -16,16 +16,6 @@ import type { PropsExpression } from './transforms/transformElement'
1616
import type { ImportItem, TransformContext } from './transform'
1717
import type { Node as BabelNode } from '@babel/types'
1818

19-
// Vue template is a platform-agnostic superset of HTML (syntax only).
20-
// More namespaces can be declared by platform specific compilers.
21-
export type Namespace = number
22-
23-
export enum Namespaces {
24-
HTML,
25-
SVG,
26-
MATH_ML,
27-
}
28-
2919
export enum NodeTypes {
3020
ROOT,
3121
ELEMENT,

packages/compiler-core/src/options.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
import type {
2-
ElementNode,
3-
Namespace,
4-
Namespaces,
5-
ParentNode,
6-
TemplateChildNode,
7-
} from './ast'
1+
import type { ElementNode, ParentNode, TemplateChildNode } from './ast'
2+
import type { Namespace, Namespaces } from '@vue/shared'
83
import type { CompilerError } from './errors'
94
import type {
105
DirectiveTransform,

packages/compiler-core/src/parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
type ElementNode,
66
ElementTypes,
77
type ForParseResult,
8-
Namespaces,
98
NodeTypes,
109
type RootNode,
1110
type SimpleExpressionNode,
@@ -14,6 +13,7 @@ import {
1413
createRoot,
1514
createSimpleExpression,
1615
} from './ast'
16+
import { Namespaces } from '@vue/shared'
1717
import type { ParserOptions } from './options'
1818
import Tokenizer, {
1919
CharCodes,

packages/compiler-dom/__tests__/parse.spec.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import {
44
type ElementNode,
55
ElementTypes,
66
type InterpolationNode,
7-
Namespaces,
87
NodeTypes,
98
type TextNode,
109
baseParse as parse,
1110
} from '@vue/compiler-core'
1211
import { parserOptions } from '../src/parserOptions'
12+
import { Namespaces } from '@vue/shared'
1313

1414
describe('DOM parser', () => {
1515
describe('Text', () => {
@@ -491,6 +491,17 @@ describe('DOM parser', () => {
491491
expect(element.ns).toBe(Namespaces.SVG)
492492
})
493493

494+
test('SVG tags without explicit root', () => {
495+
const ast = parse('<text/><view/><tspan/>', parserOptions)
496+
const textNode = ast.children[0] as ElementNode
497+
const viewNode = ast.children[1] as ElementNode
498+
const tspanNode = ast.children[2] as ElementNode
499+
500+
expect(textNode.ns).toBe(Namespaces.SVG)
501+
expect(viewNode.ns).toBe(Namespaces.SVG)
502+
expect(tspanNode.ns).toBe(Namespaces.SVG)
503+
})
504+
494505
test('MATH in HTML namespace', () => {
495506
const ast = parse('<html><math></math></html>', parserOptions)
496507
const elementHtml = ast.children[0] as ElementNode
@@ -500,6 +511,17 @@ describe('DOM parser', () => {
500511
expect(element.ns).toBe(Namespaces.MATH_ML)
501512
})
502513

514+
test('MATH tags without explicit root', () => {
515+
const ast = parse('<mi/><mn/><mo/>', parserOptions)
516+
const miNode = ast.children[0] as ElementNode
517+
const mnNode = ast.children[1] as ElementNode
518+
const moNode = ast.children[2] as ElementNode
519+
520+
expect(miNode.ns).toBe(Namespaces.MATH_ML)
521+
expect(mnNode.ns).toBe(Namespaces.MATH_ML)
522+
expect(moNode.ns).toBe(Namespaces.MATH_ML)
523+
})
524+
503525
test('root ns', () => {
504526
const ast = parse('<foreignObject><test/></foreignObject>', {
505527
...parserOptions,

packages/compiler-dom/src/parserOptions.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
import { Namespaces, NodeTypes, type ParserOptions } from '@vue/compiler-core'
2-
import { isHTMLTag, isMathMLTag, isSVGTag, isVoidTag } from '@vue/shared'
1+
import { NodeTypes, type ParserOptions } from '@vue/compiler-core'
2+
import {
3+
Namespaces,
4+
isHTMLTag,
5+
isMathMLTag,
6+
isSVGTag,
7+
isVoidTag,
8+
} from '@vue/shared'
39
import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers'
410
import { decodeHtmlBrowser } from './decodeHtmlBrowser'
511

@@ -24,7 +30,7 @@ export const parserOptions: ParserOptions = {
2430
let ns = parent ? parent.ns : rootNamespace
2531
if (parent && ns === Namespaces.MATH_ML) {
2632
if (parent.tag === 'annotation-xml') {
27-
if (tag === 'svg') {
33+
if (isSVGTag(tag)) {
2834
return Namespaces.SVG
2935
}
3036
if (
@@ -57,10 +63,10 @@ export const parserOptions: ParserOptions = {
5763
}
5864

5965
if (ns === Namespaces.HTML) {
60-
if (tag === 'svg') {
66+
if (isSVGTag(tag)) {
6167
return Namespaces.SVG
6268
}
63-
if (tag === 'math') {
69+
if (isMathMLTag(tag)) {
6470
return Namespaces.MATH_ML
6571
}
6672
}

packages/compiler-dom/src/transforms/stringifyStatic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
ElementTypes,
1010
type ExpressionNode,
1111
type HoistTransform,
12-
Namespaces,
1312
NodeTypes,
1413
type PlainElementNode,
1514
type SimpleExpressionNode,
@@ -21,6 +20,7 @@ import {
2120
isStaticArgOf,
2221
} from '@vue/compiler-core'
2322
import {
23+
Namespaces,
2424
escapeHtml,
2525
isArray,
2626
isBooleanAttr,

packages/compiler-ssr/src/transforms/ssrTransformComponent.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
type ExpressionNode,
1111
type FunctionExpression,
1212
type JSChildNode,
13-
Namespaces,
1413
type NodeTransform,
1514
NodeTypes,
1615
RESOLVE_DYNAMIC_COMPONENT,
@@ -55,7 +54,14 @@ import {
5554
ssrProcessTransitionGroup,
5655
ssrTransformTransitionGroup,
5756
} from './ssrTransformTransitionGroup'
58-
import { extend, isArray, isObject, isPlainObject, isSymbol } from '@vue/shared'
57+
import {
58+
Namespaces,
59+
extend,
60+
isArray,
61+
isObject,
62+
isPlainObject,
63+
isSymbol,
64+
} from '@vue/shared'
5965
import { buildSSRProps } from './ssrTransformElement'
6066
import {
6167
ssrProcessTransition,

packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`compiler: element transform > MathML 1`] = `
4+
"import { template as _template } from 'vue';
5+
const t0 = _template("<math><mrow><mi>x</mi></mrow></math>", true, 2)
6+
7+
export function render(_ctx) {
8+
const n0 = t0()
9+
return n0
10+
}"
11+
`;
12+
313
exports[`compiler: element transform > checkbox with static indeterminate 1`] = `
414
"import { setProp as _setProp, template as _template } from 'vue';
515
const t0 = _template("<input type=\\"checkbox\\">", true)
@@ -448,6 +458,16 @@ export function render(_ctx) {
448458
}"
449459
`;
450460
461+
exports[`compiler: element transform > svg 1`] = `
462+
"import { template as _template } from 'vue';
463+
const t0 = _template("<svg><circle r=\\"40\\"></circle></svg>", true, 1)
464+
465+
export function render(_ctx) {
466+
const n0 = t0()
467+
return n0
468+
}"
469+
`;
470+
451471
exports[`compiler: element transform > v-bind="obj" 1`] = `
452472
"import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
453473
const t0 = _template("<div></div>", true)

0 commit comments

Comments
 (0)