mirror of https://github.com/vuejs/core.git
Merge 6b8f1f2778
into ba391f5fdf
This commit is contained in:
commit
46a3e014d9
|
@ -139,6 +139,24 @@ return function render(_ctx, _cache) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform component slots > named slots w/ implicit default slot containing non-breaking space 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
with (_ctx) {
|
||||||
|
const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(_component_Comp, null, {
|
||||||
|
one: _withCtx(() => ["foo"]),
|
||||||
|
default: _withCtx(() => [" "]),
|
||||||
|
_: 1 /* STABLE */
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform component slots > nested slots scoping 1`] = `
|
exports[`compiler: transform component slots > nested slots scoping 1`] = `
|
||||||
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||||
|
|
||||||
|
@ -232,6 +250,20 @@ return function render(_ctx, _cache) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform component slots > with whitespace: 'preserve' > implicit default slot with non-breaking space 1`] = `
|
||||||
|
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(_component_Comp, null, {
|
||||||
|
header: _withCtx(() => [" Header "]),
|
||||||
|
default: _withCtx(() => ["\\n \\n "]),
|
||||||
|
_: 1 /* STABLE */
|
||||||
|
}))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform component slots > with whitespace: 'preserve' > named default slot + implicit whitespace content 1`] = `
|
exports[`compiler: transform component slots > with whitespace: 'preserve' > named default slot + implicit whitespace content 1`] = `
|
||||||
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||||
|
|
||||||
|
@ -268,6 +300,32 @@ return function render(_ctx, _cache) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform component slots > with whitespace: 'preserve' > named slot with v-if + v-else and comments 1`] = `
|
||||||
|
"const { createTextVNode: _createTextVNode, createCommentVNode: _createCommentVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 2 /* DYNAMIC */ }, [
|
||||||
|
ok
|
||||||
|
? {
|
||||||
|
name: "one",
|
||||||
|
fn: _withCtx(() => [
|
||||||
|
_createTextVNode("foo")
|
||||||
|
]),
|
||||||
|
key: "0"
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: "two",
|
||||||
|
fn: _withCtx(() => [
|
||||||
|
_createTextVNode("baz")
|
||||||
|
]),
|
||||||
|
key: "1"
|
||||||
|
}
|
||||||
|
]), 1024 /* DYNAMIC_SLOTS */))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform component slots > with whitespace: 'preserve' > should not generate whitespace only default slot 1`] = `
|
exports[`compiler: transform component slots > with whitespace: 'preserve' > should not generate whitespace only default slot 1`] = `
|
||||||
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
type ForNode,
|
type ForNode,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
generate,
|
generate,
|
||||||
|
isWhitespaceText,
|
||||||
baseParse as parse,
|
baseParse as parse,
|
||||||
transform,
|
transform,
|
||||||
} from '../../src'
|
} from '../../src'
|
||||||
|
@ -109,6 +110,24 @@ describe('compiler: transform text', () => {
|
||||||
expect(generate(root).code).toMatchSnapshot()
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('whitespace text', () => {
|
||||||
|
const root = transformWithTextOpt(`<div/>hello<div/> <div/>`)
|
||||||
|
expect(root.children.length).toBe(5)
|
||||||
|
expect(root.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect(root.children[1].type).toBe(NodeTypes.TEXT_CALL)
|
||||||
|
expect(root.children[2].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect(root.children[3].type).toBe(NodeTypes.TEXT_CALL)
|
||||||
|
expect(root.children[4].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
|
||||||
|
expect(root.children.map(isWhitespaceText)).toEqual([
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
test('consecutive text mixed with elements', () => {
|
test('consecutive text mixed with elements', () => {
|
||||||
const root = transformWithTextOpt(
|
const root = transformWithTextOpt(
|
||||||
`<div/>{{ foo }} bar {{ baz }}<div/>hello<div/>`,
|
`<div/>{{ foo }} bar {{ baz }}<div/>hello<div/>`,
|
||||||
|
|
|
@ -246,6 +246,31 @@ describe('compiler: v-if', () => {
|
||||||
loc: node3.loc,
|
loc: node3.loc,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const { node: node4 } = parseWithIfTransform(
|
||||||
|
`<div v-if="bar"/>foo<div v-else/>`,
|
||||||
|
{ onError },
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
expect(onError.mock.calls[3]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF,
|
||||||
|
loc: node4.loc,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// Non-breaking space
|
||||||
|
const { node: node5 } = parseWithIfTransform(
|
||||||
|
`<div v-if="bar"/>\u00a0<div v-else/>`,
|
||||||
|
{ onError },
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
expect(onError.mock.calls[4]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF,
|
||||||
|
loc: node5.loc,
|
||||||
|
},
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('error on v-else-if missing adjacent v-if or v-else-if', () => {
|
test('error on v-else-if missing adjacent v-if or v-else-if', () => {
|
||||||
|
@ -285,6 +310,31 @@ describe('compiler: v-if', () => {
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const { node: node4 } = parseWithIfTransform(
|
||||||
|
`<div v-if="bar"/>foo<div v-else-if="foo"/>`,
|
||||||
|
{ onError },
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
expect(onError.mock.calls[3]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF,
|
||||||
|
loc: node4.loc,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// Non-breaking space
|
||||||
|
const { node: node5 } = parseWithIfTransform(
|
||||||
|
`<div v-if="bar"/>\u00a0<div v-else-if="foo"/>`,
|
||||||
|
{ onError },
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
expect(onError.mock.calls[4]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF,
|
||||||
|
loc: node5.loc,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const {
|
const {
|
||||||
node: { branches },
|
node: { branches },
|
||||||
} = parseWithIfTransform(
|
} = parseWithIfTransform(
|
||||||
|
@ -293,7 +343,7 @@ describe('compiler: v-if', () => {
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(onError.mock.calls[3]).toMatchObject([
|
expect(onError.mock.calls[5]).toMatchObject([
|
||||||
{
|
{
|
||||||
code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF,
|
code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF,
|
||||||
loc: branches[branches.length - 1].loc,
|
loc: branches[branches.length - 1].loc,
|
||||||
|
|
|
@ -28,8 +28,12 @@ import { createObjectMatcher } from '../testUtils'
|
||||||
import { PatchFlags } from '@vue/shared'
|
import { PatchFlags } from '@vue/shared'
|
||||||
import { transformFor } from '../../src/transforms/vFor'
|
import { transformFor } from '../../src/transforms/vFor'
|
||||||
import { transformIf } from '../../src/transforms/vIf'
|
import { transformIf } from '../../src/transforms/vIf'
|
||||||
|
import { transformText } from '../../src/transforms/transformText'
|
||||||
|
|
||||||
function parseWithSlots(template: string, options: CompilerOptions = {}) {
|
function parseWithSlots(
|
||||||
|
template: string,
|
||||||
|
options: CompilerOptions & { transformText?: boolean } = {},
|
||||||
|
) {
|
||||||
const ast = parse(template, {
|
const ast = parse(template, {
|
||||||
whitespace: options.whitespace,
|
whitespace: options.whitespace,
|
||||||
})
|
})
|
||||||
|
@ -43,6 +47,7 @@ function parseWithSlots(template: string, options: CompilerOptions = {}) {
|
||||||
transformSlotOutlet,
|
transformSlotOutlet,
|
||||||
transformElement,
|
transformElement,
|
||||||
trackSlotScopes,
|
trackSlotScopes,
|
||||||
|
...(options.transformText ? [transformText] : []),
|
||||||
],
|
],
|
||||||
directiveTransforms: {
|
directiveTransforms: {
|
||||||
on: transformOn,
|
on: transformOn,
|
||||||
|
@ -307,6 +312,40 @@ describe('compiler: transform component slots', () => {
|
||||||
expect(generate(root).code).toMatchSnapshot()
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('named slots w/ implicit default slot containing non-breaking space', () => {
|
||||||
|
const { root, slots } = parseWithSlots(
|
||||||
|
`<Comp>
|
||||||
|
\u00a0
|
||||||
|
<template #one>foo</template>
|
||||||
|
</Comp>`,
|
||||||
|
)
|
||||||
|
expect(slots).toMatchObject(
|
||||||
|
createSlotMatcher({
|
||||||
|
one: {
|
||||||
|
type: NodeTypes.JS_FUNCTION_EXPRESSION,
|
||||||
|
params: undefined,
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.TEXT,
|
||||||
|
content: `foo`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
type: NodeTypes.JS_FUNCTION_EXPRESSION,
|
||||||
|
params: undefined,
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.TEXT,
|
||||||
|
content: ` \u00a0 `,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
test('dynamically named slots', () => {
|
test('dynamically named slots', () => {
|
||||||
const { root, slots } = parseWithSlots(
|
const { root, slots } = parseWithSlots(
|
||||||
`<Comp>
|
`<Comp>
|
||||||
|
@ -989,6 +1028,27 @@ describe('compiler: transform component slots', () => {
|
||||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('implicit default slot with non-breaking space', () => {
|
||||||
|
const source = `
|
||||||
|
<Comp>
|
||||||
|
|
||||||
|
<template #header> Header </template>
|
||||||
|
</Comp>
|
||||||
|
`
|
||||||
|
const { root } = parseWithSlots(source, {
|
||||||
|
whitespace: 'preserve',
|
||||||
|
})
|
||||||
|
|
||||||
|
const slots = (root as any).children[0].codegenNode.children
|
||||||
|
.properties as ObjectExpression['properties']
|
||||||
|
|
||||||
|
expect(
|
||||||
|
slots.some(p => (p.key as SimpleExpressionNode).content === 'default'),
|
||||||
|
).toBe(true)
|
||||||
|
|
||||||
|
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
test('named slot with v-if + v-else', () => {
|
test('named slot with v-if + v-else', () => {
|
||||||
const source = `
|
const source = `
|
||||||
<Comp>
|
<Comp>
|
||||||
|
@ -1002,5 +1062,23 @@ describe('compiler: transform component slots', () => {
|
||||||
|
|
||||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('named slot with v-if + v-else and comments', () => {
|
||||||
|
const source = `
|
||||||
|
<Comp>
|
||||||
|
<template #one v-if="ok">foo</template>
|
||||||
|
<!-- start -->
|
||||||
|
|
||||||
|
<!-- end -->
|
||||||
|
<template #two v-else>baz</template>
|
||||||
|
</Comp>
|
||||||
|
`
|
||||||
|
const { root } = parseWithSlots(source, {
|
||||||
|
transformText: true,
|
||||||
|
whitespace: 'preserve',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -40,6 +40,7 @@ import {
|
||||||
} from './errors'
|
} from './errors'
|
||||||
import {
|
import {
|
||||||
forAliasRE,
|
forAliasRE,
|
||||||
|
isAllWhitespace,
|
||||||
isCoreComponent,
|
isCoreComponent,
|
||||||
isSimpleIdentifier,
|
isSimpleIdentifier,
|
||||||
isStaticArgOf,
|
isStaticArgOf,
|
||||||
|
@ -880,15 +881,6 @@ function condenseWhitespace(nodes: TemplateChildNode[]): TemplateChildNode[] {
|
||||||
return removedWhitespace ? nodes.filter(Boolean) : nodes
|
return removedWhitespace ? nodes.filter(Boolean) : nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAllWhitespace(str: string) {
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
if (!isWhitespace(str.charCodeAt(i))) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasNewlineChar(str: string) {
|
function hasNewlineChar(str: string) {
|
||||||
for (let i = 0; i < str.length; i++) {
|
for (let i = 0; i < str.length; i++) {
|
||||||
const c = str.charCodeAt(i)
|
const c = str.charCodeAt(i)
|
||||||
|
|
|
@ -32,7 +32,13 @@ import { processExpression } from './transformExpression'
|
||||||
import { validateBrowserExpression } from '../validateExpression'
|
import { validateBrowserExpression } from '../validateExpression'
|
||||||
import { cloneLoc } from '../parser'
|
import { cloneLoc } from '../parser'
|
||||||
import { CREATE_COMMENT, FRAGMENT } from '../runtimeHelpers'
|
import { CREATE_COMMENT, FRAGMENT } from '../runtimeHelpers'
|
||||||
import { findDir, findProp, getMemoedVNodeCall, injectProp } from '../utils'
|
import {
|
||||||
|
findDir,
|
||||||
|
findProp,
|
||||||
|
getMemoedVNodeCall,
|
||||||
|
injectProp,
|
||||||
|
isCommentOrWhitespace,
|
||||||
|
} from '../utils'
|
||||||
import { PatchFlags } from '@vue/shared'
|
import { PatchFlags } from '@vue/shared'
|
||||||
|
|
||||||
export const transformIf: NodeTransform = createStructuralDirectiveTransform(
|
export const transformIf: NodeTransform = createStructuralDirectiveTransform(
|
||||||
|
@ -125,18 +131,11 @@ export function processIf(
|
||||||
let i = siblings.indexOf(node)
|
let i = siblings.indexOf(node)
|
||||||
while (i-- >= -1) {
|
while (i-- >= -1) {
|
||||||
const sibling = siblings[i]
|
const sibling = siblings[i]
|
||||||
if (sibling && sibling.type === NodeTypes.COMMENT) {
|
if (sibling && isCommentOrWhitespace(sibling)) {
|
||||||
context.removeNode(sibling)
|
context.removeNode(sibling)
|
||||||
__DEV__ && comments.unshift(sibling)
|
if (__DEV__ && sibling.type === NodeTypes.COMMENT) {
|
||||||
continue
|
comments.unshift(sibling)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
sibling &&
|
|
||||||
sibling.type === NodeTypes.TEXT &&
|
|
||||||
!sibling.content.trim().length
|
|
||||||
) {
|
|
||||||
context.removeNode(sibling)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,11 @@ import {
|
||||||
assert,
|
assert,
|
||||||
findDir,
|
findDir,
|
||||||
hasScopeRef,
|
hasScopeRef,
|
||||||
|
isCommentOrWhitespace,
|
||||||
isStaticExp,
|
isStaticExp,
|
||||||
isTemplateNode,
|
isTemplateNode,
|
||||||
isVSlot,
|
isVSlot,
|
||||||
|
isWhitespaceText,
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { CREATE_SLOTS, RENDER_LIST, WITH_CTX } from '../runtimeHelpers'
|
import { CREATE_SLOTS, RENDER_LIST, WITH_CTX } from '../runtimeHelpers'
|
||||||
import { createForLoopParams, finalizeForParseResult } from './vFor'
|
import { createForLoopParams, finalizeForParseResult } from './vFor'
|
||||||
|
@ -222,7 +224,7 @@ export function buildSlots(
|
||||||
let prev
|
let prev
|
||||||
while (j--) {
|
while (j--) {
|
||||||
prev = children[j]
|
prev = children[j]
|
||||||
if (prev.type !== NodeTypes.COMMENT && isNonWhitespaceContent(prev)) {
|
if (!isCommentOrWhitespace(prev)) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,7 +321,7 @@ export function buildSlots(
|
||||||
// #3766
|
// #3766
|
||||||
// with whitespace: 'preserve', whitespaces between slots will end up in
|
// with whitespace: 'preserve', whitespaces between slots will end up in
|
||||||
// implicitDefaultChildren. Ignore if all implicit children are whitespaces.
|
// implicitDefaultChildren. Ignore if all implicit children are whitespaces.
|
||||||
implicitDefaultChildren.some(node => isNonWhitespaceContent(node))
|
!implicitDefaultChildren.every(isWhitespaceText)
|
||||||
) {
|
) {
|
||||||
// implicit default slot (mixed with named slots)
|
// implicit default slot (mixed with named slots)
|
||||||
if (hasNamedDefaultSlot) {
|
if (hasNamedDefaultSlot) {
|
||||||
|
@ -411,11 +413,3 @@ function hasForwardedSlots(children: TemplateChildNode[]): boolean {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNonWhitespaceContent(node: TemplateChildNode): boolean {
|
|
||||||
if (node.type !== NodeTypes.TEXT && node.type !== NodeTypes.TEXT_CALL)
|
|
||||||
return true
|
|
||||||
return node.type === NodeTypes.TEXT
|
|
||||||
? !!node.content.trim()
|
|
||||||
: isNonWhitespaceContent(node.content)
|
|
||||||
}
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ import type { PropsExpression } from './transforms/transformElement'
|
||||||
import { parseExpression } from '@babel/parser'
|
import { parseExpression } from '@babel/parser'
|
||||||
import type { Expression, Node } from '@babel/types'
|
import type { Expression, Node } from '@babel/types'
|
||||||
import { unwrapTSNode } from './babelUtils'
|
import { unwrapTSNode } from './babelUtils'
|
||||||
|
import { isWhitespace } from './tokenizer'
|
||||||
|
|
||||||
export const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
|
export const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
|
||||||
p.type === NodeTypes.SIMPLE_EXPRESSION && p.isStatic
|
p.type === NodeTypes.SIMPLE_EXPRESSION && p.isStatic
|
||||||
|
@ -564,3 +565,23 @@ export function getMemoedVNodeCall(
|
||||||
}
|
}
|
||||||
|
|
||||||
export const forAliasRE: RegExp = /([\s\S]*?)\s+(?:in|of)\s+(\S[\s\S]*)/
|
export const forAliasRE: RegExp = /([\s\S]*?)\s+(?:in|of)\s+(\S[\s\S]*)/
|
||||||
|
|
||||||
|
export function isAllWhitespace(str: string): boolean {
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
if (!isWhitespace(str.charCodeAt(i))) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWhitespaceText(node: TemplateChildNode): boolean {
|
||||||
|
return (
|
||||||
|
(node.type === NodeTypes.TEXT && isAllWhitespace(node.content)) ||
|
||||||
|
(node.type === NodeTypes.TEXT_CALL && isWhitespaceText(node.content))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCommentOrWhitespace(node: TemplateChildNode): boolean {
|
||||||
|
return node.type === NodeTypes.COMMENT || isWhitespaceText(node)
|
||||||
|
}
|
||||||
|
|
|
@ -135,6 +135,18 @@ describe('Transition multi children warnings', () => {
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('non-breaking spaces are treated as normal text', () => {
|
||||||
|
checkWarning(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
\u00a0
|
||||||
|
<div>foo</div>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('inject persisted when child has v-show', () => {
|
test('inject persisted when child has v-show', () => {
|
||||||
|
@ -164,3 +176,19 @@ test('the v-if/else-if/else branches in Transition should ignore comments', () =
|
||||||
`).code,
|
`).code,
|
||||||
).toMatchSnapshot()
|
).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('comments and preserved whitespace are ignored', () => {
|
||||||
|
expect(
|
||||||
|
compile(
|
||||||
|
`
|
||||||
|
<transition>
|
||||||
|
<!-- foo --> <!-- bar -->
|
||||||
|
<div>foo bar</div>
|
||||||
|
</transition>
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
whitespace: 'preserve',
|
||||||
|
},
|
||||||
|
).code,
|
||||||
|
).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
|
@ -1,5 +1,22 @@
|
||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`comments and preserved whitespace are ignored 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
with (_ctx) {
|
||||||
|
const { createCommentVNode: _createCommentVNode, createElementVNode: _createElementVNode, Transition: _Transition, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(_Transition, null, {
|
||||||
|
default: _withCtx(() => [
|
||||||
|
_createElementVNode("div", null, "foo bar")
|
||||||
|
]),
|
||||||
|
_: 1 /* STABLE */
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`inject persisted when child has v-show 1`] = `
|
exports[`inject persisted when child has v-show 1`] = `
|
||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
type IfBranchNode,
|
type IfBranchNode,
|
||||||
type NodeTransform,
|
type NodeTransform,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
|
isCommentOrWhitespace,
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import { TRANSITION } from '../runtimeHelpers'
|
import { TRANSITION } from '../runtimeHelpers'
|
||||||
import { DOMErrorCodes, createDOMCompilerError } from '../errors'
|
import { DOMErrorCodes, createDOMCompilerError } from '../errors'
|
||||||
|
@ -56,11 +57,9 @@ export const transformTransition: NodeTransform = (node, context) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasMultipleChildren(node: ComponentNode | IfBranchNode): boolean {
|
function hasMultipleChildren(node: ComponentNode | IfBranchNode): boolean {
|
||||||
// #1352 filter out potential comment nodes.
|
// filter out potential comment nodes (#1352) and whitespace (#4637)
|
||||||
const children = (node.children = node.children.filter(
|
const children = (node.children = node.children.filter(
|
||||||
c =>
|
c => !isCommentOrWhitespace(c),
|
||||||
c.type !== NodeTypes.COMMENT &&
|
|
||||||
!(c.type === NodeTypes.TEXT && !c.content.trim()),
|
|
||||||
))
|
))
|
||||||
const child = children[0]
|
const child = children[0]
|
||||||
return (
|
return (
|
||||||
|
|
Loading…
Reference in New Issue