Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
const _component_Comp = _resolveComponent("Comp")
const n0 = t0()
const n3 = t1()
const n2 = _child(n3, 1)
_setInsertionState(n3, 0)
const n1 = _createComponentWithFallback(_component_Comp)
const n2 = _child(n3)
_renderEffect(() => {
_setProp(n3, "id", _ctx.foo)
_setText(n2, _toDisplayString(_ctx.bar))
Expand Down Expand Up @@ -212,14 +212,38 @@ export function render(_ctx) {
}"
`;

exports[`compile > execution order > setInsertionState > next, child and nthChild should be above the setInsertionState 1`] = `
"import { resolveComponent as _resolveComponent, child as _child, next as _next, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, nthChild as _nthChild, createIf as _createIf, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>")
const t1 = _template("<div><div></div><!><div></div><!><div><button></button></div></div>", true)

export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n6 = t1()
const n5 = _next(_child(n6), 1)
const n7 = _nthChild(n6, 3, 3)
const p0 = _next(n7, 4)
const n4 = _child(p0, 0)
_setInsertionState(n6, n5)
const n0 = _createComponentWithFallback(_component_Comp)
_setInsertionState(n6, n7)
const n1 = _createIf(() => (true), () => {
const n3 = t0()
return n3
})
_renderEffect(() => _setProp(n4, "disabled", _ctx.foo))
return n6
}"
`;

exports[`compile > execution order > with insertionState 1`] = `
"import { resolveComponent as _resolveComponent, child as _child, setInsertionState as _setInsertionState, createSlot as _createSlot, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
const t0 = _template("<div><div></div></div>", true)

export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n3 = t0()
const n1 = _child(n3)
const n1 = _child(n3, 0)
_setInsertionState(n1, null)
const n0 = _createSlot("default", null)
_setInsertionState(n3, 1)
Expand All @@ -234,9 +258,9 @@ const t0 = _template("<div><span> </span> <br> </div>", true)

