exports[`compiler: codegen function mode preamble 1`] = `
"const _Vue = Vue
+
return function render() {
with (this) {
const { helperOne: _helperOne, helperTwo: _helperTwo } = _Vue
+
return null
}
}"
"
return function render() {
with (this) {
- return foo
- ? \\"foo\\"
- : (a + b)
- ? _toString(bye)
- : _createVNode(_Comment, 0, \\"foo\\")
- }
-}"
-`;
-
-exports[`compiler: codegen ifNode with no v-else 1`] = `
-"
-return function render() {
- with (this) {
- return foo
- ? \\"foo\\"
- : (a + b)
- ? _toString(bye)
- : null
+ return (foo, bar)
}
}"
`;
exports[`compiler: integration tests function mode 1`] = `
"const _Vue = Vue
+
return function render() {
with (this) {
- const { createVNode: _createVNode, toString: _toString, renderList: _renderList } = _Vue
+ const { createVNode: _createVNode, toString: _toString, openBlock: _openBlock, applyDirectives: _applyDirectives, createBlock: _createBlock, Empty: _Empty, Fragment: _Fragment, renderList: _renderList } = _Vue
+
return _createVNode(\\"div\\", {
id: \\"foo\\",
class: bar.baz
}, [
_toString(world.burn()),
- ok
- ? _createVNode(\\"div\\", null, \\"yes\\")
- : \\"no\\",
+ (_openBlock(), ok
+ ? _createBlock(
+ \\"div\\",
+ { key: 0 },
+ \\"yes\\"
+ )
+ : _createBlock(
+ _Fragment,
+ { key: 1 },
+ \\"no\\"
+ )),
_renderList(list, (value, index) => {
return _createVNode(\\"div\\", null, [
_createVNode(\\"span\\", null, _toString(value + index))
`;
exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = `
-"const { createVNode, toString, renderList } = Vue
+"const { createVNode, toString, openBlock, applyDirectives, createBlock, Empty, Fragment, renderList } = Vue
return function render() {
const _ctx = this
class: _ctx.bar.baz
}, [
toString(_ctx.world.burn()),
- (_ctx.ok)
- ? createVNode(\\"div\\", null, \\"yes\\")
- : \\"no\\",
+ (openBlock(), (_ctx.ok)
+ ? createBlock(
+ \\"div\\",
+ { key: 0 },
+ \\"yes\\"
+ )
+ : createBlock(
+ Fragment,
+ { key: 1 },
+ \\"no\\"
+ )),
renderList(_ctx.list, (value, index) => {
return createVNode(\\"div\\", null, [
createVNode(\\"span\\", null, toString(value + index))
`;
exports[`compiler: integration tests module mode 1`] = `
-"import { createVNode, toString, renderList } from \\"vue\\"
+"import { createVNode, toString, openBlock, applyDirectives, createBlock, Empty, Fragment, renderList } from \\"vue\\"
export default function render() {
const _ctx = this
class: _ctx.bar.baz
}, [
_toString(_ctx.world.burn()),
- (_ctx.ok)
- ? createVNode(\\"div\\", null, \\"yes\\")
- : \\"no\\",
+ (openBlock(), (_ctx.ok)
+ ? createBlock(
+ \\"div\\",
+ { key: 0 },
+ \\"yes\\"
+ )
+ : createBlock(
+ Fragment,
+ { key: 1 },
+ \\"no\\"
+ )),
_renderList(_ctx.list, (value, index) => {
return createVNode(\\"div\\", null, [
createVNode(\\"span\\", null, _toString(value + index))
createArrayExpression,
ElementNode,
createCompoundExpression,
- createInterpolation
+ createInterpolation,
+ createSequenceExpression
} from '../src'
import {
CREATE_VNODE,
[
createObjectProperty(
createSimpleExpression(`id`, true, mockLoc),
- createSimpleExpression(`foo`, true, mockLoc),
- mockLoc
+ createSimpleExpression(`foo`, true, mockLoc)
)
],
mockLoc
const { code } = generate(
createRoot({
children: [
- createCompoundExpression(
- [
- `_ctx.`,
- createSimpleExpression(`foo`, false, mockLoc),
- ` + `,
- {
- type: NodeTypes.INTERPOLATION,
- loc: mockLoc,
- content: createSimpleExpression(`bar`, false, mockLoc)
- }
- ],
- mockLoc
- )
+ createCompoundExpression([
+ `_ctx.`,
+ createSimpleExpression(`foo`, false, mockLoc),
+ ` + `,
+ {
+ type: NodeTypes.INTERPOLATION,
+ loc: mockLoc,
+ content: createSimpleExpression(`bar`, false, mockLoc)
+ }
+ ])
]
})
)
{
type: NodeTypes.IF,
loc: mockLoc,
- branches: [
- {
- type: NodeTypes.IF_BRANCH,
- condition: createSimpleExpression('foo', false, mockLoc),
- loc: mockLoc,
- children: [
- {
- type: NodeTypes.TEXT,
- content: 'foo',
- isEmpty: false,
- loc: mockLoc
- }
- ]
- },
- {
- type: NodeTypes.IF_BRANCH,
- condition: createSimpleExpression('a + b', false, mockLoc),
- loc: mockLoc,
- children: [createInterpolation(`bye`, mockLoc)]
- },
- {
- type: NodeTypes.IF_BRANCH,
- condition: undefined,
- loc: mockLoc,
- children: [
- {
- type: NodeTypes.COMMENT,
- content: 'foo',
- loc: mockLoc
- }
- ]
- }
- ]
+ branches: [],
+ codegenNode: createSequenceExpression([
+ createSimpleExpression('foo', false),
+ createSimpleExpression('bar', false)
+ ])
}
]
})
)
- expect(code).toMatch(`
- return foo
- ? "foo"
- : (a + b)
- ? _${TO_STRING}(bye)
- : _${CREATE_VNODE}(_${COMMENT}, 0, "foo")`)
- expect(code).toMatchSnapshot()
- })
-
- test('ifNode with no v-else', () => {
- const { code } = generate(
- createRoot({
- children: [
- {
- type: NodeTypes.IF,
- loc: mockLoc,
- branches: [
- {
- type: NodeTypes.IF_BRANCH,
- condition: createSimpleExpression('foo', false, mockLoc),
- loc: mockLoc,
- children: [
- {
- type: NodeTypes.TEXT,
- content: 'foo',
- isEmpty: false,
- loc: mockLoc
- }
- ]
- },
- {
- type: NodeTypes.IF_BRANCH,
- condition: createSimpleExpression('a + b', false, mockLoc),
- loc: mockLoc,
- children: [createInterpolation(`bye`, mockLoc)]
- }
- ]
- }
- ]
- })
- )
- expect(code).toMatch(`
- return foo
- ? "foo"
- : (a + b)
- ? _${TO_STRING}(bye)
- : null`)
+ expect(code).toMatch(`return (foo, bar)`)
expect(code).toMatchSnapshot()
})
[
createObjectProperty(
createSimpleExpression(`id`, true, mockLoc),
- createSimpleExpression(`foo`, true, mockLoc),
- mockLoc
+ createSimpleExpression(`foo`, true, mockLoc)
),
createObjectProperty(
createSimpleExpression(`prop`, false, mockLoc),
- createSimpleExpression(`bar`, false, mockLoc),
- mockLoc
+ createSimpleExpression(`bar`, false, mockLoc)
),
// compound expression as computed key
createObjectProperty(
createSimpleExpression(`bar`, false, mockLoc)
]
},
- createSimpleExpression(`bar`, false, mockLoc),
- mockLoc
+ createSimpleExpression(`bar`, false, mockLoc)
)
],
mockLoc
createObjectProperty(
// should quote the key!
createSimpleExpression(`some-key`, true, mockLoc),
- createSimpleExpression(`foo`, true, mockLoc),
- mockLoc
+ createSimpleExpression(`foo`, true, mockLoc)
)
],
mockLoc
])`)
expect(code).toMatchSnapshot()
})
+
+ test.todo('SequenceExpression')
+
+ test.todo('ConditionalExpression')
})
filename: `foo.vue`
})
- expect(code).toMatch(
- `const { createVNode: _createVNode, toString: _toString, renderList: _renderList } = _Vue`
- )
-
expect(code).toMatchSnapshot()
expect(map!.sources).toEqual([`foo.vue`])
expect(map!.sourcesContent).toEqual([source])
prefixIdentifiers: true
})
- expect(code).toMatch(`const { createVNode, toString, renderList } = Vue`)
-
expect(code).toMatchSnapshot()
expect(map!.sources).toEqual([`foo.vue`])
expect(map!.sourcesContent).toEqual([source])
filename: `foo.vue`
})
- expect(code).toMatch(
- `import { createVNode, toString, renderList } from "vue"`
- )
-
expect(code).toMatchSnapshot()
expect(map!.sources).toEqual([`foo.vue`])
expect(map!.sourcesContent).toEqual([source])
exports[`compiler: optimize interpolation consecutive text 1`] = `
"const _Vue = Vue
+
return function render() {
with (this) {
const { toString: _toString } = _Vue
+
return _toString(foo) + \\" bar \\" + _toString(baz)
}
}"
exports[`compiler: optimize interpolation consecutive text between elements 1`] = `
"const _Vue = Vue
+
return function render() {
with (this) {
const { createVNode: _createVNode, toString: _toString } = _Vue
+
return [
_createVNode(\\"div\\"),
_toString(foo) + \\" bar \\" + _toString(baz),
exports[`compiler: optimize interpolation consecutive text mixed with elements 1`] = `
"const _Vue = Vue
+
return function render() {
with (this) {
const { createVNode: _createVNode, toString: _toString } = _Vue
+
return [
_createVNode(\\"div\\"),
_toString(foo) + \\" bar \\" + _toString(baz),
exports[`compiler: optimize interpolation no consecutive text 1`] = `
"const _Vue = Vue
+
return function render() {
with (this) {
const { toString: _toString } = _Vue
+
return _toString(foo)
}
}"
})
test('static props', () => {
- const { root, node } = parseWithElementTransform(
- `<div id="foo" class="bar" />`
- )
+ const { node } = parseWithElementTransform(`<div id="foo" class="bar" />`)
expect(node.callee).toBe(`_${CREATE_VNODE}`)
- // should hoist the static object
- expect(root.hoists).toMatchObject([
+ expect(node.arguments).toMatchObject([
+ `"div"`,
createStaticObjectMatcher({
id: 'foo',
class: 'bar'
})
])
- expect(node.arguments).toMatchObject([
- `"div"`,
- {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_hoisted_1`
- }
- ])
})
test('props + children', () => {
- const { root, node } = parseWithElementTransform(
- `<div id="foo"><span/></div>`
- )
+ const { node } = parseWithElementTransform(`<div id="foo"><span/></div>`)
expect(node.callee).toBe(`_${CREATE_VNODE}`)
- expect(root.hoists).toMatchObject([
- createStaticObjectMatcher({
- id: 'foo'
- })
- ])
expect(node.arguments).toMatchObject([
`"div"`,
- {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_hoisted_1`
- },
+ createStaticObjectMatcher({
+ id: 'foo'
+ }),
[
{
type: NodeTypes.ELEMENT,
foo(dir) {
_dir = dir
return {
- props: createObjectProperty(dir.arg!, dir.exp!, dir.loc),
+ props: createObjectProperty(dir.arg!, dir.exp!),
needRuntime: false
}
}
foo(dir) {
_dir = dir
return {
- props: [createObjectProperty(dir.arg!, dir.exp!, dir.loc)],
+ props: [createObjectProperty(dir.arg!, dir.exp!)],
needRuntime: true
}
}
DirectiveNode,
NodeTypes,
CompilerOptions,
- IfNode,
InterpolationNode
} from '../../src'
import { transformIf } from '../../src/transforms/vIf'
})
})
- test('should prefix v-if condition', () => {
- const node = parseWithExpressionTransform(`<div v-if="ok"/>`) as IfNode
- expect(node.branches[0].condition).toMatchObject({
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_ctx.ok`
- })
- })
-
test('should not prefix whitelisted globals', () => {
const node = parseWithExpressionTransform(
`{{ Math.max(1, 2) }}`
column: 19
}
}
- },
- loc: {
- start: {
- line: 1,
- column: 6
- },
- end: {
- line: 1,
- column: 20
- }
}
})
})
import { parse } from '../../src/parse'
import { transform } from '../../src/transform'
import { transformIf } from '../../src/transforms/vIf'
+import { transformElement } from '../../src/transforms/transformElement'
import {
IfNode,
NodeTypes,
returnIndex: number = 0
): IfNode {
const node = parse(template, options)
- transform(node, { nodeTransforms: [transformIf], ...options })
+ transform(node, {
+ nodeTransforms: [transformIf, transformElement],
+ ...options
+ })
if (!options.onError) {
expect(node.children.length).toBe(1)
expect(node.children[0].type).toBe(NodeTypes.IF)
expect((b3.children[1] as TextNode).content).toBe(`fine`)
})
- test('error on v-else missing adjacent v-if', () => {
- const onError = jest.fn()
-
- const node1 = parseWithIfTransform(`<div v-else/>`, { onError })
- expect(onError.mock.calls[0]).toMatchObject([
- {
- code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
- loc: node1.loc
- }
- ])
-
- const node2 = parseWithIfTransform(`<div/><div v-else/>`, { onError }, 1)
- expect(onError.mock.calls[1]).toMatchObject([
- {
- code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
- loc: node2.loc
- }
- ])
-
- const node3 = parseWithIfTransform(`<div/>foo<div v-else/>`, { onError }, 2)
- expect(onError.mock.calls[2]).toMatchObject([
- {
- code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
- loc: node3.loc
- }
- ])
+ test('should prefix v-if condition', () => {
+ const node = parseWithIfTransform(`<div v-if="ok"/>`, {
+ prefixIdentifiers: true
+ }) as IfNode
+ expect(node.branches[0].condition).toMatchObject({
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: `_ctx.ok`
+ })
})
- test('error on v-else-if missing adjacent v-if', () => {
- const onError = jest.fn()
-
- const node1 = parseWithIfTransform(`<div v-else-if="foo"/>`, { onError })
- expect(onError.mock.calls[0]).toMatchObject([
- {
- code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
- loc: node1.loc
- }
- ])
-
- const node2 = parseWithIfTransform(
- `<div/><div v-else-if="foo"/>`,
- { onError },
- 1
- )
- expect(onError.mock.calls[1]).toMatchObject([
- {
- code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
- loc: node2.loc
- }
- ])
-
- const node3 = parseWithIfTransform(
- `<div/>foo<div v-else-if="foo"/>`,
- { onError },
- 2
- )
- expect(onError.mock.calls[2]).toMatchObject([
- {
- code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
- loc: node3.loc
- }
- ])
+ describe('codegen', () => {
+ // TODO
+ })
+
+ describe('errors', () => {
+ test('error on v-else missing adjacent v-if', () => {
+ const onError = jest.fn()
+
+ const node1 = parseWithIfTransform(`<div v-else/>`, { onError })
+ expect(onError.mock.calls[0]).toMatchObject([
+ {
+ code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
+ loc: node1.loc
+ }
+ ])
+
+ const node2 = parseWithIfTransform(`<div/><div v-else/>`, { onError }, 1)
+ expect(onError.mock.calls[1]).toMatchObject([
+ {
+ code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
+ loc: node2.loc
+ }
+ ])
+
+ const node3 = parseWithIfTransform(
+ `<div/>foo<div v-else/>`,
+ { onError },
+ 2
+ )
+ expect(onError.mock.calls[2]).toMatchObject([
+ {
+ code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
+ loc: node3.loc
+ }
+ ])
+ })
+
+ test('error on v-else-if missing adjacent v-if', () => {
+ const onError = jest.fn()
+
+ const node1 = parseWithIfTransform(`<div v-else-if="foo"/>`, { onError })
+ expect(onError.mock.calls[0]).toMatchObject([
+ {
+ code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
+ loc: node1.loc
+ }
+ ])
+
+ const node2 = parseWithIfTransform(
+ `<div/><div v-else-if="foo"/>`,
+ { onError },
+ 1
+ )
+ expect(onError.mock.calls[1]).toMatchObject([
+ {
+ code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
+ loc: node2.loc
+ }
+ ])
+
+ const node3 = parseWithIfTransform(
+ `<div/>foo<div v-else-if="foo"/>`,
+ { onError },
+ 2
+ )
+ expect(onError.mock.calls[2]).toMatchObject([
+ {
+ code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
+ loc: node3.loc
+ }
+ ])
+ })
})
})
column: 25
}
}
- },
- loc: {
- start: {
- line: 1,
- column: 6
- },
- end: {
- line: 1,
- column: 26
- }
}
})
})
export interface IfNode extends Node {
type: NodeTypes.IF
branches: IfBranchNode[]
- codegenNode: JSChildNode | undefined
+ codegenNode: SequenceExpression
}
export interface IfBranchNode extends Node {
alternate: JSChildNode
}
+// AST Utilities ---------------------------------------------------------------
+
+// Some expressions, e.g. sequence and conditional expressions, are never
+// associated with template nodes, so their source locations are just a stub.
+// Container types like CompoundExpression also don't need a real location.
+const locStub: SourceLocation = {
+ source: '',
+ start: { line: 1, column: 1, offset: 0 },
+ end: { line: 1, column: 1, offset: 0 }
+}
+
export function createArrayExpression(
elements: ArrayExpression['elements'],
- loc: SourceLocation
+ loc: SourceLocation = locStub
): ArrayExpression {
return {
type: NodeTypes.JS_ARRAY_EXPRESSION,
export function createObjectExpression(
properties: Property[],
- loc: SourceLocation
+ loc: SourceLocation = locStub
): ObjectExpression {
return {
type: NodeTypes.JS_OBJECT_EXPRESSION,
export function createObjectProperty(
key: ExpressionNode,
- value: JSChildNode,
- loc: SourceLocation
+ value: JSChildNode
): Property {
return {
type: NodeTypes.JS_PROPERTY,
- loc,
+ loc: locStub,
key,
value
}
export function createSimpleExpression(
content: string,
isStatic: boolean,
- loc: SourceLocation
+ loc: SourceLocation = locStub
): SimpleExpressionNode {
return {
type: NodeTypes.SIMPLE_EXPRESSION,
}
export function createCompoundExpression(
- children: CompoundExpressionNode['children'],
- loc: SourceLocation
+ children: CompoundExpressionNode['children']
): CompoundExpressionNode {
return {
type: NodeTypes.COMPOUND_EXPRESSION,
- loc,
+ loc: locStub,
children
}
}
export function createCallExpression(
callee: string,
- args: CallExpression['arguments'],
- loc: SourceLocation
+ args: CallExpression['arguments'] = [],
+ loc: SourceLocation = locStub
): CallExpression {
return {
type: NodeTypes.JS_CALL_EXPRESSION,
export function createFunctionExpression(
params: ExpressionNode | undefined,
returns: TemplateChildNode[],
- loc: SourceLocation
+ loc: SourceLocation = locStub
): SlotFunctionExpression {
return {
type: NodeTypes.JS_SLOT_FUNCTION,
}
}
-// sequence and conditional expressions are never associated with template nodes,
-// so their source locations are just a stub.
-const locStub: SourceLocation = {
- source: '',
- start: { line: 1, column: 1, offset: 0 },
- end: { line: 1, column: 1, offset: 0 }
-}
-
-export function createSequenceExpression(expressions: JSChildNode[]) {
+export function createSequenceExpression(
+ expressions: JSChildNode[]
+): SequenceExpression {
return {
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions,
test: ExpressionNode,
consequent: JSChildNode,
alternate: JSChildNode
-) {
+): ConditionalExpression {
return {
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
test,
CallExpression,
ArrayExpression,
ObjectExpression,
- IfBranchNode,
SourceLocation,
Position,
InterpolationNode,
push(`const { ${ast.imports.join(', ')} } = Vue\n`)
} else {
// save Vue in a separate variable to avoid collision
- push(`const _Vue = Vue`)
+ push(`const _Vue = Vue\n`)
}
}
genHoists(ast.hoists, context)
if (hasImports) {
push(`const { ${ast.imports.map(n => `${n}: _${n}`).join(', ')} } = _Vue`)
newline()
+ newline()
}
} else {
push(`const _ctx = this`)
// control flow
function genIf(node: IfNode, context: CodegenContext) {
- genIfBranch(node.branches[0], node.branches, 1, context)
-}
-
-function genIfBranch(
- { condition, children }: IfBranchNode,
- branches: IfBranchNode[],
- nextIndex: number,
- context: CodegenContext
-) {
- if (condition) {
- // v-if or v-else-if
- const { push, indent, deindent, newline } = context
- if (condition.type === NodeTypes.SIMPLE_EXPRESSION) {
- const needsQuote = !isSimpleIdentifier(condition.content)
- needsQuote && push(`(`)
- genExpression(condition, context)
- needsQuote && push(`)`)
- } else {
- genCompoundExpression(condition, context)
- }
- indent()
- context.indentLevel++
- push(`? `)
- genChildren(children, context, true)
- context.indentLevel--
- newline()
- push(`: `)
- if (nextIndex < branches.length) {
- genIfBranch(branches[nextIndex], branches, nextIndex + 1, context)
- } else {
- context.push(`null`)
- }
- deindent(true /* without newline */)
- } else {
- // v-else
- __DEV__ && assert(nextIndex === branches.length)
- genChildren(children, context, true)
- }
+ genNode(node.codegenNode, context)
}
function genFor(node: ForNode, context: CodegenContext) {
context.indentLevel--
newline()
push(`: `)
+ const isNested = alternate.type === NodeTypes.JS_CONDITIONAL_EXPRESSION
+ if (!isNested) {
+ context.indentLevel++
+ }
genNode(alternate, context)
+ if (!isNested) {
+ context.indentLevel--
+ }
deindent(true /* without newline */)
}
X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END,
// transform errors
+ X_IF_NO_EXPRESSION,
X_ELSE_IF_NO_ADJACENT_IF,
X_ELSE_NO_ADJACENT_IF,
X_FOR_NO_EXPRESSION,
'Note that dynamic directive argument connot contain spaces.',
// transform errors
+ [ErrorCodes.X_IF_NO_EXPRESSION]: `v-if/v-else-if is missing expression.`,
[ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF]: `v-else-if has no adjacent v-if.`,
[ErrorCodes.X_ELSE_NO_ADJACENT_IF]: `v-else has no adjacent v-if.`,
- [ErrorCodes.X_FOR_NO_EXPRESSION]: `v-for has no expression.`,
+ [ErrorCodes.X_FOR_NO_EXPRESSION]: `v-for is missing expression.`,
[ErrorCodes.X_FOR_MALFORMED_EXPRESSION]: `v-for has invalid expression.`,
[ErrorCodes.X_V_BIND_NO_EXPRESSION]: `v-bind is missing expression.`,
[ErrorCodes.X_V_ON_NO_EXPRESSION]: `v-on is missing expression.`,
export const COMMENT = `Comment`
export const TEXT = `Text`
export const SUSPENSE = `Suspense`
+export const EMPTY = `Empty`
+export const OPEN_BLOCK = `openBlock`
+export const CREATE_BLOCK = `createBlock`
export const CREATE_VNODE = `createVNode`
export const RESOLVE_COMPONENT = `resolveComponent`
export const RESOLVE_DIRECTIVE = `resolveDirective`
patchFlag: number
dynamicPropNames: string[]
} {
- let isStatic = true
let properties: ObjectExpression['properties'] = []
const mergeArgs: PropsExpression[] = []
const runtimeDirectives: DirectiveNode[] = []
value ? value.content : '',
true,
value ? value.loc : loc
- ),
- loc
+ )
)
)
} else {
// directives
- isStatic = false
const { name, arg, exp, loc } = prop
// skip v-slot - it is handled by its dedicated transform.
)
}
- // hoist the object if it's fully static
- if (isStatic && propsExpression) {
- propsExpression = context.hoist(propsExpression)
- }
-
// determine the flags to add
if (hasDynammicKeys) {
patchFlag |= PatchFlags.FULL_PROPS
dir.modifiers.map(modifier =>
createObjectProperty(
createSimpleExpression(modifier, true, loc),
- createSimpleExpression(`true`, false, loc),
- loc
+ createSimpleExpression(`true`, false, loc)
)
),
loc
let ret
if (children.length) {
- ret = createCompoundExpression(children, node.loc)
+ ret = createCompoundExpression(children)
} else {
ret = node
}
arg.content === `name`
) {
// dynamic :name="xxx"
- slot = createCompoundExpression(
- [
- $slots + `[`,
- ...(exp.type === NodeTypes.SIMPLE_EXPRESSION
- ? [exp]
- : exp.children),
- `]`
- ],
- loc
- )
+ slot = createCompoundExpression([
+ $slots + `[`,
+ ...(exp.type === NodeTypes.SIMPLE_EXPRESSION
+ ? [exp]
+ : exp.children),
+ `]`
+ ])
nameIndex = i
break
}
return {
props: createObjectProperty(
arg!,
- exp || createSimpleExpression('', true, loc),
- loc
+ exp || createSimpleExpression('', true, loc)
),
needRuntime: false
}
import {
createStructuralDirectiveTransform,
- traverseChildren
+ traverseChildren,
+ TransformContext
} from '../transform'
import {
NodeTypes,
ElementNode,
DirectiveNode,
IfBranchNode,
- SimpleExpressionNode
+ SimpleExpressionNode,
+ createSequenceExpression,
+ createCallExpression,
+ createConditionalExpression,
+ ConditionalExpression,
+ CallExpression,
+ createSimpleExpression,
+ JSChildNode,
+ ObjectExpression,
+ createObjectProperty,
+ Property
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression'
+import {
+ OPEN_BLOCK,
+ CREATE_BLOCK,
+ EMPTY,
+ FRAGMENT,
+ APPLY_DIRECTIVES
+} from '../runtimeConstants'
+import { isString } from '@vue/shared'
export const transformIf = createStructuralDirectiveTransform(
/^(if|else|else-if)$/,
(node, dir, context) => {
+ if (
+ dir.name !== 'else' &&
+ (!dir.exp || !(dir.exp as SimpleExpressionNode).content.trim())
+ ) {
+ const loc = dir.exp ? dir.exp.loc : node.loc
+ context.onError(createCompilerError(ErrorCodes.X_IF_NO_EXPRESSION, loc))
+ dir.exp = createSimpleExpression(`true`, false, loc)
+ }
+
if (!__BROWSER__ && context.prefixIdentifiers && dir.exp) {
// dir.exp can only be simple expression because vIf transform is applied
// before expression transform.
- processExpression(dir.exp as SimpleExpressionNode, context)
+ dir.exp = processExpression(dir.exp as SimpleExpressionNode, context)
}
+
if (dir.name === 'if') {
+ const codegenNode = createSequenceExpression([
+ createCallExpression(context.helper(OPEN_BLOCK))
+ ])
+
context.replaceNode({
type: NodeTypes.IF,
loc: node.loc,
- branches: [createIfBranch(node, dir)]
+ branches: [createIfBranch(node, dir)],
+ codegenNode
})
+
+ // Exit callback. Complete the codegenNode when all children have been
+ // transformed.
+ return () => {
+ codegenNode.expressions.push(
+ createCodegenNodeForBranch(node, dir, 0, context)
+ )
+ }
} else {
// locate the adjacent v-if
const siblings = context.parent!.children
// since the branch was removed, it will not be traversed.
// make sure to traverse here.
traverseChildren(branch, context)
+ // attach this branch's codegen node to the v-if root.
+ let parentCondition = sibling.codegenNode
+ .expressions[1] as ConditionalExpression
+ while (true) {
+ if (
+ parentCondition.alternate.type ===
+ NodeTypes.JS_CONDITIONAL_EXPRESSION
+ ) {
+ parentCondition = parentCondition.alternate
+ } else {
+ parentCondition.alternate = createCodegenNodeForBranch(
+ node,
+ dir,
+ sibling.branches.length - 1,
+ context
+ )
+ break
+ }
+ }
} else {
context.onError(
createCompilerError(
children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node]
}
}
+
+function createCodegenNodeForBranch(
+ node: ElementNode,
+ dir: DirectiveNode,
+ index: number,
+ context: TransformContext
+): ConditionalExpression | CallExpression {
+ if (dir.exp) {
+ return createConditionalExpression(
+ dir.exp,
+ createChildrenCodegenNode(node, index, context),
+ createCallExpression(context.helper(CREATE_BLOCK), [
+ context.helper(EMPTY)
+ ])
+ )
+ } else {
+ return createChildrenCodegenNode(node, index, context)
+ }
+}
+
+function createChildrenCodegenNode(
+ node: ElementNode,
+ index: number,
+ { helper }: TransformContext
+): CallExpression {
+ const isTemplate = node.tagType === ElementTypes.TEMPLATE
+ const keyExp = `{ key: ${index} }`
+ if (isTemplate) {
+ return createCallExpression(helper(CREATE_BLOCK), [
+ helper(FRAGMENT),
+ keyExp,
+ node.children
+ ])
+ } else {
+ let childCodegen = node.codegenNode!
+ if (childCodegen.callee === helper(APPLY_DIRECTIVES)) {
+ childCodegen = childCodegen.arguments[0] as CallExpression
+ }
+ // change child to a block
+ childCodegen.callee = helper(CREATE_BLOCK)
+ // branch key
+ const existingProps = childCodegen.arguments[1]
+ if (!existingProps || existingProps === `null`) {
+ childCodegen.arguments[1] = keyExp
+ } else {
+ // inject branch key if not already have a key
+ const props = existingProps as CallExpression | ObjectExpression
+ if (props.type === NodeTypes.JS_CALL_EXPRESSION) {
+ // merged props... add ours
+ // only inject key to object literal if it's the first argument so that
+ // if doesn't override user provided keys
+ const first = props.arguments[0] as string | JSChildNode
+ if (!isString(first) && first.type === NodeTypes.JS_OBJECT_EXPRESSION) {
+ first.properties.unshift(createKeyProperty(index))
+ } else {
+ props.arguments.unshift(keyExp)
+ }
+ } else {
+ props.properties.unshift(createKeyProperty(index))
+ }
+ }
+ return childCodegen
+ }
+}
+
+function createKeyProperty(index: number): Property {
+ return createObjectProperty(
+ createSimpleExpression(`key`, true),
+ createSimpleExpression(index + '', false)
+ )
+}
arg.loc
)
} else {
- eventName = createCompoundExpression([`"on" + (`, arg, `)`], arg.loc)
+ eventName = createCompoundExpression([`"on" + (`, arg, `)`])
}
} else {
// already a compound epxression.
return {
props: createObjectProperty(
eventName,
- exp || createSimpleExpression(`() => {}`, false, loc),
- loc
+ exp || createSimpleExpression(`() => {}`, false, loc)
),
needRuntime: false
}
slotProps,
children,
children.length ? children[0].loc : loc
- ),
- loc
+ )
)
}