// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+exports[`compiler: element transform > MathML 1`] = `
+"import { template as _template } from 'vue';
+const t0 = _template("<math><mrow><mi>x</mi></mrow></math>", true, 2)
+
+export function render(_ctx) {
+ const n0 = t0()
+ return n0
+}"
+`;
+
+ exports[`compiler: element transform > checkbox with static indeterminate 1`] = `
+ "import { setProp as _setProp, template as _template } from 'vue';
+ const t0 = _template("<input type=\\"checkbox\\">", true)
+
+ export function render(_ctx) {
+ const n0 = t0()
+ _setProp(n0, "indeterminate", "")
+ return n0
+ }"
+ `;
+
exports[`compiler: element transform > component > cache v-on expression with unique handler name 1`] = `
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
}"
`;
+exports[`compiler v-bind > v-bind w/ svg elements 1`] = `
+"import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<svg></svg>", true, 1)
+
+export function render(_ctx) {
+ const n0 = t0()
+ _renderEffect(() => _setDynamicProps(n0, [_ctx.obj], true, true))
+ return n0
+}"
+`;
+
exports[`compiler v-bind > with constant value 1`] = `
"import { setProp as _setProp, template as _template } from 'vue';
- const t0 = _template("<div f=\\"foo1\\" h=\\"1\\"></div>", true)
+ const t0 = _template("<div e=\\"2\\" f=\\"foo1\\" g=\\"1\\" h=\\"1\\"></div>", true)
export function render(_ctx, $props, $emit, $attrs, $slots) {
const n0 = t0()
it('escapes raw static text when generating the template string', () => {
const { ir } = compileWithTextTransform('<code><script></code>')
- expect(ir.template).toContain('<code><script></code>')
- expect(ir.template).not.toContain('<code><script></code>')
+ expect([...ir.template.keys()]).toContain('<code><script></code>')
+ expect([...ir.template.keys()]).not.toContain('<code><script></code>')
})
+
+ test('constant text', () => {
+ const { code } = compileWithTextTransform(
+ `
+ <div>
+ {{ (2) }}
+ {{ \`foo\${1}\` }}
+ {{ 1 }}
+ {{ 1n }}
+ {{ '1' }}
+ </div>`,
+ )
+ expect(code).includes(`_template("<div>2 foo1 1 1 1</div>", true)`)
+ expect(code).toMatchSnapshot()
+ })
})
test('v-if + v-else-if + v-else', () => {
const { code, ir } = compileWithVIf(
- `<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`,
+ `<div v-if="ok"/><p v-else-if="orNot"/><p v-else-if="false"/><template v-else>fine</template>`,
)
expect(code).matchSnapshot()
- expect(ir.template).toEqual(['<div></div>', '<p></p>', 'fine'])
+ expect([...ir.template.keys()]).toEqual(['<div></div>', '<p></p>', 'fine'])
expect(ir.block.returns).toEqual([0])
expect(ir.block.dynamic.children[0].operation).toMatchObject({
import { type CodeFragment, NEWLINE, buildCodeFragment, genCall } from './utils'
export function genTemplates(
- templates: string[],
+ templates: Map<string, number>,
rootIndex: number | undefined,
- { helper }: CodegenContext,
+ context: CodegenContext,
): string {
- return templates
- .map(
- (template, i) =>
- `const ${context.tName(i)} = ${context.helper('template')}(${JSON.stringify(
- template,
- )}${i === rootIndex ? ', true' : ''})\n`,
+ const result: string[] = []
+ let i = 0
+ templates.forEach((ns, template) => {
+ result.push(
- `const t${i} = ${helper('template')}(${JSON.stringify(
++ `const ${context.tName(i)} = ${context.helper('template')}(${JSON.stringify(
+ template,
+ )}${i === rootIndex ? ', true' : ns ? ', false' : ''}${ns ? `, ${ns}` : ''})\n`,
)
- .join('')
+ i++
+ })
+ return result.join('')
}
export function genSelf(
} from '@vue/runtime-core'
import { extend } from '@vue/shared'
- const positionMap = new WeakMap<VNode, DOMRect>()
- const newPositionMap = new WeakMap<VNode, DOMRect>()
- const moveCbKey = Symbol('_moveCb')
+ interface Position {
+ top: number
+ left: number
+ }
+
+ const positionMap = new WeakMap<VNode, Position>()
+ const newPositionMap = new WeakMap<VNode, Position>()
-export const moveCbKey: unique symbol = Symbol('_moveCb')
++const moveCbKey: unique symbol = Symbol('_moveCb')
const enterCbKey = Symbol('_enterCb')
export type TransitionGroupProps = Omit<TransitionProps, 'mode'> & {
vModelSelectInit,
vModelSetSelected,
} from './directives/vModel'
- export { svgNS, mathmlNS } from './nodeOps'
+/**
+ * @internal
+ */
- addTransitionClass,
- removeTransitionClass,
++export { svgNS } from './nodeOps'
+/**
+ * @internal
+ */
+export { xlinkNS } from './modules/attrs'
+ /**
+ * @internal
+ */
+ export {
+ resolveTransitionProps,
+ TransitionPropsValidators,
+ forceReflow,
- moveCbKey,
+ type ElementWithTransition,
+ } from './components/Transition'
+ /**
+ * @internal
+ */
+ export {
+ hasCSSTransform,
+ callPendingCbs,
+ handleMovedChildren,
+ baseApplyTranslation,
+ } from './components/TransitionGroup'
/**
* @internal
*/
setValue,
} from '../../src/dom/prop'
import { setStyle } from '../../src/dom/prop'
- import { VaporComponentInstance } from '../../src/component'
+ import { VaporComponentInstance, createComponent } from '../../src/component'
-import { ref, setCurrentInstance } from '@vue/runtime-dom'
+import { ref, setCurrentInstance, svgNS, xlinkNS } from '@vue/runtime-dom'
+ import { makeRender } from '../_utils'
+ import {
+ createDynamicComponent,
+ defineVaporComponent,
+ renderEffect,
+ template,
+ } from '../../src'
let removeComponentInstance = NOOP
beforeEach(() => {
mergeProps,
patchStyle,
shouldSetAsProp,
+ toClassSet,
+ toStyleMap,
unsafeToTrustedHTML,
+ vShowHidden,
warn,
+ warnPropMismatch,
+ xlinkNS,
} from '@vue/runtime-dom'
import {
type VaporComponentInstance,
;(el as any)._falseValue = value
}
+ if (
+ (__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
+ isHydrating &&
+ !attributeHasMismatch(el, key, value)
+ ) {
+ el[`$${key}`] = value
+ return
+ }
+
if (value !== el[`$${key}`]) {
el[`$${key}`] = value
-- if (value != null) {
-- el.setAttribute(key, value)
++ if (isSVG && key.startsWith('xlink:')) {
++ if (value != null) {
++ el.setAttributeNS(xlinkNS, key, value)
++ } else {
++ el.removeAttributeNS(xlinkNS, key.slice(6, key.length))
++ }
} else {
-- el.removeAttribute(key)
++ if (value != null) {
++ el.setAttribute(key, value)
++ } else {
++ el.removeAttribute(key)
++ }
}
}
}
needRemove && el.removeAttribute(key)
}
-export function setClass(el: TargetElement, value: any): void {
+export function setClass(
+ el: TargetElement,
+ value: any,
+ isSVG: boolean = false,
+): void {
if (el.$root) {
setClassIncremental(el, value)
- } else if ((value = normalizeClass(value)) !== el.$cls) {
- if (isSVG) {
- el.setAttribute('class', (el.$cls = value))
- } else {
- el.className = el.$cls = value
+ } else {
+ value = normalizeClass(value)
+ if (
+ (__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
+ isHydrating &&
+ !classHasMismatch(el, value, false)
+ ) {
+ el.$cls = value
+ return
+ }
+
+ if (value !== el.$cls) {
- el.className = el.$cls = value
++ if (isSVG) {
++ el.setAttribute('class', (el.$cls = value))
++ } else {
++ el.className = el.$cls = value
++ }
}
}
}
}
}
-export function setDynamicProps(el: any, args: any[]): void {
+ export function setBlockHtml(
+ block: Block & { $html?: string },
+ value: any,
+ ): void {
+ value = value == null ? '' : unsafeToTrustedHTML(value)
+ if (block.$html !== value) {
+ setHtmlToBlock(block, (block.$html = value))
+ }
+ }
+
+ function setHtmlToBlock(block: Block, value: any): void {
+ if (block instanceof Node) {
+ if (block instanceof Element) {
+ block.innerHTML = value
+ } else if (__DEV__) {
+ warnCannotSetProp('innerHTML')
+ }
+ } else if (isVaporComponent(block)) {
+ setHtmlToBlock(block.block, value)
+ } else if (isArray(block)) {
+ if (__DEV__) {
+ warnCannotSetProp('innerHTML')
+ }
+ } else {
+ setHtmlToBlock(block.nodes, value)
+ }
+ }
+
+export function setDynamicProps(
+ el: any,
+ args: any[],
+ root?: boolean,
+ isSVG?: boolean,
+): void {
const props = args.length > 1 ? mergeProps(...args) : args[0]
const cacheKey = `$dprops${isApplyingFallthroughProps ? '$' : ''}`
const prevKeys = el[cacheKey] as string[]
el: TargetElement,
key: string,
value: any,
+ isSVG: boolean = false,
): void {
- // TODO
- const isSVG = false
+ let forceHydrate = false
if (key === 'class') {
- setClass(el, value)
+ setClass(el, value, isSVG)
} else if (key === 'style') {
setStyle(el, value)
} else if (isOn(key)) {
} else if (key === 'textContent') {
setElementText(el, value)
} else if (key === 'value' && canSetValueDirectly(el.tagName)) {
- setValue(el, value)
+ setValue(el, value, forceHydrate)
} else {
- setDOMProp(el, key, value)
+ setDOMProp(el, key, value, forceHydrate)
}
} else {
- setAttr(el, key, value)
+ setAttr(el, key, value, isSVG)
}
return value
}
import { adoptTemplate, currentHydrationNode, isHydrating } from './hydration'
- import { child, createTextNode } from './node'
-import { _child, createElement, createTextNode } from './node'
+import { type Namespace, Namespaces } from '@vue/shared'
++import { _child, createTextNode } from './node'
let t: HTMLTemplateElement
return createTextNode(html)
}
if (!node) {
- t = t || createElement('template')
- t.innerHTML = html
- node = _child(t.content)
+ t = t || document.createElement('template')
+ if (ns) {
+ const tag = ns === Namespaces.SVG ? 'svg' : 'math'
+ t.innerHTML = `<${tag}>${html}</${tag}>`
- node = child(child(t.content) as ParentNode)
++ node = _child(_child(t.content) as ParentNode)
+ } else {
+ t.innerHTML = html
- node = child(t.content)
++ node = _child(t.content)
+ }
}
const ret = node.cloneNode(true)
if (root) (ret as any).$root = true