export function render(_ctx) {
const n3 = t0()
const n0 = _child(n3)
const n1 = _next(n0)
const n2 = _nthChild(n3, 3)
const n0 = _child(n3, 0)
const n1 = _next(n0, 1)
const n2 = _nthChild(n3, 3, 3)
const x0 = _txt(n0)
_setText(x0, _toDisplayString(_ctx.foo))
_renderEffect(() => {
Expand Down
17 changes: 17 additions & 0 deletions packages/compiler-vapor/__tests__/compile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,23 @@ describe('compile', () => {
)
})

describe('setInsertionState', () => {
test('next, child and nthChild should be above the setInsertionState', () => {
const code = compile(`
<div>
<div />
<Comp />
<div />
<div v-if="true" />
<div>
<button :disabled="foo" />
</div>
</div>
`)
expect(code).toMatchSnapshot()
})
})

test('with v-once', () => {
const code = compile(
`<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const t0 = _template("<div> </div>", true)

export function render(_ctx) {
const n1 = t0()
const n0 = _child(n1)
const n0 = _child(n1, 0)
const x1 = _txt(n1)
_renderEffect(() => {
const _foo = _ctx.foo
Expand Down Expand Up @@ -86,7 +86,7 @@ const t0 = _template("<div> </div>", true)

export function render(_ctx) {
const n1 = t0()
const n0 = _child(n1)
const n0 = _child(n1, 0)
const x1 = _txt(n1)
_renderEffect(() => {
const _String = String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const t1 = _template("<div><div></div><!><div></div></div>", true)

export function render(_ctx) {
const n4 = t1()
const n3 = _next(_child(n4))
const n3 = _next(_child(n4), 1)
_setInsertionState(n4, n3)
const n0 = _createIf(() => (1), () => {
const n2 = t0()
Expand All @@ -23,9 +23,9 @@ const t0 = _template("<div><p> </p> <p> </p></div>", true)

export function render(_ctx) {
const n3 = t0()
const n0 = _child(n3)
const n1 = _next(n0)
const n2 = _next(n1)
const n0 = _child(n3, 0)
const n1 = _next(n0, 1)
const n2 = _next(n1, 2)
const x0 = _txt(n0)
const x2 = _txt(n2)
_renderEffect(() => {
Expand All @@ -43,7 +43,7 @@ const t0 = _template("<div><div>x</div><div>x</div><div> </div></div>", true)

export function render(_ctx) {
const n1 = t0()
const n0 = _nthChild(n1, 2)
const n0 = _nthChild(n1, 2, 2)
const x0 = _txt(n0)
_renderEffect(() => _setText(x0, _toDisplayString(_ctx.msg)))
return n1
Expand All @@ -56,12 +56,12 @@ const t0 = _template("<div><div>x</div><div><span> </span></div><div><span> </sp

export function render(_ctx) {
const n3 = t0()
const p0 = _next(_child(n3))
const n0 = _child(p0)
const p1 = _next(p0)
const n1 = _child(p1)
const p2 = _next(p1)
const n2 = _child(p2)
const p0 = _next(_child(n3), 1)
const n0 = _child(p0, 0)
const p1 = _next(p0, 2)
const n1 = _child(p1, 0)
const p2 = _next(p1, 3)
const n2 = _child(p2, 0)
const x0 = _txt(n0)
const x1 = _txt(n1)
const x2 = _txt(n2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
const n3 = _createComponentWithFallback(_component_Comp)
const n2 = _child(n3)
const n2 = _child(n3, 0)
_renderEffect(() => _setText(n2, _toDisplayString(_for_item0.value)))
return [n2, n3]
}, undefined, 2)
Expand All @@ -259,7 +259,7 @@ export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
const n3 = _createComponentWithFallback(_component_Comp)
const n2 = _child(n3)
const n2 = _child(n3, 0)
_renderEffect(() => _setText(n2, _toDisplayString(_for_item0.value)))
return [n2, n3]
}, undefined, 2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const t0 = _template("<div> <span></span></div>", true)

export function render(_ctx, $props, $emit, $attrs, $slots) {
const n2 = t0()
const n0 = _child(n2)
const n1 = _next(n0)
const n0 = _child(n2, 0)
const n1 = _next(n0, 1)
_setText(n0, _toDisplayString(_ctx.msg) + " ")
_setClass(n1, _ctx.clz)
return n2
Expand Down Expand Up @@ -54,7 +54,7 @@ const t0 = _template("<div><div></div></div>", true)

export function render(_ctx) {
const n1 = t0()
const n0 = _child(n1)
const n0 = _child(n1, 0)
_setProp(n0, "id", _ctx.foo)
return n1
}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe('compiler: children transform', () => {
<div>{{ msg }}</div>
</div>`,
)
expect(code).contains(`const n0 = _nthChild(n1, 2)`)
expect(code).contains(`const n0 = _nthChild(n1, 2, 2)`)
expect(code).toMatchSnapshot()
})

Expand All @@ -69,7 +69,7 @@ describe('compiler: children transform', () => {
</div>`,
)
// ensure the insertion anchor is generated before the insertion statement
expect(code).toMatch(`const n3 = _next(_child(n4))`)
expect(code).toMatch(`const n3 = _next(_child(n4), 1)`)
expect(code).toMatch(`_setInsertionState(n4, n3)`)
expect(code).toMatchSnapshot()
})
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler-vapor/src/generators/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function genBlockContent(
}
for (const child of dynamic.children) {
if (!child.hasDynamicChild) {
push(...genChildren(child, context, `n${child.id!}`))
push(...genChildren(child, context, push, `n${child.id!}`))
}
}

Expand Down
49 changes: 38 additions & 11 deletions packages/compiler-vapor/src/generators/template.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { CodegenContext } from '../generate'
import { DynamicFlag, type IRDynamicInfo } from '../ir'
import {
DynamicFlag,
type IRDynamicInfo,
type InsertionStateTypes,
} from '../ir'
import { genDirectivesForElement } from './directive'
import { genOperationWithInsertionState } from './operation'
import { type CodeFragment, NEWLINE, buildCodeFragment, genCall } from './utils'
Expand Down Expand Up @@ -36,7 +40,7 @@ export function genSelf(
}

if (hasDynamicChild) {
push(...genChildren(dynamic, context, `n${id}`))
push(...genChildren(dynamic, context, push, `n${id}`))
}

return frag
Expand All @@ -45,6 +49,7 @@ export function genSelf(
export function genChildren(
dynamic: IRDynamicInfo,
context: CodegenContext,
pushBlock: (...items: CodeFragment[]) => number,
from: string = `n${dynamic.id}`,
): CodeFragment[] {
const { helper } = context
Expand All @@ -53,10 +58,20 @@ export function genChildren(

let offset = 0
let prev: [variable: string, elementIndex: number] | undefined
let ifBranchCount = 0
let prependCount = 0

for (const [index, child] of children.entries()) {
if (
child.operation &&
(child.operation as InsertionStateTypes).anchor === -1
) {
prependCount++
}
if (child.flags & DynamicFlag.NON_TEMPLATE) {
offset--
} else if (child.ifBranch) {
ifBranchCount++
}

const id =
Expand All @@ -72,29 +87,41 @@ export function genChildren(
}

const elementIndex = index + offset
const logicalIndex = elementIndex - ifBranchCount + prependCount
// p for "placeholder" variables that are meant for possible reuse by
// other access paths
const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
push(NEWLINE, `const ${variable} = `)

pushBlock(NEWLINE, `const ${variable} = `)
if (prev) {
if (elementIndex - prev[1] === 1) {
push(...genCall(helper('next'), prev[0]))
pushBlock(...genCall(helper('next'), prev[0], String(logicalIndex)))
} else {
push(...genCall(helper('nthChild'), from, String(elementIndex)))
pushBlock(
...genCall(
helper('nthChild'),
from,
String(elementIndex),
String(logicalIndex),
),
)
}
} else {
if (elementIndex === 0) {
push(...genCall(helper('child'), from))
pushBlock(...genCall(helper('child'), from, String(logicalIndex)))
} else {
// check if there's a node that we can reuse from
let init = genCall(helper('child'), from)
if (elementIndex === 1) {
init = genCall(helper('next'), init)
init = genCall(helper('next'), init, String(logicalIndex))
} else if (elementIndex > 1) {
init = genCall(helper('nthChild'), from, String(elementIndex))
init = genCall(
helper('nthChild'),
from,
String(elementIndex),
String(logicalIndex),
)
}
push(...init)
pushBlock(...init)
}
}

Expand All @@ -107,7 +134,7 @@ export function genChildren(
}

prev = [variable, elementIndex]
push(...genChildren(child, context, variable))
push(...genChildren(child, context, pushBlock, variable))
}

return frag
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-vapor/src/ir/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ export interface IRDynamicInfo {
hasDynamicChild?: boolean
operation?: OperationNode
needsKey?: boolean
ifBranch?: boolean
}

export interface IREffect {
Expand Down
5 changes: 3 additions & 2 deletions packages/compiler-vapor/src/transforms/transformText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
isConstantExpression,
isStaticExpression,
} from '../utils'
import { escapeHtml } from '@vue/shared'

type TextLike = TextNode | InterpolationNode
const seen = new WeakMap<
Expand Down Expand Up @@ -82,7 +83,7 @@ export const transformText: NodeTransform = (node, context) => {
} else if (node.type === NodeTypes.INTERPOLATION) {
processInterpolation(context as TransformContext<InterpolationNode>)
} else if (node.type === NodeTypes.TEXT) {
context.template += node.content
context.template += escapeHtml(node.content)
}
}

Expand Down Expand Up @@ -143,7 +144,7 @@ function processTextContainer(
const literals = values.map(getLiteralExpressionValue)

if (literals.every(l => l != null)) {
context.childrenTemplate = literals.map(l => String(l))
context.childrenTemplate = literals.map(l => escapeHtml(String(l)))
} else {
context.childrenTemplate = [' ']
context.registerOperation({
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-vapor/src/transforms/vIf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export function processIf(
} else {
// check the adjacent v-if
const siblingIf = getSiblingIf(context, true)
context.dynamic.ifBranch = true

const siblings = context.parent && context.parent.dynamic.children
let lastIfNode
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-core/src/components/Teleport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ function moveTeleport(
}
}

interface TeleportTargetElement extends Element {
export interface TeleportTargetElement extends Element {
// last teleport target
_lpa?: Node | null
}
Expand Down
11 changes: 11 additions & 0 deletions packages/runtime-core/src/hydration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,17 @@ export function createHydrationFunctions(
)
}
}

// the server output does not contain blank text nodes. It appears here that
// it is a dynamically inserted anchor, and needs to be skipped.
// e.g. vaporInteropImpl.mount() > selfAnchor
if (
node &&
node.nodeType === DOMNodeTypes.TEXT &&
!(node as Text).data.trim()
) {
node = nextSibling(node)
}
return node
}

Expand Down
Loading
Loading