Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: MathML support #7836

Merged
merged 14 commits into from
Dec 8, 2023
4 changes: 2 additions & 2 deletions packages/compiler-dom/src/parserOptions.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { ParserOptions, NodeTypes, Namespaces } from '@vue/compiler-core'
import { isVoidTag, isHTMLTag, isSVGTag } from '@vue/shared'
import { isVoidTag, isHTMLTag, isSVGTag, isMathMLTag } from '@vue/shared'
import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers'
import { decodeHtmlBrowser } from './decodeHtmlBrowser'

export const parserOptions: ParserOptions = {
parseMode: 'html',
isVoidTag,
isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag),
isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag) || isMathMLTag(tag),
isPreTag: tag => tag === 'pre',
decodeEntities: __BROWSER__ ? decodeHtmlBrowser : undefined,

Expand Down
22 changes: 17 additions & 5 deletions packages/runtime-core/src/apiCreateApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
ComponentPublicInstance
} from './componentPublicInstance'
import { Directive, validateDirectiveName } from './directives'
import { RootRenderFunction } from './renderer'
import { ElementNamespace, RootRenderFunction } from './renderer'
import { InjectionKey } from './apiInject'
import { warn } from './warning'
import { createVNode, cloneVNode, VNode } from './vnode'
Expand Down Expand Up @@ -47,7 +47,7 @@ export interface App<HostElement = any> {
mount(
rootContainer: HostElement | string,
isHydrate?: boolean,
isSVG?: boolean
namespace?: boolean | ElementNamespace
): ComponentPublicInstance
unmount(): void
provide<T>(key: InjectionKey<T> | string, value: T): this
Expand Down Expand Up @@ -297,7 +297,7 @@ export function createAppAPI<HostElement>(
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
namespace?: boolean | ElementNamespace
): any {
if (!isMounted) {
// #5571
Expand All @@ -313,17 +313,29 @@ export function createAppAPI<HostElement>(
// this will be set on the root instance on initial mount.
vnode.appContext = context

if (namespace === true) {
namespace = 'svg'
} else if (namespace === false) {
namespace = undefined
}

// HMR root reload
if (__DEV__) {
context.reload = () => {
render(cloneVNode(vnode), rootContainer, isSVG)
// casting to ElementNamespace because TS doesn't guarantee type narrowing
// over function boundaries
render(
cloneVNode(vnode),
rootContainer,
namespace as ElementNamespace
)
}
}

if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
render(vnode, rootContainer, isSVG)
render(vnode, rootContainer, namespace)
}
isMounted = true
app._container = rootContainer
Expand Down
14 changes: 10 additions & 4 deletions packages/runtime-core/src/compat/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from '@vue/shared'
import { warn } from '../warning'
import { cloneVNode, createVNode } from '../vnode'
import { RootRenderFunction } from '../renderer'
import { ElementNamespace, RootRenderFunction } from '../renderer'
import {
App,
AppConfig,
Expand Down Expand Up @@ -503,15 +503,21 @@ function installCompatMount(
container = selectorOrEl || document.createElement('div')
}

const isSVG = container instanceof SVGElement
let namespace: ElementNamespace
if (container instanceof SVGElement) namespace = 'svg'
else if (
typeof MathMLElement === 'function' &&
container instanceof MathMLElement
)
namespace = 'mathml'

// HMR root reload
if (__DEV__) {
context.reload = () => {
const cloned = cloneVNode(vnode)
// compat mode will use instance if not reset to null
cloned.component = null
render(cloned, container, isSVG)
render(cloned, container, namespace)
}
}

Expand All @@ -538,7 +544,7 @@ function installCompatMount(
container.innerHTML = ''

// TODO hydration
render(vnode, container, isSVG)
render(vnode, container, namespace)

if (container instanceof Element) {
container.removeAttribute('v-cloak')
Expand Down
15 changes: 11 additions & 4 deletions packages/runtime-core/src/components/KeepAlive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ import {
queuePostRenderEffect,
MoveType,
RendererElement,
RendererNode
RendererNode,
ElementNamespace
} from '../renderer'
import { setTransitionHooks } from './BaseTransition'
import { ComponentRenderContext } from '../componentPublicInstance'
Expand All @@ -64,7 +65,7 @@ export interface KeepAliveContext extends ComponentRenderContext {
vnode: VNode,
container: RendererElement,
anchor: RendererNode | null,
isSVG: boolean,
namespace: ElementNamespace,
optimized: boolean
) => void
deactivate: (vnode: VNode) => void
Expand Down Expand Up @@ -125,7 +126,13 @@ const KeepAliveImpl: ComponentOptions = {
} = sharedContext
const storageContainer = createElement('div')

sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
sharedContext.activate = (
vnode,
container,
anchor,
namespace,
optimized
) => {
const instance = vnode.component!
move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
// in case props have changed
Expand All @@ -136,7 +143,7 @@ const KeepAliveImpl: ComponentOptions = {
anchor,
instance,
parentSuspense,
isSVG,
namespace,
vnode.slotScopeIds,
optimized
)
Expand Down
51 changes: 26 additions & 25 deletions packages/runtime-core/src/components/Suspense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
MoveType,
SetupRenderEffectFn,
RendererNode,
RendererElement
RendererElement,
ElementNamespace
} from '../renderer'
import { queuePostFlushCb } from '../scheduler'
import { filterSingleRoot, updateHOCHostEl } from '../componentRenderUtils'
Expand Down Expand Up @@ -63,7 +64,7 @@ export const SuspenseImpl = {
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean,
// platform-specific impl passed from renderer
Expand All @@ -76,7 +77,7 @@ export const SuspenseImpl = {
anchor,
parentComponent,
parentSuspense,
isSVG,
namespace,
slotScopeIds,
optimized,
rendererInternals
Expand All @@ -88,7 +89,7 @@ export const SuspenseImpl = {
container,
anchor,
parentComponent,
isSVG,
namespace,
slotScopeIds,
optimized,
rendererInternals
Expand Down Expand Up @@ -130,7 +131,7 @@ function mountSuspense(
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean,
rendererInternals: RendererInternals
Expand All @@ -147,7 +148,7 @@ function mountSuspense(
container,
hiddenContainer,
anchor,
isSVG,
namespace,
slotScopeIds,
optimized,
rendererInternals
Expand All @@ -161,7 +162,7 @@ function mountSuspense(
null,
parentComponent,
suspense,
isSVG,
namespace,
slotScopeIds
)
// now check if we have encountered any async deps
Expand All @@ -179,7 +180,7 @@ function mountSuspense(
anchor,
parentComponent,
null, // fallback tree will not have suspense context
isSVG,
namespace,
slotScopeIds
)
setActiveBranch(suspense, vnode.ssFallback!)
Expand All @@ -195,7 +196,7 @@ function patchSuspense(
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
isSVG: boolean,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean,
{ p: patch, um: unmount, o: { createElement } }: RendererInternals
Expand All @@ -218,7 +219,7 @@ function patchSuspense(
null,
parentComponent,
suspense,
isSVG,
namespace,
slotScopeIds,
optimized
)
Expand All @@ -232,7 +233,7 @@ function patchSuspense(
anchor,
parentComponent,
null, // fallback tree will not have suspense context
isSVG,
namespace,
slotScopeIds,
optimized
)
Expand Down Expand Up @@ -267,7 +268,7 @@ function patchSuspense(
null,
parentComponent,
suspense,
isSVG,
namespace,
slotScopeIds,
optimized
)
Expand All @@ -281,7 +282,7 @@ function patchSuspense(
anchor,
parentComponent,
null, // fallback tree will not have suspense context
isSVG,
namespace,
slotScopeIds,
optimized
)
Expand All @@ -296,7 +297,7 @@ function patchSuspense(
anchor,
parentComponent,
suspense,
isSVG,
namespace,
slotScopeIds,
optimized
)
Expand All @@ -311,7 +312,7 @@ function patchSuspense(
null,
parentComponent,
suspense,
isSVG,
namespace,
slotScopeIds,
optimized
)
Expand All @@ -330,7 +331,7 @@ function patchSuspense(
anchor,
parentComponent,
suspense,
isSVG,
namespace,
slotScopeIds,
optimized
)
Expand All @@ -349,7 +350,7 @@ function patchSuspense(
null,
parentComponent,
suspense,
isSVG,
namespace,
slotScopeIds,
optimized
)
Expand All @@ -376,7 +377,7 @@ export interface SuspenseBoundary {
vnode: VNode<RendererNode, RendererElement, SuspenseProps>
parent: SuspenseBoundary | null
parentComponent: ComponentInternalInstance | null
isSVG: boolean
namespace: ElementNamespace
container: RendererElement
hiddenContainer: RendererElement
anchor: RendererNode | null
Expand Down Expand Up @@ -413,7 +414,7 @@ function createSuspenseBoundary(
container: RendererElement,
hiddenContainer: RendererElement,
anchor: RendererNode | null,
isSVG: boolean,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean,
rendererInternals: RendererInternals,
Expand Down Expand Up @@ -455,7 +456,7 @@ function createSuspenseBoundary(
vnode,
parent: parentSuspense,
parentComponent,
isSVG,
namespace,
container,
hiddenContainer,
anchor,
Expand Down Expand Up @@ -576,7 +577,7 @@ function createSuspenseBoundary(
return
}

const { vnode, activeBranch, parentComponent, container, isSVG } =
const { vnode, activeBranch, parentComponent, container, namespace } =
suspense

// invoke @fallback event
Expand All @@ -594,7 +595,7 @@ function createSuspenseBoundary(
next(activeBranch!),
parentComponent,
null, // fallback tree will not have suspense context
isSVG,
namespace,
slotScopeIds,
optimized
)
Expand Down Expand Up @@ -675,7 +676,7 @@ function createSuspenseBoundary(
// consider the comment placeholder case.
hydratedEl ? null : next(instance.subTree),
suspense,
isSVG,
namespace,
optimized
)
if (placeholder) {
Expand Down Expand Up @@ -721,7 +722,7 @@ function hydrateSuspense(
vnode: VNode,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean,
rendererInternals: RendererInternals,
Expand All @@ -742,7 +743,7 @@ function hydrateSuspense(
node.parentNode!,
document.createElement('div'),
null,
isSVG,
namespace,
slotScopeIds,
optimized,
rendererInternals,
Expand Down
Loading