Merge ac390a6d95
into 5f8314cb7f
This commit is contained in:
commit
f8b4f289fc
|
@ -45,6 +45,20 @@ return function render(_ctx, _cache) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: v-for > codegen > no whitespace around (in|of) with basic v-for 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
with (_ctx) {
|
||||||
|
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList([items], (item) => {
|
||||||
|
return (_openBlock(), _createElementBlock("span"))
|
||||||
|
}), 256 /* UNKEYED_FRAGMENT */))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: v-for > codegen > skipped key 1`] = `
|
exports[`compiler: v-for > codegen > skipped key 1`] = `
|
||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,20 @@ return function render(_ctx, _cache) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: v-for > codegen > no whitespace around (in|of) with basic v-for 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
with (_ctx) {
|
||||||
|
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList([items], (item) => {
|
||||||
|
return (_openBlock(), _createElementBlock("span"))
|
||||||
|
}), 256 /* UNKEYED_FRAGMENT */))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: v-for > codegen > skipped key 1`] = `
|
exports[`compiler: v-for > codegen > skipped key 1`] = `
|
||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,43 @@ describe('compiler: v-for', () => {
|
||||||
"state ['my items']",
|
"state ['my items']",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('no whitespace around (in|of) with simple expression', () => {
|
||||||
|
const { node: forNode } = parseWithForTransform(
|
||||||
|
'<span v-for="(item)in[items]" />',
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('item')
|
||||||
|
expect((forNode.source as SimpleExpressionNode).content).toBe('[items]')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('no whitespace around (in|of) with object de-structured value', () => {
|
||||||
|
const { node: forNode } = parseWithForTransform(
|
||||||
|
'<span v-for="{ id, value }in[item]" />',
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe(
|
||||||
|
'{ id, value }',
|
||||||
|
)
|
||||||
|
expect((forNode.source as SimpleExpressionNode).content).toBe('[item]')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('no whitespace around (in|of) with array de-structured value', () => {
|
||||||
|
const { node: forNode } = parseWithForTransform(
|
||||||
|
'<span v-for="[ id ]in[item]" />',
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe(
|
||||||
|
'[ id ]',
|
||||||
|
)
|
||||||
|
expect((forNode.source as SimpleExpressionNode).content).toBe('[item]')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('errors', () => {
|
describe('errors', () => {
|
||||||
|
@ -257,6 +294,18 @@ describe('compiler: v-for', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('invalid expression containing (in|of)', () => {
|
||||||
|
const onError = vi.fn()
|
||||||
|
parseWithForTransform('<span v-for="fooinbar" />', { onError })
|
||||||
|
|
||||||
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: ErrorCodes.X_V_FOR_MALFORMED_EXPRESSION,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test('missing source', () => {
|
test('missing source', () => {
|
||||||
const onError = vi.fn()
|
const onError = vi.fn()
|
||||||
parseWithForTransform('<span v-for="item in" />', { onError })
|
parseWithForTransform('<span v-for="item in" />', { onError })
|
||||||
|
@ -293,6 +342,18 @@ describe('compiler: v-for', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('missing source and value', () => {
|
||||||
|
const onError = vi.fn()
|
||||||
|
parseWithForTransform('<span v-for=" in " />', { onError })
|
||||||
|
|
||||||
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: ErrorCodes.X_V_FOR_MALFORMED_EXPRESSION,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test('<template v-for> key placement', () => {
|
test('<template v-for> key placement', () => {
|
||||||
const onError = vi.fn()
|
const onError = vi.fn()
|
||||||
parseWithForTransform(
|
parseWithForTransform(
|
||||||
|
@ -437,6 +498,48 @@ describe('compiler: v-for', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('no whitespace around (in|of) with bracketed value, key, index', () => {
|
||||||
|
const source = '<span v-for="( item, key, index )in[items]" />'
|
||||||
|
const { node: forNode } = parseWithForTransform(source)
|
||||||
|
|
||||||
|
const itemOffset = source.indexOf('item')
|
||||||
|
const value = forNode.valueAlias as SimpleExpressionNode
|
||||||
|
expect(value.content).toBe('item')
|
||||||
|
expect(value.loc.start.offset).toBe(itemOffset)
|
||||||
|
expect(value.loc.start.line).toBe(1)
|
||||||
|
expect(value.loc.start.column).toBe(itemOffset + 1)
|
||||||
|
expect(value.loc.end.line).toBe(1)
|
||||||
|
expect(value.loc.end.column).toBe(itemOffset + 1 + `item`.length)
|
||||||
|
|
||||||
|
const keyOffset = source.indexOf('key')
|
||||||
|
const key = forNode.keyAlias as SimpleExpressionNode
|
||||||
|
expect(key.content).toBe('key')
|
||||||
|
expect(key.loc.start.offset).toBe(keyOffset)
|
||||||
|
expect(key.loc.start.line).toBe(1)
|
||||||
|
expect(key.loc.start.column).toBe(keyOffset + 1)
|
||||||
|
expect(key.loc.end.line).toBe(1)
|
||||||
|
expect(key.loc.end.column).toBe(keyOffset + 1 + `key`.length)
|
||||||
|
|
||||||
|
const indexOffset = source.indexOf('index')
|
||||||
|
const index = forNode.objectIndexAlias as SimpleExpressionNode
|
||||||
|
expect(index.content).toBe('index')
|
||||||
|
expect(index.loc.start.offset).toBe(indexOffset)
|
||||||
|
expect(index.loc.start.line).toBe(1)
|
||||||
|
expect(index.loc.start.column).toBe(indexOffset + 1)
|
||||||
|
expect(index.loc.end.line).toBe(1)
|
||||||
|
expect(index.loc.end.column).toBe(indexOffset + 1 + `index`.length)
|
||||||
|
|
||||||
|
const itemsOffset = source.indexOf('[items]')
|
||||||
|
expect((forNode.source as SimpleExpressionNode).content).toBe('[items]')
|
||||||
|
expect(forNode.source.loc.start.offset).toBe(itemsOffset)
|
||||||
|
expect(forNode.source.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.start.column).toBe(itemsOffset + 1)
|
||||||
|
expect(forNode.source.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.end.column).toBe(
|
||||||
|
itemsOffset + 1 + `[items]`.length,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test('skipped key', () => {
|
test('skipped key', () => {
|
||||||
const source = '<span v-for="( item,, index ) in items" />'
|
const source = '<span v-for="( item,, index ) in items" />'
|
||||||
const { node: forNode } = parseWithForTransform(source)
|
const { node: forNode } = parseWithForTransform(source)
|
||||||
|
@ -747,6 +850,21 @@ describe('compiler: v-for', () => {
|
||||||
expect(generate(root).code).toMatchSnapshot()
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('no whitespace around (in|of) with basic v-for', () => {
|
||||||
|
const {
|
||||||
|
root,
|
||||||
|
node: { codegenNode },
|
||||||
|
} = parseWithForTransform('<span v-for="(item)in[items]" />')
|
||||||
|
expect(assertSharedCodegen(codegenNode)).toMatchObject({
|
||||||
|
source: { content: `[items]` },
|
||||||
|
params: [{ content: `item` }],
|
||||||
|
innerVNodeCall: {
|
||||||
|
tag: `"span"`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
test('value + key + index', () => {
|
test('value + key + index', () => {
|
||||||
const {
|
const {
|
||||||
root,
|
root,
|
||||||
|
|
|
@ -491,15 +491,25 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
||||||
const stripParensRE = /^\(|\)$/g
|
const stripParensRE = /^\(|\)$/g
|
||||||
|
|
||||||
|
function matchForAlias(exp: string) {
|
||||||
|
const inMatch = exp.match(forAliasRE)
|
||||||
|
if (!inMatch) return
|
||||||
|
|
||||||
|
const LHS = inMatch[1].trim()
|
||||||
|
const RHS = inMatch[2].trim()
|
||||||
|
|
||||||
|
if (LHS && RHS) return { LHS, RHS }
|
||||||
|
}
|
||||||
|
|
||||||
function parseForExpression(
|
function parseForExpression(
|
||||||
input: SimpleExpressionNode,
|
input: SimpleExpressionNode,
|
||||||
): ForParseResult | undefined {
|
): ForParseResult | undefined {
|
||||||
const loc = input.loc
|
const loc = input.loc
|
||||||
const exp = input.content
|
const exp = input.content
|
||||||
const inMatch = exp.match(forAliasRE)
|
const inMatch = matchForAlias(exp)
|
||||||
if (!inMatch) return
|
if (!inMatch) return
|
||||||
|
|
||||||
const [, LHS, RHS] = inMatch
|
const { LHS, RHS } = inMatch
|
||||||
|
|
||||||
const createAliasExpression = (
|
const createAliasExpression = (
|
||||||
content: string,
|
content: string,
|
||||||
|
@ -518,14 +528,14 @@ function parseForExpression(
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: ForParseResult = {
|
const result: ForParseResult = {
|
||||||
source: createAliasExpression(RHS.trim(), exp.indexOf(RHS, LHS.length)),
|
source: createAliasExpression(RHS, exp.indexOf(RHS, LHS.length)),
|
||||||
value: undefined,
|
value: undefined,
|
||||||
key: undefined,
|
key: undefined,
|
||||||
index: undefined,
|
index: undefined,
|
||||||
finalized: false,
|
finalized: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
let valueContent = LHS.trim().replace(stripParensRE, '').trim()
|
let valueContent = LHS.replace(stripParensRE, '').trim()
|
||||||
const trimmedOffset = LHS.indexOf(valueContent)
|
const trimmedOffset = LHS.indexOf(valueContent)
|
||||||
|
|
||||||
const iteratorMatch = valueContent.match(forIteratorRE)
|
const iteratorMatch = valueContent.match(forIteratorRE)
|
||||||
|
|
|
@ -563,4 +563,5 @@ 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]*)/
|
||||||
|
|
Loading…
Reference in New Issue