feat(compiler): handle complex destructure expressions in v-for
This commit is contained in:
parent
798a9cbe9b
commit
389a07835c
@ -4,13 +4,11 @@ import {
|
|||||||
ElementNode,
|
ElementNode,
|
||||||
DirectiveNode,
|
DirectiveNode,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
ForNode,
|
|
||||||
CompilerOptions,
|
CompilerOptions,
|
||||||
IfNode,
|
IfNode,
|
||||||
InterpolationNode
|
InterpolationNode
|
||||||
} from '../../src'
|
} from '../../src'
|
||||||
import { transformIf } from '../../src/transforms/vIf'
|
import { transformIf } from '../../src/transforms/vIf'
|
||||||
import { transformFor } from '../../src/transforms/vFor'
|
|
||||||
import { transformExpression } from '../../src/transforms/transformExpression'
|
import { transformExpression } from '../../src/transforms/transformExpression'
|
||||||
|
|
||||||
function parseWithExpressionTransform(
|
function parseWithExpressionTransform(
|
||||||
@ -20,7 +18,7 @@ function parseWithExpressionTransform(
|
|||||||
const ast = parse(template)
|
const ast = parse(template)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
prefixIdentifiers: true,
|
prefixIdentifiers: true,
|
||||||
nodeTransforms: [transformIf, transformFor, transformExpression],
|
nodeTransforms: [transformIf, transformExpression],
|
||||||
...options
|
...options
|
||||||
})
|
})
|
||||||
return ast.children[0]
|
return ast.children[0]
|
||||||
@ -169,103 +167,6 @@ describe('compiler: expression transform', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should prefix v-for source', () => {
|
|
||||||
const node = parseWithExpressionTransform(
|
|
||||||
`<div v-for="i in list"/>`
|
|
||||||
) as ForNode
|
|
||||||
expect(node.source).toMatchObject({
|
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
||||||
content: `_ctx.list`
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should prefix v-for source w/ complex expression', () => {
|
|
||||||
const node = parseWithExpressionTransform(
|
|
||||||
`<div v-for="i in list.concat([foo])"/>`
|
|
||||||
) as ForNode
|
|
||||||
expect(node.source).toMatchObject({
|
|
||||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
|
||||||
children: [
|
|
||||||
{ content: `_ctx.list` },
|
|
||||||
`.`,
|
|
||||||
{ content: `concat` },
|
|
||||||
`([`,
|
|
||||||
{ content: `_ctx.foo` },
|
|
||||||
`])`
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should not prefix v-for alias', () => {
|
|
||||||
const node = parseWithExpressionTransform(
|
|
||||||
`<div v-for="i in list">{{ i }}{{ j }}</div>`
|
|
||||||
) as ForNode
|
|
||||||
const div = node.children[0] as ElementNode
|
|
||||||
expect((div.children[0] as InterpolationNode).content).toMatchObject({
|
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
||||||
content: `i`
|
|
||||||
})
|
|
||||||
expect((div.children[1] as InterpolationNode).content).toMatchObject({
|
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
||||||
content: `_ctx.j`
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should not prefix v-for aliases (multiple)', () => {
|
|
||||||
const node = parseWithExpressionTransform(
|
|
||||||
`<div v-for="(i, j, k) in list">{{ i + j + k }}{{ l }}</div>`
|
|
||||||
) as ForNode
|
|
||||||
const div = node.children[0] as ElementNode
|
|
||||||
expect((div.children[0] as InterpolationNode).content).toMatchObject({
|
|
||||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
|
||||||
children: [
|
|
||||||
{ content: `i` },
|
|
||||||
` + `,
|
|
||||||
{ content: `j` },
|
|
||||||
` + `,
|
|
||||||
{ content: `k` }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
expect((div.children[1] as InterpolationNode).content).toMatchObject({
|
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
||||||
content: `_ctx.l`
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should prefix id outside of v-for', () => {
|
|
||||||
const node = parseWithExpressionTransform(
|
|
||||||
`<div><div v-for="i in list" />{{ i }}</div>`
|
|
||||||
) as ElementNode
|
|
||||||
expect((node.children[1] as InterpolationNode).content).toMatchObject({
|
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
||||||
content: `_ctx.i`
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('nested v-for', () => {
|
|
||||||
const node = parseWithExpressionTransform(
|
|
||||||
`<div v-for="i in list">
|
|
||||||
<div v-for="i in list">{{ i + j }}</div>{{ i }}
|
|
||||||
</div>`
|
|
||||||
) as ForNode
|
|
||||||
const outerDiv = node.children[0] as ElementNode
|
|
||||||
const innerFor = outerDiv.children[0] as ForNode
|
|
||||||
const innerExp = (innerFor.children[0] as ElementNode)
|
|
||||||
.children[0] as InterpolationNode
|
|
||||||
expect(innerExp.content).toMatchObject({
|
|
||||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
|
||||||
children: [{ content: 'i' }, ` + `, { content: `_ctx.j` }]
|
|
||||||
})
|
|
||||||
|
|
||||||
// when an inner v-for shadows a variable of an outer v-for and exit,
|
|
||||||
// it should not cause the outer v-for's alias to be removed from known ids
|
|
||||||
const outerExp = outerDiv.children[1] as InterpolationNode
|
|
||||||
expect(outerExp.content).toMatchObject({
|
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
||||||
content: `i`
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should not prefix whitelisted globals', () => {
|
test('should not prefix whitelisted globals', () => {
|
||||||
const node = parseWithExpressionTransform(
|
const node = parseWithExpressionTransform(
|
||||||
`{{ Math.max(1, 2) }}`
|
`{{ Math.max(1, 2) }}`
|
||||||
@ -334,7 +235,9 @@ describe('compiler: expression transform', () => {
|
|||||||
expect(node.content).toMatchObject({
|
expect(node.content).toMatchObject({
|
||||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
children: [
|
children: [
|
||||||
`({ foo }) => `,
|
`({ `,
|
||||||
|
{ content: `foo` },
|
||||||
|
` }) => `,
|
||||||
{ content: `foo` },
|
{ content: `foo` },
|
||||||
` + `,
|
` + `,
|
||||||
{ content: `_ctx.bar` }
|
{ content: `_ctx.bar` }
|
||||||
|
@ -1,148 +1,185 @@
|
|||||||
import { parse } from '../../src/parse'
|
import { parse } from '../../src/parse'
|
||||||
import { transform } from '../../src/transform'
|
import { transform } from '../../src/transform'
|
||||||
import { transformFor } from '../../src/transforms/vFor'
|
import { transformFor } from '../../src/transforms/vFor'
|
||||||
import { ForNode, NodeTypes, SimpleExpressionNode } from '../../src/ast'
|
import {
|
||||||
|
ForNode,
|
||||||
|
NodeTypes,
|
||||||
|
SimpleExpressionNode,
|
||||||
|
ElementNode,
|
||||||
|
InterpolationNode
|
||||||
|
} from '../../src/ast'
|
||||||
import { ErrorCodes } from '../../src/errors'
|
import { ErrorCodes } from '../../src/errors'
|
||||||
import { CompilerOptions } from '../../src'
|
import { CompilerOptions } from '../../src'
|
||||||
|
import { transformExpression } from '../../src/transforms/transformExpression'
|
||||||
|
|
||||||
function parseWithForTransform(
|
function parseWithForTransform(
|
||||||
template: string,
|
template: string,
|
||||||
options: CompilerOptions = {}
|
options: CompilerOptions = {}
|
||||||
): ForNode {
|
) {
|
||||||
const node = parse(template, options)
|
const node = parse(template, options)
|
||||||
transform(node, { nodeTransforms: [transformFor], ...options })
|
transform(node, {
|
||||||
if (!options.onError) {
|
nodeTransforms: [
|
||||||
expect(node.children.length).toBe(1)
|
transformFor,
|
||||||
expect(node.children[0].type).toBe(NodeTypes.FOR)
|
...(options.prefixIdentifiers ? [transformExpression] : [])
|
||||||
}
|
],
|
||||||
return node.children[0] as ForNode
|
...options
|
||||||
|
})
|
||||||
|
return node.children[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('compiler: transform v-for', () => {
|
describe('compiler: transform v-for', () => {
|
||||||
test('number expression', () => {
|
test('number expression', () => {
|
||||||
const forNode = parseWithForTransform('<span v-for="index in 5" />')
|
const forNode = parseWithForTransform(
|
||||||
|
'<span v-for="index in 5" />'
|
||||||
|
) as ForNode
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
expect(forNode.valueAlias!.content).toBe('index')
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('index')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('5')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('5')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('value', () => {
|
test('value', () => {
|
||||||
const forNode = parseWithForTransform('<span v-for="(item) in items" />')
|
const forNode = parseWithForTransform(
|
||||||
|
'<span v-for="(item) in items" />'
|
||||||
|
) as ForNode
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('item')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('object de-structured value', () => {
|
test('object de-structured value', () => {
|
||||||
const forNode = parseWithForTransform(
|
const forNode = parseWithForTransform(
|
||||||
'<span v-for="({ id, value }) in items" />'
|
'<span v-for="({ id, value }) in items" />'
|
||||||
)
|
) as ForNode
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
expect(forNode.valueAlias!.content).toBe('{ id, value }')
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe(
|
||||||
|
'{ id, value }'
|
||||||
|
)
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('array de-structured value', () => {
|
test('array de-structured value', () => {
|
||||||
const forNode = parseWithForTransform(
|
const forNode = parseWithForTransform(
|
||||||
'<span v-for="([ id, value ]) in items" />'
|
'<span v-for="([ id, value ]) in items" />'
|
||||||
)
|
) as ForNode
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
expect(forNode.valueAlias!.content).toBe('[ id, value ]')
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe(
|
||||||
|
'[ id, value ]'
|
||||||
|
)
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('value and key', () => {
|
test('value and key', () => {
|
||||||
const forNode = parseWithForTransform(
|
const forNode = parseWithForTransform(
|
||||||
'<span v-for="(item, key) in items" />'
|
'<span v-for="(item, key) in items" />'
|
||||||
)
|
) as ForNode
|
||||||
expect(forNode.keyAlias).not.toBeUndefined()
|
expect(forNode.keyAlias).not.toBeUndefined()
|
||||||
expect(forNode.keyAlias!.content).toBe('key')
|
expect((forNode.keyAlias as SimpleExpressionNode).content).toBe('key')
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('item')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('value, key and index', () => {
|
test('value, key and index', () => {
|
||||||
const forNode = parseWithForTransform(
|
const forNode = parseWithForTransform(
|
||||||
'<span v-for="(value, key, index) in items" />'
|
'<span v-for="(value, key, index) in items" />'
|
||||||
)
|
) as ForNode
|
||||||
expect(forNode.keyAlias).not.toBeUndefined()
|
expect(forNode.keyAlias).not.toBeUndefined()
|
||||||
expect(forNode.keyAlias!.content).toBe('key')
|
expect((forNode.keyAlias as SimpleExpressionNode).content).toBe('key')
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
expect((forNode.objectIndexAlias as SimpleExpressionNode).content).toBe(
|
||||||
expect(forNode.valueAlias!.content).toBe('value')
|
'index'
|
||||||
|
)
|
||||||
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('value')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('skipped key', () => {
|
test('skipped key', () => {
|
||||||
const forNode = parseWithForTransform(
|
const forNode = parseWithForTransform(
|
||||||
'<span v-for="(value,,index) in items" />'
|
'<span v-for="(value,,index) in items" />'
|
||||||
)
|
) as ForNode
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
expect((forNode.objectIndexAlias as SimpleExpressionNode).content).toBe(
|
||||||
expect(forNode.valueAlias!.content).toBe('value')
|
'index'
|
||||||
|
)
|
||||||
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('value')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('skipped value and key', () => {
|
test('skipped value and key', () => {
|
||||||
const forNode = parseWithForTransform('<span v-for="(,,index) in items" />')
|
const forNode = parseWithForTransform(
|
||||||
|
'<span v-for="(,,index) in items" />'
|
||||||
|
) as ForNode
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
expect((forNode.objectIndexAlias as SimpleExpressionNode).content).toBe(
|
||||||
|
'index'
|
||||||
|
)
|
||||||
expect(forNode.valueAlias).toBeUndefined()
|
expect(forNode.valueAlias).toBeUndefined()
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('unbracketed value', () => {
|
test('unbracketed value', () => {
|
||||||
const forNode = parseWithForTransform('<span v-for="item in items" />')
|
const forNode = parseWithForTransform(
|
||||||
|
'<span v-for="item in items" />'
|
||||||
|
) as ForNode
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('item')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('unbracketed value and key', () => {
|
test('unbracketed value and key', () => {
|
||||||
const forNode = parseWithForTransform('<span v-for="item, key in items" />')
|
const forNode = parseWithForTransform(
|
||||||
|
'<span v-for="item, key in items" />'
|
||||||
|
) as ForNode
|
||||||
expect(forNode.keyAlias).not.toBeUndefined()
|
expect(forNode.keyAlias).not.toBeUndefined()
|
||||||
expect(forNode.keyAlias!.content).toBe('key')
|
expect((forNode.keyAlias as SimpleExpressionNode).content).toBe('key')
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('item')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('unbracketed value, key and index', () => {
|
test('unbracketed value, key and index', () => {
|
||||||
const forNode = parseWithForTransform(
|
const forNode = parseWithForTransform(
|
||||||
'<span v-for="value, key, index in items" />'
|
'<span v-for="value, key, index in items" />'
|
||||||
)
|
) as ForNode
|
||||||
expect(forNode.keyAlias).not.toBeUndefined()
|
expect(forNode.keyAlias).not.toBeUndefined()
|
||||||
expect(forNode.keyAlias!.content).toBe('key')
|
expect((forNode.keyAlias as SimpleExpressionNode).content).toBe('key')
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
expect((forNode.objectIndexAlias as SimpleExpressionNode).content).toBe(
|
||||||
expect(forNode.valueAlias!.content).toBe('value')
|
'index'
|
||||||
|
)
|
||||||
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('value')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('unbracketed skipped key', () => {
|
test('unbracketed skipped key', () => {
|
||||||
const forNode = parseWithForTransform(
|
const forNode = parseWithForTransform(
|
||||||
'<span v-for="value, , index in items" />'
|
'<span v-for="value, , index in items" />'
|
||||||
)
|
) as ForNode
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
expect((forNode.objectIndexAlias as SimpleExpressionNode).content).toBe(
|
||||||
expect(forNode.valueAlias!.content).toBe('value')
|
'index'
|
||||||
|
)
|
||||||
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('value')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('unbracketed skipped value and key', () => {
|
test('unbracketed skipped value and key', () => {
|
||||||
const forNode = parseWithForTransform('<span v-for=", , index in items" />')
|
const forNode = parseWithForTransform(
|
||||||
|
'<span v-for=", , index in items" />'
|
||||||
|
) as ForNode
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
expect((forNode.objectIndexAlias as SimpleExpressionNode).content).toBe(
|
||||||
|
'index'
|
||||||
|
)
|
||||||
expect(forNode.valueAlias).toBeUndefined()
|
expect(forNode.valueAlias).toBeUndefined()
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
})
|
})
|
||||||
@ -210,17 +247,16 @@ describe('compiler: transform v-for', () => {
|
|||||||
describe('source location', () => {
|
describe('source location', () => {
|
||||||
test('value & source', () => {
|
test('value & source', () => {
|
||||||
const source = '<span v-for="item in items" />'
|
const source = '<span v-for="item in items" />'
|
||||||
const forNode = parseWithForTransform(source)
|
const forNode = parseWithForTransform(source) as ForNode
|
||||||
|
|
||||||
const itemOffset = source.indexOf('item')
|
const itemOffset = source.indexOf('item')
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
const value = forNode.valueAlias as SimpleExpressionNode
|
||||||
expect(forNode.valueAlias!.loc.start.offset).toBe(itemOffset)
|
expect((forNode.valueAlias as SimpleExpressionNode).content).toBe('item')
|
||||||
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
expect(value.loc.start.offset).toBe(itemOffset)
|
||||||
expect(forNode.valueAlias!.loc.start.column).toBe(itemOffset + 1)
|
expect(value.loc.start.line).toBe(1)
|
||||||
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
expect(value.loc.start.column).toBe(itemOffset + 1)
|
||||||
expect(forNode.valueAlias!.loc.end.column).toBe(
|
expect(value.loc.end.line).toBe(1)
|
||||||
itemOffset + 1 + `item`.length
|
expect(value.loc.end.column).toBe(itemOffset + 1 + `item`.length)
|
||||||
)
|
|
||||||
|
|
||||||
const itemsOffset = source.indexOf('items')
|
const itemsOffset = source.indexOf('items')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
@ -235,17 +271,16 @@ describe('compiler: transform v-for', () => {
|
|||||||
|
|
||||||
test('bracketed value', () => {
|
test('bracketed value', () => {
|
||||||
const source = '<span v-for="( item ) in items" />'
|
const source = '<span v-for="( item ) in items" />'
|
||||||
const forNode = parseWithForTransform(source)
|
const forNode = parseWithForTransform(source) as ForNode
|
||||||
|
|
||||||
const itemOffset = source.indexOf('item')
|
const itemOffset = source.indexOf('item')
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
const value = forNode.valueAlias as SimpleExpressionNode
|
||||||
expect(forNode.valueAlias!.loc.start.offset).toBe(itemOffset)
|
expect(value.content).toBe('item')
|
||||||
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
expect(value.loc.start.offset).toBe(itemOffset)
|
||||||
expect(forNode.valueAlias!.loc.start.column).toBe(itemOffset + 1)
|
expect(value.loc.start.line).toBe(1)
|
||||||
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
expect(value.loc.start.column).toBe(itemOffset + 1)
|
||||||
expect(forNode.valueAlias!.loc.end.column).toBe(
|
expect(value.loc.end.line).toBe(1)
|
||||||
itemOffset + 1 + `item`.length
|
expect(value.loc.end.column).toBe(itemOffset + 1 + `item`.length)
|
||||||
)
|
|
||||||
|
|
||||||
const itemsOffset = source.indexOf('items')
|
const itemsOffset = source.indexOf('items')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
@ -260,17 +295,16 @@ describe('compiler: transform v-for', () => {
|
|||||||
|
|
||||||
test('de-structured value', () => {
|
test('de-structured value', () => {
|
||||||
const source = '<span v-for="( { id, key }) in items" />'
|
const source = '<span v-for="( { id, key }) in items" />'
|
||||||
const forNode = parseWithForTransform(source)
|
const forNode = parseWithForTransform(source) as ForNode
|
||||||
|
|
||||||
|
const value = forNode.valueAlias as SimpleExpressionNode
|
||||||
const valueIndex = source.indexOf('{ id, key }')
|
const valueIndex = source.indexOf('{ id, key }')
|
||||||
expect(forNode.valueAlias!.content).toBe('{ id, key }')
|
expect(value.content).toBe('{ id, key }')
|
||||||
expect(forNode.valueAlias!.loc.start.offset).toBe(valueIndex)
|
expect(value.loc.start.offset).toBe(valueIndex)
|
||||||
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
expect(value.loc.start.line).toBe(1)
|
||||||
expect(forNode.valueAlias!.loc.start.column).toBe(valueIndex + 1)
|
expect(value.loc.start.column).toBe(valueIndex + 1)
|
||||||
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
expect(value.loc.end.line).toBe(1)
|
||||||
expect(forNode.valueAlias!.loc.end.column).toBe(
|
expect(value.loc.end.column).toBe(valueIndex + 1 + '{ id, key }'.length)
|
||||||
valueIndex + 1 + '{ id, key }'.length
|
|
||||||
)
|
|
||||||
|
|
||||||
const itemsOffset = source.indexOf('items')
|
const itemsOffset = source.indexOf('items')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
@ -285,37 +319,34 @@ describe('compiler: transform v-for', () => {
|
|||||||
|
|
||||||
test('bracketed value, key, index', () => {
|
test('bracketed value, key, index', () => {
|
||||||
const source = '<span v-for="( item, key, index ) in items" />'
|
const source = '<span v-for="( item, key, index ) in items" />'
|
||||||
const forNode = parseWithForTransform(source)
|
const forNode = parseWithForTransform(source) as ForNode
|
||||||
|
|
||||||
const itemOffset = source.indexOf('item')
|
const itemOffset = source.indexOf('item')
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
const value = forNode.valueAlias as SimpleExpressionNode
|
||||||
expect(forNode.valueAlias!.loc.start.offset).toBe(itemOffset)
|
expect(value.content).toBe('item')
|
||||||
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
expect(value.loc.start.offset).toBe(itemOffset)
|
||||||
expect(forNode.valueAlias!.loc.start.column).toBe(itemOffset + 1)
|
expect(value.loc.start.line).toBe(1)
|
||||||
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
expect(value.loc.start.column).toBe(itemOffset + 1)
|
||||||
expect(forNode.valueAlias!.loc.end.column).toBe(
|
expect(value.loc.end.line).toBe(1)
|
||||||
itemOffset + 1 + `item`.length
|
expect(value.loc.end.column).toBe(itemOffset + 1 + `item`.length)
|
||||||
)
|
|
||||||
|
|
||||||
const keyOffset = source.indexOf('key')
|
const keyOffset = source.indexOf('key')
|
||||||
expect(forNode.keyAlias!.content).toBe('key')
|
const key = forNode.keyAlias as SimpleExpressionNode
|
||||||
expect(forNode.keyAlias!.loc.start.offset).toBe(keyOffset)
|
expect(key.content).toBe('key')
|
||||||
expect(forNode.keyAlias!.loc.start.line).toBe(1)
|
expect(key.loc.start.offset).toBe(keyOffset)
|
||||||
expect(forNode.keyAlias!.loc.start.column).toBe(keyOffset + 1)
|
expect(key.loc.start.line).toBe(1)
|
||||||
expect(forNode.keyAlias!.loc.end.line).toBe(1)
|
expect(key.loc.start.column).toBe(keyOffset + 1)
|
||||||
expect(forNode.keyAlias!.loc.end.column).toBe(
|
expect(key.loc.end.line).toBe(1)
|
||||||
keyOffset + 1 + `key`.length
|
expect(key.loc.end.column).toBe(keyOffset + 1 + `key`.length)
|
||||||
)
|
|
||||||
|
|
||||||
const indexOffset = source.indexOf('index')
|
const indexOffset = source.indexOf('index')
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
const index = forNode.objectIndexAlias as SimpleExpressionNode
|
||||||
expect(forNode.objectIndexAlias!.loc.start.offset).toBe(indexOffset)
|
expect(index.content).toBe('index')
|
||||||
expect(forNode.objectIndexAlias!.loc.start.line).toBe(1)
|
expect(index.loc.start.offset).toBe(indexOffset)
|
||||||
expect(forNode.objectIndexAlias!.loc.start.column).toBe(indexOffset + 1)
|
expect(index.loc.start.line).toBe(1)
|
||||||
expect(forNode.objectIndexAlias!.loc.end.line).toBe(1)
|
expect(index.loc.start.column).toBe(indexOffset + 1)
|
||||||
expect(forNode.objectIndexAlias!.loc.end.column).toBe(
|
expect(index.loc.end.line).toBe(1)
|
||||||
indexOffset + 1 + `index`.length
|
expect(index.loc.end.column).toBe(indexOffset + 1 + `index`.length)
|
||||||
)
|
|
||||||
|
|
||||||
const itemsOffset = source.indexOf('items')
|
const itemsOffset = source.indexOf('items')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
@ -330,27 +361,25 @@ describe('compiler: transform v-for', () => {
|
|||||||
|
|
||||||
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 forNode = parseWithForTransform(source)
|
const forNode = parseWithForTransform(source) as ForNode
|
||||||
|
|
||||||
const itemOffset = source.indexOf('item')
|
const itemOffset = source.indexOf('item')
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
const value = forNode.valueAlias as SimpleExpressionNode
|
||||||
expect(forNode.valueAlias!.loc.start.offset).toBe(itemOffset)
|
expect(value.content).toBe('item')
|
||||||
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
expect(value.loc.start.offset).toBe(itemOffset)
|
||||||
expect(forNode.valueAlias!.loc.start.column).toBe(itemOffset + 1)
|
expect(value.loc.start.line).toBe(1)
|
||||||
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
expect(value.loc.start.column).toBe(itemOffset + 1)
|
||||||
expect(forNode.valueAlias!.loc.end.column).toBe(
|
expect(value.loc.end.line).toBe(1)
|
||||||
itemOffset + 1 + `item`.length
|
expect(value.loc.end.column).toBe(itemOffset + 1 + `item`.length)
|
||||||
)
|
|
||||||
|
|
||||||
const indexOffset = source.indexOf('index')
|
const indexOffset = source.indexOf('index')
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
const index = forNode.objectIndexAlias as SimpleExpressionNode
|
||||||
expect(forNode.objectIndexAlias!.loc.start.offset).toBe(indexOffset)
|
expect(index.content).toBe('index')
|
||||||
expect(forNode.objectIndexAlias!.loc.start.line).toBe(1)
|
expect(index.loc.start.offset).toBe(indexOffset)
|
||||||
expect(forNode.objectIndexAlias!.loc.start.column).toBe(indexOffset + 1)
|
expect(index.loc.start.line).toBe(1)
|
||||||
expect(forNode.objectIndexAlias!.loc.end.line).toBe(1)
|
expect(index.loc.start.column).toBe(indexOffset + 1)
|
||||||
expect(forNode.objectIndexAlias!.loc.end.column).toBe(
|
expect(index.loc.end.line).toBe(1)
|
||||||
indexOffset + 1 + `index`.length
|
expect(index.loc.end.column).toBe(indexOffset + 1 + `index`.length)
|
||||||
)
|
|
||||||
|
|
||||||
const itemsOffset = source.indexOf('items')
|
const itemsOffset = source.indexOf('items')
|
||||||
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
expect((forNode.source as SimpleExpressionNode).content).toBe('items')
|
||||||
@ -363,4 +392,146 @@ describe('compiler: transform v-for', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('prefixIdentifiers: true', () => {
|
||||||
|
test('should prefix v-for source', () => {
|
||||||
|
const node = parseWithForTransform(`<div v-for="i in list"/>`, {
|
||||||
|
prefixIdentifiers: true
|
||||||
|
}) as ForNode
|
||||||
|
expect(node.source).toMatchObject({
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `_ctx.list`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should prefix v-for source w/ complex expression', () => {
|
||||||
|
const node = parseWithForTransform(
|
||||||
|
`<div v-for="i in list.concat([foo])"/>`,
|
||||||
|
{ prefixIdentifiers: true }
|
||||||
|
) as ForNode
|
||||||
|
expect(node.source).toMatchObject({
|
||||||
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
|
children: [
|
||||||
|
{ content: `_ctx.list` },
|
||||||
|
`.`,
|
||||||
|
{ content: `concat` },
|
||||||
|
`([`,
|
||||||
|
{ content: `_ctx.foo` },
|
||||||
|
`])`
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not prefix v-for alias', () => {
|
||||||
|
const node = parseWithForTransform(
|
||||||
|
`<div v-for="i in list">{{ i }}{{ j }}</div>`,
|
||||||
|
{ prefixIdentifiers: true }
|
||||||
|
) as ForNode
|
||||||
|
const div = node.children[0] as ElementNode
|
||||||
|
expect((div.children[0] as InterpolationNode).content).toMatchObject({
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `i`
|
||||||
|
})
|
||||||
|
expect((div.children[1] as InterpolationNode).content).toMatchObject({
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `_ctx.j`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not prefix v-for aliases (multiple)', () => {
|
||||||
|
const node = parseWithForTransform(
|
||||||
|
`<div v-for="(i, j, k) in list">{{ i + j + k }}{{ l }}</div>`,
|
||||||
|
{ prefixIdentifiers: true }
|
||||||
|
) as ForNode
|
||||||
|
const div = node.children[0] as ElementNode
|
||||||
|
expect((div.children[0] as InterpolationNode).content).toMatchObject({
|
||||||
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
|
children: [
|
||||||
|
{ content: `i` },
|
||||||
|
` + `,
|
||||||
|
{ content: `j` },
|
||||||
|
` + `,
|
||||||
|
{ content: `k` }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
expect((div.children[1] as InterpolationNode).content).toMatchObject({
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `_ctx.l`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should prefix id outside of v-for', () => {
|
||||||
|
const node = parseWithForTransform(
|
||||||
|
`<div><div v-for="i in list" />{{ i }}</div>`,
|
||||||
|
{ prefixIdentifiers: true }
|
||||||
|
) as ElementNode
|
||||||
|
expect((node.children[1] as InterpolationNode).content).toMatchObject({
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `_ctx.i`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('nested v-for', () => {
|
||||||
|
const node = parseWithForTransform(
|
||||||
|
`<div v-for="i in list">
|
||||||
|
<div v-for="i in list">{{ i + j }}</div>{{ i }}
|
||||||
|
</div>`,
|
||||||
|
{ prefixIdentifiers: true }
|
||||||
|
) as ForNode
|
||||||
|
const outerDiv = node.children[0] as ElementNode
|
||||||
|
const innerFor = outerDiv.children[0] as ForNode
|
||||||
|
const innerExp = (innerFor.children[0] as ElementNode)
|
||||||
|
.children[0] as InterpolationNode
|
||||||
|
expect(innerExp.content).toMatchObject({
|
||||||
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
|
children: [{ content: 'i' }, ` + `, { content: `_ctx.j` }]
|
||||||
|
})
|
||||||
|
|
||||||
|
// when an inner v-for shadows a variable of an outer v-for and exit,
|
||||||
|
// it should not cause the outer v-for's alias to be removed from known ids
|
||||||
|
const outerExp = outerDiv.children[1] as InterpolationNode
|
||||||
|
expect(outerExp.content).toMatchObject({
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: `i`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-for aliases w/ complex expressions', () => {
|
||||||
|
const node = parseWithForTransform(
|
||||||
|
`<div v-for="({ foo = bar, baz: [qux = quux] }) in list">
|
||||||
|
{{ foo + bar + baz + qux + quux }}
|
||||||
|
</div>`,
|
||||||
|
{ prefixIdentifiers: true }
|
||||||
|
) as ForNode
|
||||||
|
expect(node.valueAlias!).toMatchObject({
|
||||||
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
|
children: [
|
||||||
|
`{ `,
|
||||||
|
{ content: `foo` },
|
||||||
|
` = `,
|
||||||
|
{ content: `_ctx.bar` },
|
||||||
|
`, baz: [`,
|
||||||
|
{ content: `qux` },
|
||||||
|
` = `,
|
||||||
|
{ content: `_ctx.quux` },
|
||||||
|
`] }`
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const div = node.children[0] as ElementNode
|
||||||
|
expect((div.children[0] as InterpolationNode).content).toMatchObject({
|
||||||
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
|
children: [
|
||||||
|
{ content: `foo` },
|
||||||
|
` + `,
|
||||||
|
{ content: `_ctx.bar` },
|
||||||
|
` + `,
|
||||||
|
{ content: `_ctx.baz` },
|
||||||
|
` + `,
|
||||||
|
{ content: `qux` },
|
||||||
|
` + `,
|
||||||
|
{ content: `_ctx.quux` }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -83,9 +83,8 @@ describe('compiler: transform component slots', () => {
|
|||||||
default: {
|
default: {
|
||||||
type: NodeTypes.JS_SLOT_FUNCTION,
|
type: NodeTypes.JS_SLOT_FUNCTION,
|
||||||
params: {
|
params: {
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
content: `{ foo }`,
|
children: [`{ `, { content: `foo` }, ` }`]
|
||||||
isStatic: false
|
|
||||||
},
|
},
|
||||||
returns: [
|
returns: [
|
||||||
{
|
{
|
||||||
@ -266,9 +265,8 @@ describe('compiler: transform component slots', () => {
|
|||||||
default: {
|
default: {
|
||||||
type: NodeTypes.JS_SLOT_FUNCTION,
|
type: NodeTypes.JS_SLOT_FUNCTION,
|
||||||
params: {
|
params: {
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
content: `{ bar }`,
|
children: [`{ `, { content: `bar` }, ` }`]
|
||||||
isStatic: false
|
|
||||||
},
|
},
|
||||||
returns: [
|
returns: [
|
||||||
{
|
{
|
||||||
|
@ -150,9 +150,9 @@ export interface IfBranchNode extends Node {
|
|||||||
export interface ForNode extends Node {
|
export interface ForNode extends Node {
|
||||||
type: NodeTypes.FOR
|
type: NodeTypes.FOR
|
||||||
source: ExpressionNode
|
source: ExpressionNode
|
||||||
valueAlias: SimpleExpressionNode | undefined
|
valueAlias: ExpressionNode | undefined
|
||||||
keyAlias: SimpleExpressionNode | undefined
|
keyAlias: ExpressionNode | undefined
|
||||||
objectIndexAlias: SimpleExpressionNode | undefined
|
objectIndexAlias: ExpressionNode | undefined
|
||||||
children: ChildNode[]
|
children: ChildNode[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,14 +509,14 @@ function genFor(node: ForNode, context: CodegenContext) {
|
|||||||
genNode(source, context)
|
genNode(source, context)
|
||||||
push(`, (`)
|
push(`, (`)
|
||||||
if (valueAlias) {
|
if (valueAlias) {
|
||||||
genExpression(valueAlias, context)
|
genNode(valueAlias, context)
|
||||||
}
|
}
|
||||||
if (keyAlias) {
|
if (keyAlias) {
|
||||||
if (!valueAlias) {
|
if (!valueAlias) {
|
||||||
push(`__value`)
|
push(`__value`)
|
||||||
}
|
}
|
||||||
push(`, `)
|
push(`, `)
|
||||||
genExpression(keyAlias, context)
|
genNode(keyAlias, context)
|
||||||
}
|
}
|
||||||
if (objectIndexAlias) {
|
if (objectIndexAlias) {
|
||||||
if (!keyAlias) {
|
if (!keyAlias) {
|
||||||
@ -527,7 +527,7 @@ function genFor(node: ForNode, context: CodegenContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
push(`, `)
|
push(`, `)
|
||||||
genExpression(objectIndexAlias, context)
|
genNode(objectIndexAlias, context)
|
||||||
}
|
}
|
||||||
push(`) => {`)
|
push(`) => {`)
|
||||||
indent()
|
indent()
|
||||||
|
@ -63,8 +63,8 @@ export interface TransformContext extends Required<TransformOptions> {
|
|||||||
replaceNode(node: ChildNode): void
|
replaceNode(node: ChildNode): void
|
||||||
removeNode(node?: ChildNode): void
|
removeNode(node?: ChildNode): void
|
||||||
onNodeRemoved: () => void
|
onNodeRemoved: () => void
|
||||||
addIdentifier(id: string): void
|
addIdentifiers(exp: ExpressionNode): void
|
||||||
removeIdentifier(id: string): void
|
removeIdentifiers(exp: ExpressionNode): void
|
||||||
hoist(exp: JSChildNode): ExpressionNode
|
hoist(exp: JSChildNode): ExpressionNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,15 +126,24 @@ function createTransformContext(
|
|||||||
context.parent.children.splice(removalIndex, 1)
|
context.parent.children.splice(removalIndex, 1)
|
||||||
},
|
},
|
||||||
onNodeRemoved: () => {},
|
onNodeRemoved: () => {},
|
||||||
addIdentifier(id) {
|
addIdentifiers(exp) {
|
||||||
const { identifiers } = context
|
// identifier tracking only happens in non-browser builds.
|
||||||
if (identifiers[id] === undefined) {
|
if (!__BROWSER__) {
|
||||||
identifiers[id] = 0
|
if (exp.identifiers) {
|
||||||
|
exp.identifiers.forEach(addId)
|
||||||
|
} else if (exp.type === NodeTypes.SIMPLE_EXPRESSION) {
|
||||||
|
addId(exp.content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
;(identifiers[id] as number)++
|
|
||||||
},
|
},
|
||||||
removeIdentifier(id) {
|
removeIdentifiers(exp) {
|
||||||
;(context.identifiers[id] as number)--
|
if (!__BROWSER__) {
|
||||||
|
if (exp.identifiers) {
|
||||||
|
exp.identifiers.forEach(removeId)
|
||||||
|
} else if (exp.type === NodeTypes.SIMPLE_EXPRESSION) {
|
||||||
|
removeId(exp.content)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
hoist(exp) {
|
hoist(exp) {
|
||||||
context.hoists.push(exp)
|
context.hoists.push(exp)
|
||||||
@ -145,6 +154,19 @@ function createTransformContext(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addId(id: string) {
|
||||||
|
const { identifiers } = context
|
||||||
|
if (identifiers[id] === undefined) {
|
||||||
|
identifiers[id] = 0
|
||||||
|
}
|
||||||
|
;(identifiers[id] as number)++
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeId(id: string) {
|
||||||
|
;(context.identifiers[id] as number)--
|
||||||
|
}
|
||||||
|
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +67,8 @@ interface PrefixMeta {
|
|||||||
export function processExpression(
|
export function processExpression(
|
||||||
node: SimpleExpressionNode,
|
node: SimpleExpressionNode,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
|
// some expressions like v-slot props & v-for aliases should be parsed as
|
||||||
|
// function params
|
||||||
asParams: boolean = false
|
asParams: boolean = false
|
||||||
): ExpressionNode {
|
): ExpressionNode {
|
||||||
if (!context.prefixIdentifiers) {
|
if (!context.prefixIdentifiers) {
|
||||||
@ -75,7 +77,7 @@ export function processExpression(
|
|||||||
|
|
||||||
// fast path if expression is a simple identifier.
|
// fast path if expression is a simple identifier.
|
||||||
if (isSimpleIdentifier(node.content)) {
|
if (isSimpleIdentifier(node.content)) {
|
||||||
if (!context.identifiers[node.content]) {
|
if (!asParams && !context.identifiers[node.content]) {
|
||||||
node.content = `_ctx.${node.content}`
|
node.content = `_ctx.${node.content}`
|
||||||
}
|
}
|
||||||
return node
|
return node
|
||||||
@ -107,17 +109,14 @@ export function processExpression(
|
|||||||
if (node.type === 'Identifier') {
|
if (node.type === 'Identifier') {
|
||||||
if (!ids.includes(node)) {
|
if (!ids.includes(node)) {
|
||||||
if (!knownIds[node.name] && shouldPrefix(node, parent)) {
|
if (!knownIds[node.name] && shouldPrefix(node, parent)) {
|
||||||
if (
|
if (isPropertyShorthand(node, parent)) {
|
||||||
isPropertyKey(node, parent) &&
|
|
||||||
(parent as Property).value === node
|
|
||||||
) {
|
|
||||||
// property shorthand like { foo }, we need to add the key since we
|
// property shorthand like { foo }, we need to add the key since we
|
||||||
// rewrite the value
|
// rewrite the value
|
||||||
node.prefix = `${node.name}: `
|
node.prefix = `${node.name}: `
|
||||||
}
|
}
|
||||||
node.name = `_ctx.${node.name}`
|
node.name = `_ctx.${node.name}`
|
||||||
ids.push(node)
|
ids.push(node)
|
||||||
} else if (!isPropertyKey(node, parent)) {
|
} else if (!isStaticPropertyKey(node, parent)) {
|
||||||
// also generate sub-expressioms for other identifiers for better
|
// also generate sub-expressioms for other identifiers for better
|
||||||
// source map support. (except for property keys which are static)
|
// source map support. (except for property keys which are static)
|
||||||
ids.push(node)
|
ids.push(node)
|
||||||
@ -131,9 +130,11 @@ export function processExpression(
|
|||||||
enter(child, parent) {
|
enter(child, parent) {
|
||||||
if (
|
if (
|
||||||
child.type === 'Identifier' &&
|
child.type === 'Identifier' &&
|
||||||
!// do not keep as scope variable if this is a default value
|
// do not record as scope variable if is a destrcuture key
|
||||||
// assignment of a param
|
!isStaticPropertyKey(child, parent) &&
|
||||||
(
|
// do not record if this is a default value
|
||||||
|
// assignment of a destructured variable
|
||||||
|
!(
|
||||||
parent &&
|
parent &&
|
||||||
parent.type === 'AssignmentPattern' &&
|
parent.type === 'AssignmentPattern' &&
|
||||||
parent.right === child
|
parent.right === child
|
||||||
@ -213,7 +214,16 @@ const isFunction = (node: Node): node is Function =>
|
|||||||
/Function(Expression|Declaration)$/.test(node.type)
|
/Function(Expression|Declaration)$/.test(node.type)
|
||||||
|
|
||||||
const isPropertyKey = (node: Node, parent: Node) =>
|
const isPropertyKey = (node: Node, parent: Node) =>
|
||||||
parent.type === 'Property' && parent.key === node && !parent.computed
|
parent &&
|
||||||
|
parent.type === 'Property' &&
|
||||||
|
parent.key === node &&
|
||||||
|
!parent.computed
|
||||||
|
|
||||||
|
const isPropertyShorthand = (node: Node, parent: Node) =>
|
||||||
|
isPropertyKey(node, parent) && (parent as Property).value === node
|
||||||
|
|
||||||
|
const isStaticPropertyKey = (node: Node, parent: Node) =>
|
||||||
|
isPropertyKey(node, parent) && (parent as Property).value !== node
|
||||||
|
|
||||||
const globals = new Set(
|
const globals = new Set(
|
||||||
(
|
(
|
||||||
@ -236,11 +246,7 @@ function shouldPrefix(identifier: Identifier, parent: Node) {
|
|||||||
parent.params.includes(identifier))
|
parent.params.includes(identifier))
|
||||||
) &&
|
) &&
|
||||||
// not a key of Property
|
// not a key of Property
|
||||||
!(
|
!isStaticPropertyKey(identifier, parent) &&
|
||||||
isPropertyKey(identifier, parent) &&
|
|
||||||
// shorthand keys should be prefixed
|
|
||||||
!((parent as Property).value === identifier)
|
|
||||||
) &&
|
|
||||||
// not a property of a MemberExpression
|
// not a property of a MemberExpression
|
||||||
!(
|
!(
|
||||||
parent.type === 'MemberExpression' &&
|
parent.type === 'MemberExpression' &&
|
||||||
|
@ -39,19 +39,20 @@ export const transformFor = createStructuralDirectiveTransform(
|
|||||||
children: [node]
|
children: [node]
|
||||||
})
|
})
|
||||||
|
|
||||||
// scope management
|
if (!__BROWSER__) {
|
||||||
const { addIdentifier, removeIdentifier } = context
|
// scope management
|
||||||
|
const { addIdentifiers, removeIdentifiers } = context
|
||||||
|
|
||||||
// inject identifiers to context
|
// inject identifiers to context
|
||||||
value && addIdentifier(value.content)
|
value && addIdentifiers(value)
|
||||||
key && addIdentifier(key.content)
|
key && addIdentifiers(key)
|
||||||
index && addIdentifier(index.content)
|
index && addIdentifiers(index)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// remove injected identifiers on exit
|
value && removeIdentifiers(value)
|
||||||
value && removeIdentifier(value.content)
|
key && removeIdentifiers(key)
|
||||||
key && removeIdentifier(key.content)
|
index && removeIdentifiers(index)
|
||||||
index && removeIdentifier(index.content)
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.onError(
|
context.onError(
|
||||||
@ -67,14 +68,16 @@ export const transformFor = createStructuralDirectiveTransform(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
|
const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
|
||||||
|
// This regex doesn't cover the case if key or index aliases have destructuring,
|
||||||
|
// but those do not make sense in the first place, so this works in practice.
|
||||||
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
||||||
const stripParensRE = /^\(|\)$/g
|
const stripParensRE = /^\(|\)$/g
|
||||||
|
|
||||||
interface ForParseResult {
|
interface ForParseResult {
|
||||||
source: ExpressionNode
|
source: ExpressionNode
|
||||||
value: SimpleExpressionNode | undefined
|
value: ExpressionNode | undefined
|
||||||
key: SimpleExpressionNode | undefined
|
key: ExpressionNode | undefined
|
||||||
index: SimpleExpressionNode | undefined
|
index: ExpressionNode | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseForExpression(
|
function parseForExpression(
|
||||||
@ -88,21 +91,22 @@ function parseForExpression(
|
|||||||
|
|
||||||
const [, LHS, RHS] = inMatch
|
const [, LHS, RHS] = inMatch
|
||||||
|
|
||||||
let source: ExpressionNode = createAliasExpression(
|
|
||||||
loc,
|
|
||||||
RHS.trim(),
|
|
||||||
exp.indexOf(RHS, LHS.length)
|
|
||||||
)
|
|
||||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
|
||||||
source = processExpression(source, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: ForParseResult = {
|
const result: ForParseResult = {
|
||||||
source,
|
source: createAliasExpression(
|
||||||
|
loc,
|
||||||
|
RHS.trim(),
|
||||||
|
exp.indexOf(RHS, LHS.length)
|
||||||
|
),
|
||||||
value: undefined,
|
value: undefined,
|
||||||
key: undefined,
|
key: undefined,
|
||||||
index: undefined
|
index: undefined
|
||||||
}
|
}
|
||||||
|
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||||
|
result.source = processExpression(
|
||||||
|
result.source as SimpleExpressionNode,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let valueContent = LHS.trim()
|
let valueContent = LHS.trim()
|
||||||
.replace(stripParensRE, '')
|
.replace(stripParensRE, '')
|
||||||
@ -118,6 +122,9 @@ function parseForExpression(
|
|||||||
if (keyContent) {
|
if (keyContent) {
|
||||||
keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length)
|
keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length)
|
||||||
result.key = createAliasExpression(loc, keyContent, keyOffset)
|
result.key = createAliasExpression(loc, keyContent, keyOffset)
|
||||||
|
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||||
|
result.key = processExpression(result.key, context, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iteratorMatch[2]) {
|
if (iteratorMatch[2]) {
|
||||||
@ -134,12 +141,18 @@ function parseForExpression(
|
|||||||
: trimmedOffset + valueContent.length
|
: trimmedOffset + valueContent.length
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||||
|
result.index = processExpression(result.index, context, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueContent) {
|
if (valueContent) {
|
||||||
result.value = createAliasExpression(loc, valueContent, trimmedOffset)
|
result.value = createAliasExpression(loc, valueContent, trimmedOffset)
|
||||||
|
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||||
|
result.value = processExpression(result.value, context, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -31,12 +31,9 @@ export const trackSlotScopes: NodeTransform = (node, context) => {
|
|||||||
) {
|
) {
|
||||||
const vSlot = node.props.find(isVSlot)
|
const vSlot = node.props.find(isVSlot)
|
||||||
if (vSlot && vSlot.exp) {
|
if (vSlot && vSlot.exp) {
|
||||||
const { identifiers } = vSlot.exp
|
context.addIdentifiers(vSlot.exp)
|
||||||
if (identifiers) {
|
return () => {
|
||||||
identifiers.forEach(context.addIdentifier)
|
context.removeIdentifiers(vSlot.exp!)
|
||||||
return () => {
|
|
||||||
identifiers.forEach(context.removeIdentifier)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user