diff --git a/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap index 2d41b784b..628f352e6 100644 --- a/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap @@ -22,19 +22,6 @@ return function render() { }" `; -exports[`compiler: codegen SlotFunctionExpression 1`] = ` -" -return function render() { - with (this) { - return _createVNode(Comp, 0, { - default: ({ foo }) => [ - _toString(foo) - ] - }) - } -}" -`; - exports[`compiler: codegen callExpression + objectExpression + arrayExpression 1`] = ` " return function render() { @@ -71,60 +58,6 @@ return function render() { }" `; -exports[`compiler: codegen forNode 1`] = ` -" -return function render() { - with (this) { - return _renderList(list, (v, k, i) => { - return _toString(v) - }) - } -}" -`; - -exports[`compiler: codegen forNode w/ prefixIdentifiers: true 1`] = ` -" -return function render() { - const _ctx = this - return renderList(list, (v, k, i) => { - return toString(v) - }) -}" -`; - -exports[`compiler: codegen forNode w/ skipped key alias 1`] = ` -" -return function render() { - with (this) { - return _renderList(list, (v, __key, i) => { - return _toString(v) - }) - } -}" -`; - -exports[`compiler: codegen forNode w/ skipped value alias 1`] = ` -" -return function render() { - with (this) { - return _renderList(list, (__value, k, i) => { - return _toString(v) - }) - } -}" -`; - -exports[`compiler: codegen forNode w/ skipped value and key aliases 1`] = ` -" -return function render() { - with (this) { - return _renderList(list, (__value, __key, i) => { - return _toString(v) - }) - } -}" -`; - exports[`compiler: codegen function mode preamble 1`] = ` "const _Vue = Vue diff --git a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap index 359a881dd..1ac0c95bc 100644 --- a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap @@ -15,11 +15,11 @@ return function render() { (_openBlock(), ok ? _createBlock(\\"div\\", { key: 0 }, \\"yes\\") : _createBlock(_Fragment, { key: 1 }, \\"no\\")), - _renderList(list, (value, index) => { + (_openBlock(), _createBlock(_Fragment, null, _renderList(list, (value, index) => { return _createVNode(\\"div\\", null, [ _createVNode(\\"span\\", null, _toString(value + index)) ]) - }) + }))) ], 2) } }" @@ -38,11 +38,11 @@ return function render() { (openBlock(), (_ctx.ok) ? createBlock(\\"div\\", { key: 0 }, \\"yes\\") : createBlock(Fragment, { key: 1 }, \\"no\\")), - renderList(_ctx.list, (value, index) => { + (openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => { return createVNode(\\"div\\", null, [ createVNode(\\"span\\", null, toString(value + index)) ]) - }) + }))) ], 2) }" `; @@ -60,11 +60,11 @@ export default function render() { (openBlock(), (_ctx.ok) ? createBlock(\\"div\\", { key: 0 }, \\"yes\\") : createBlock(Fragment, { key: 1 }, \\"no\\")), - _renderList(_ctx.list, (value, index) => { + (openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => { return createVNode(\\"div\\", null, [ createVNode(\\"span\\", null, _toString(value + index)) ]) - }) + }))) ], 2) }" `; diff --git a/packages/compiler-core/__tests__/codegen.spec.ts b/packages/compiler-core/__tests__/codegen.spec.ts index d8e752bf0..469e6ed3a 100644 --- a/packages/compiler-core/__tests__/codegen.spec.ts +++ b/packages/compiler-core/__tests__/codegen.spec.ts @@ -4,8 +4,6 @@ import { NodeTypes, RootNode, createSimpleExpression, - Namespaces, - ElementTypes, createObjectExpression, createObjectProperty, createArrayExpression, @@ -15,12 +13,7 @@ import { createCallExpression, createConditionalExpression } from '../src' -import { - CREATE_VNODE, - COMMENT, - TO_STRING, - RENDER_LIST -} from '../src/runtimeConstants' +import { CREATE_VNODE, COMMENT, TO_STRING } from '../src/runtimeConstants' import { createElementWithCodegen } from './testUtils' function createRoot(options: Partial = {}): RootNode { @@ -250,202 +243,202 @@ describe('compiler: codegen', () => { expect(code).toMatchSnapshot() }) - test('forNode', () => { - const { code } = generate( - createRoot({ - children: [ - { - type: NodeTypes.FOR, - loc: locStub, - source: createSimpleExpression(`list`, false, locStub), - valueAlias: createSimpleExpression(`v`, false, locStub), - keyAlias: createSimpleExpression(`k`, false, locStub), - objectIndexAlias: createSimpleExpression(`i`, false, locStub), - children: [createInterpolation(`v`, locStub)] - } - ] - }) - ) - expect(code).toMatch( - `return _${RENDER_LIST}(list, (v, k, i) => { - return _${TO_STRING}(v) - })` - ) - expect(code).toMatchSnapshot() - }) + // test('forNode', () => { + // const { code } = generate( + // createRoot({ + // children: [ + // { + // type: NodeTypes.FOR, + // loc: locStub, + // source: createSimpleExpression(`list`, false, locStub), + // valueAlias: createSimpleExpression(`v`, false, locStub), + // keyAlias: createSimpleExpression(`k`, false, locStub), + // objectIndexAlias: createSimpleExpression(`i`, false, locStub), + // children: [createInterpolation(`v`, locStub)] + // } + // ] + // }) + // ) + // expect(code).toMatch( + // `return _${RENDER_LIST}(list, (v, k, i) => { + // return _${TO_STRING}(v) + // })` + // ) + // expect(code).toMatchSnapshot() + // }) - test('forNode w/ prefixIdentifiers: true', () => { - const { code } = generate( - createRoot({ - children: [ - { - type: NodeTypes.FOR, - loc: locStub, - source: createSimpleExpression(`list`, false, locStub), - valueAlias: createSimpleExpression(`v`, false, locStub), - keyAlias: createSimpleExpression(`k`, false, locStub), - objectIndexAlias: createSimpleExpression(`i`, false, locStub), - children: [createInterpolation(`v`, locStub)] - } - ] - }), - { - prefixIdentifiers: true - } - ) - expect(code).toMatch( - `return ${RENDER_LIST}(list, (v, k, i) => { - return ${TO_STRING}(v) - })` - ) - expect(code).toMatchSnapshot() - }) + // test('forNode w/ prefixIdentifiers: true', () => { + // const { code } = generate( + // createRoot({ + // children: [ + // { + // type: NodeTypes.FOR, + // loc: locStub, + // source: createSimpleExpression(`list`, false, locStub), + // valueAlias: createSimpleExpression(`v`, false, locStub), + // keyAlias: createSimpleExpression(`k`, false, locStub), + // objectIndexAlias: createSimpleExpression(`i`, false, locStub), + // children: [createInterpolation(`v`, locStub)] + // } + // ] + // }), + // { + // prefixIdentifiers: true + // } + // ) + // expect(code).toMatch( + // `return ${RENDER_LIST}(list, (v, k, i) => { + // return ${TO_STRING}(v) + // })` + // ) + // expect(code).toMatchSnapshot() + // }) - test('forNode w/ skipped value alias', () => { - const { code } = generate( - createRoot({ - children: [ - { - type: NodeTypes.FOR, - loc: locStub, - source: createSimpleExpression(`list`, false, locStub), - valueAlias: undefined, - keyAlias: createSimpleExpression(`k`, false, locStub), - objectIndexAlias: createSimpleExpression(`i`, false, locStub), - children: [createInterpolation(`v`, locStub)] - } - ] - }) - ) - expect(code).toMatch( - `return _${RENDER_LIST}(list, (__value, k, i) => { - return _${TO_STRING}(v) - })` - ) - expect(code).toMatchSnapshot() - }) + // test('forNode w/ skipped value alias', () => { + // const { code } = generate( + // createRoot({ + // children: [ + // { + // type: NodeTypes.FOR, + // loc: locStub, + // source: createSimpleExpression(`list`, false, locStub), + // valueAlias: undefined, + // keyAlias: createSimpleExpression(`k`, false, locStub), + // objectIndexAlias: createSimpleExpression(`i`, false, locStub), + // children: [createInterpolation(`v`, locStub)] + // } + // ] + // }) + // ) + // expect(code).toMatch( + // `return _${RENDER_LIST}(list, (__value, k, i) => { + // return _${TO_STRING}(v) + // })` + // ) + // expect(code).toMatchSnapshot() + // }) - test('forNode w/ skipped key alias', () => { - const { code } = generate( - createRoot({ - children: [ - { - type: NodeTypes.FOR, - loc: locStub, - source: createSimpleExpression(`list`, false, locStub), - valueAlias: createSimpleExpression(`v`, false, locStub), - keyAlias: undefined, - objectIndexAlias: createSimpleExpression(`i`, false, locStub), - children: [createInterpolation(`v`, locStub)] - } - ] - }) - ) - expect(code).toMatch( - `return _${RENDER_LIST}(list, (v, __key, i) => { - return _${TO_STRING}(v) - })` - ) - expect(code).toMatchSnapshot() - }) + // test('forNode w/ skipped key alias', () => { + // const { code } = generate( + // createRoot({ + // children: [ + // { + // type: NodeTypes.FOR, + // loc: locStub, + // source: createSimpleExpression(`list`, false, locStub), + // valueAlias: createSimpleExpression(`v`, false, locStub), + // keyAlias: undefined, + // objectIndexAlias: createSimpleExpression(`i`, false, locStub), + // children: [createInterpolation(`v`, locStub)] + // } + // ] + // }) + // ) + // expect(code).toMatch( + // `return _${RENDER_LIST}(list, (v, __key, i) => { + // return _${TO_STRING}(v) + // })` + // ) + // expect(code).toMatchSnapshot() + // }) - test('forNode w/ skipped value and key aliases', () => { - const { code } = generate( - createRoot({ - children: [ - { - type: NodeTypes.FOR, - loc: locStub, - source: createSimpleExpression(`list`, false, locStub), - valueAlias: undefined, - keyAlias: undefined, - objectIndexAlias: createSimpleExpression(`i`, false, locStub), - children: [createInterpolation(`v`, locStub)] - } - ] - }) - ) - expect(code).toMatch( - `return _${RENDER_LIST}(list, (__value, __key, i) => { - return _${TO_STRING}(v) - })` - ) - expect(code).toMatchSnapshot() - }) + // test('forNode w/ skipped value and key aliases', () => { + // const { code } = generate( + // createRoot({ + // children: [ + // { + // type: NodeTypes.FOR, + // loc: locStub, + // source: createSimpleExpression(`list`, false, locStub), + // valueAlias: undefined, + // keyAlias: undefined, + // objectIndexAlias: createSimpleExpression(`i`, false, locStub), + // children: [createInterpolation(`v`, locStub)] + // } + // ] + // }) + // ) + // expect(code).toMatch( + // `return _${RENDER_LIST}(list, (__value, __key, i) => { + // return _${TO_STRING}(v) + // })` + // ) + // expect(code).toMatchSnapshot() + // }) - test('SlotFunctionExpression', () => { - const { code } = generate( - createRoot({ - children: [ - { - type: NodeTypes.ELEMENT, - tagType: ElementTypes.COMPONENT, - ns: Namespaces.HTML, - isSelfClosing: false, - tag: `Comp`, - loc: locStub, - props: [], - children: [], - codegenNode: { - type: NodeTypes.JS_CALL_EXPRESSION, - loc: locStub, - callee: `_${CREATE_VNODE}`, - arguments: [ - `Comp`, - `0`, - { - type: NodeTypes.JS_OBJECT_EXPRESSION, - loc: locStub, - properties: [ - { - type: NodeTypes.JS_PROPERTY, - loc: locStub, - key: { - type: NodeTypes.SIMPLE_EXPRESSION, - isStatic: true, - content: `default`, - loc: locStub - }, - value: { - type: NodeTypes.JS_SLOT_FUNCTION, - loc: locStub, - params: { - type: NodeTypes.SIMPLE_EXPRESSION, - isStatic: false, - content: `{ foo }`, - loc: locStub - }, - returns: [ - { - type: NodeTypes.INTERPOLATION, - loc: locStub, - content: { - type: NodeTypes.SIMPLE_EXPRESSION, - isStatic: false, - content: `foo`, - loc: locStub - } - } - ] - } - } - ] - } - ] - } - } - ] - }) - ) - expect(code).toMatch( - `return _createVNode(Comp, 0, { - default: ({ foo }) => [ - _toString(foo) - ] - })` - ) - expect(code).toMatchSnapshot() - }) + // test('SlotFunctionExpression', () => { + // const { code } = generate( + // createRoot({ + // children: [ + // { + // type: NodeTypes.ELEMENT, + // tagType: ElementTypes.COMPONENT, + // ns: Namespaces.HTML, + // isSelfClosing: false, + // tag: `Comp`, + // loc: locStub, + // props: [], + // children: [], + // codegenNode: { + // type: NodeTypes.JS_CALL_EXPRESSION, + // loc: locStub, + // callee: `_${CREATE_VNODE}`, + // arguments: [ + // `Comp`, + // `0`, + // { + // type: NodeTypes.JS_OBJECT_EXPRESSION, + // loc: locStub, + // properties: [ + // { + // type: NodeTypes.JS_PROPERTY, + // loc: locStub, + // key: { + // type: NodeTypes.SIMPLE_EXPRESSION, + // isStatic: true, + // content: `default`, + // loc: locStub + // }, + // value: { + // type: NodeTypes.JS_FUNCTION_EXPRESSION, + // loc: locStub, + // params: { + // type: NodeTypes.SIMPLE_EXPRESSION, + // isStatic: false, + // content: `{ foo }`, + // loc: locStub + // }, + // returns: [ + // { + // type: NodeTypes.INTERPOLATION, + // loc: locStub, + // content: { + // type: NodeTypes.SIMPLE_EXPRESSION, + // isStatic: false, + // content: `foo`, + // loc: locStub + // } + // } + // ] + // } + // } + // ] + // } + // ] + // } + // } + // ] + // }) + // ) + // expect(code).toMatch( + // `return _createVNode(Comp, 0, { + // default: ({ foo }) => [ + // _toString(foo) + // ] + // })` + // ) + // expect(code).toMatchSnapshot() + // }) test('callExpression + objectExpression + arrayExpression', () => { const { code } = generate( diff --git a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts index bfd4a68ae..93fc5329c 100644 --- a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts @@ -59,7 +59,7 @@ describe('compiler: transform component slots', () => { expect(slots).toMatchObject( createSlotMatcher({ default: { - type: NodeTypes.JS_SLOT_FUNCTION, + type: NodeTypes.JS_FUNCTION_EXPRESSION, params: undefined, returns: [ { @@ -81,7 +81,7 @@ describe('compiler: transform component slots', () => { expect(slots).toMatchObject( createSlotMatcher({ default: { - type: NodeTypes.JS_SLOT_FUNCTION, + type: NodeTypes.JS_FUNCTION_EXPRESSION, params: { type: NodeTypes.COMPOUND_EXPRESSION, children: [`{ `, { content: `foo` }, ` }`] @@ -121,7 +121,7 @@ describe('compiler: transform component slots', () => { expect(slots).toMatchObject( createSlotMatcher({ one: { - type: NodeTypes.JS_SLOT_FUNCTION, + type: NodeTypes.JS_FUNCTION_EXPRESSION, params: { type: NodeTypes.SIMPLE_EXPRESSION, content: `{ foo }`, @@ -143,7 +143,7 @@ describe('compiler: transform component slots', () => { ] }, two: { - type: NodeTypes.JS_SLOT_FUNCTION, + type: NodeTypes.JS_FUNCTION_EXPRESSION, params: { type: NodeTypes.SIMPLE_EXPRESSION, content: `{ bar }`, @@ -184,7 +184,7 @@ describe('compiler: transform component slots', () => { expect(slots).toMatchObject( createSlotMatcher({ '[_ctx.one]': { - type: NodeTypes.JS_SLOT_FUNCTION, + type: NodeTypes.JS_FUNCTION_EXPRESSION, params: { type: NodeTypes.SIMPLE_EXPRESSION, content: `{ foo }`, @@ -206,7 +206,7 @@ describe('compiler: transform component slots', () => { ] }, '[_ctx.two]': { - type: NodeTypes.JS_SLOT_FUNCTION, + type: NodeTypes.JS_FUNCTION_EXPRESSION, params: { type: NodeTypes.SIMPLE_EXPRESSION, content: `{ bar }`, @@ -247,7 +247,7 @@ describe('compiler: transform component slots', () => { expect(slots).toMatchObject( createSlotMatcher({ default: { - type: NodeTypes.JS_SLOT_FUNCTION, + type: NodeTypes.JS_FUNCTION_EXPRESSION, params: { type: NodeTypes.SIMPLE_EXPRESSION, content: `{ foo }`, @@ -263,7 +263,7 @@ describe('compiler: transform component slots', () => { `null`, createSlotMatcher({ default: { - type: NodeTypes.JS_SLOT_FUNCTION, + type: NodeTypes.JS_FUNCTION_EXPRESSION, params: { type: NodeTypes.COMPOUND_EXPRESSION, children: [`{ `, { content: `bar` }, ` }`] diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index effd98e04..cc8f4296a 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -28,7 +28,7 @@ export const enum NodeTypes { JS_OBJECT_EXPRESSION, JS_PROPERTY, JS_ARRAY_EXPRESSION, - JS_SLOT_FUNCTION, + JS_FUNCTION_EXPRESSION, JS_SEQUENCE_EXPRESSION, JS_CONDITIONAL_EXPRESSION } @@ -158,6 +158,7 @@ export interface ForNode extends Node { keyAlias: ExpressionNode | undefined objectIndexAlias: ExpressionNode | undefined children: TemplateChildNode[] + codegenNode: SequenceExpression } // We also include a number of JavaScript AST nodes for code generation. @@ -168,7 +169,7 @@ export type JSChildNode = | ObjectExpression | ArrayExpression | ExpressionNode - | SlotFunctionExpression + | FunctionExpression | ConditionalExpression | SequenceExpression @@ -194,10 +195,11 @@ export interface ArrayExpression extends Node { elements: Array } -export interface SlotFunctionExpression extends Node { - type: NodeTypes.JS_SLOT_FUNCTION - params: ExpressionNode | undefined - returns: TemplateChildNode[] +export interface FunctionExpression extends Node { + type: NodeTypes.JS_FUNCTION_EXPRESSION + params: ExpressionNode | ExpressionNode[] | undefined + returns: TemplateChildNode | TemplateChildNode[] + newline: boolean } export interface SequenceExpression extends Node { @@ -235,7 +237,7 @@ export function createArrayExpression( } export function createObjectExpression( - properties: Property[], + properties: ObjectExpression['properties'], loc: SourceLocation = locStub ): ObjectExpression { return { @@ -246,8 +248,8 @@ export function createObjectExpression( } export function createObjectProperty( - key: ExpressionNode, - value: JSChildNode + key: Property['key'], + value: Property['value'] ): Property { return { type: NodeTypes.JS_PROPERTY, @@ -258,8 +260,8 @@ export function createObjectProperty( } export function createSimpleExpression( - content: string, - isStatic: boolean, + content: SimpleExpressionNode['content'], + isStatic: SimpleExpressionNode['isStatic'], loc: SourceLocation = locStub ): SimpleExpressionNode { return { @@ -271,7 +273,7 @@ export function createSimpleExpression( } export function createInterpolation( - content: string | CompoundExpressionNode, + content: InterpolationNode['content'] | string, loc: SourceLocation ): InterpolationNode { return { @@ -294,7 +296,7 @@ export function createCompoundExpression( } export function createCallExpression( - callee: string, + callee: CallExpression['callee'], args: CallExpression['arguments'] = [], loc: SourceLocation = locStub ): CallExpression { @@ -307,20 +309,22 @@ export function createCallExpression( } export function createFunctionExpression( - params: ExpressionNode | undefined, - returns: TemplateChildNode[], + params: FunctionExpression['params'], + returns: FunctionExpression['returns'], + newline: boolean = false, loc: SourceLocation = locStub -): SlotFunctionExpression { +): FunctionExpression { return { - type: NodeTypes.JS_SLOT_FUNCTION, + type: NodeTypes.JS_FUNCTION_EXPRESSION, params, returns, + newline, loc } } export function createSequenceExpression( - expressions: JSChildNode[] + expressions: SequenceExpression['expressions'] ): SequenceExpression { return { type: NodeTypes.JS_SEQUENCE_EXPRESSION, @@ -330,9 +334,9 @@ export function createSequenceExpression( } export function createConditionalExpression( - test: ExpressionNode, - consequent: JSChildNode, - alternate: JSChildNode + test: ConditionalExpression['test'], + consequent: ConditionalExpression['consequent'], + alternate: ConditionalExpression['alternate'] ): ConditionalExpression { return { type: NodeTypes.JS_CONDITIONAL_EXPRESSION, diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 0875573e0..c25518c14 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -2,8 +2,6 @@ import { RootNode, TemplateChildNode, ElementNode, - IfNode, - ForNode, TextNode, CommentNode, ExpressionNode, @@ -18,7 +16,7 @@ import { CompoundExpressionNode, SimpleExpressionNode, ElementTypes, - SlotFunctionExpression, + FunctionExpression, SequenceExpression, ConditionalExpression } from './ast' @@ -29,12 +27,7 @@ import { isSimpleIdentifier } from './utils' import { isString, isArray } from '@vue/shared' -import { - RENDER_LIST, - TO_STRING, - CREATE_VNODE, - COMMENT -} from './runtimeConstants' +import { TO_STRING, CREATE_VNODE, COMMENT } from './runtimeConstants' type CodegenNode = TemplateChildNode | JSChildNode @@ -342,7 +335,15 @@ function genNodeList( function genNode(node: CodegenNode, context: CodegenContext) { switch (node.type) { case NodeTypes.ELEMENT: - genElement(node, context) + case NodeTypes.IF: + case NodeTypes.FOR: + __DEV__ && + assert( + node.codegenNode != null, + `Codegen node is missing for element/if/for node. ` + + `Apply appropriate transforms first.` + ) + genNode(node.codegenNode!, context) break case NodeTypes.TEXT: genText(node, context) @@ -359,12 +360,6 @@ function genNode(node: CodegenNode, context: CodegenContext) { case NodeTypes.COMMENT: genComment(node, context) break - case NodeTypes.IF: - genIf(node, context) - break - case NodeTypes.FOR: - genFor(node, context) - break case NodeTypes.JS_CALL_EXPRESSION: genCallExpression(node, context) break @@ -374,8 +369,8 @@ function genNode(node: CodegenNode, context: CodegenContext) { case NodeTypes.JS_ARRAY_EXPRESSION: genArrayExpression(node, context) break - case NodeTypes.JS_SLOT_FUNCTION: - genSlotFunction(node, context) + case NodeTypes.JS_FUNCTION_EXPRESSION: + genFunctionExpression(node, context) break case NodeTypes.JS_SEQUENCE_EXPRESSION: genSequenceExpression(node, context) @@ -394,16 +389,6 @@ function genNode(node: CodegenNode, context: CodegenContext) { } } -function genElement(node: ElementNode, context: CodegenContext) { - __DEV__ && - assert( - node.codegenNode != null, - `AST is not transformed for codegen. ` + - `Apply appropriate transforms first.` - ) - genCallExpression(node.codegenNode!, context, false) -} - function genText( node: TextNode | SimpleExpressionNode, context: CodegenContext @@ -469,56 +454,10 @@ function genComment(node: CommentNode, context: CodegenContext) { } } -// control flow -function genIf(node: IfNode, context: CodegenContext) { - genNode(node.codegenNode, context) -} - -function genFor(node: ForNode, context: CodegenContext) { - const { push, helper, indent, deindent } = context - const { source, keyAlias, valueAlias, objectIndexAlias, children } = node - push(`${helper(RENDER_LIST)}(`, node, true) - genNode(source, context) - push(`, (`) - if (valueAlias) { - genNode(valueAlias, context) - } - if (keyAlias) { - if (!valueAlias) { - push(`__value`) - } - push(`, `) - genNode(keyAlias, context) - } - if (objectIndexAlias) { - if (!keyAlias) { - if (!valueAlias) { - push(`__value, __key`) - } else { - push(`, __key`) - } - } - push(`, `) - genNode(objectIndexAlias, context) - } - push(`) => {`) - indent() - push(`return `) - genChildren(children, context, true) - deindent() - push(`})`) -} - // JavaScript -function genCallExpression( - node: CallExpression, - context: CodegenContext, - multilines = false -) { +function genCallExpression(node: CallExpression, context: CodegenContext) { context.push(node.callee + `(`, node, true) - multilines && context.indent() - genNodeList(node.arguments, context, multilines) - multilines && context.deindent() + genNodeList(node.arguments, context) context.push(`)`) } @@ -554,15 +493,33 @@ function genArrayExpression(node: ArrayExpression, context: CodegenContext) { genNodeListAsArray(node.elements, context) } -function genSlotFunction( - node: SlotFunctionExpression, +function genFunctionExpression( + node: FunctionExpression, context: CodegenContext ) { - context.push(`(`, node) - if (node.params) genNode(node.params, context) - context.push(`) => `) - // pre-normalized slots should always return arrays - genNodeListAsArray(node.returns, context) + const { push, indent, deindent } = context + const { params, returns, newline } = node + push(`(`, node) + if (isArray(params)) { + genNodeList(params, context) + } else if (params) { + genNode(params, context) + } + push(`) => `) + if (newline) { + push(`{`) + indent() + push(`return `) + } + if (isArray(returns)) { + genNodeListAsArray(returns, context) + } else { + genNode(returns, context) + } + if (newline) { + deindent() + push(`}`) + } } function genConditionalExpression( diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index ddfca31f5..0f3a9aedf 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -7,11 +7,20 @@ import { ExpressionNode, createSimpleExpression, SourceLocation, - SimpleExpressionNode + SimpleExpressionNode, + createSequenceExpression, + createCallExpression, + createFunctionExpression, + ElementTypes } from '../ast' import { createCompilerError, ErrorCodes } from '../errors' import { getInnerRange } from '../utils' -import { RENDER_LIST } from '../runtimeConstants' +import { + RENDER_LIST, + OPEN_BLOCK, + CREATE_BLOCK, + FRAGMENT +} from '../runtimeConstants' import { processExpression } from './transformExpression' export const transformFor = createStructuralDirectiveTransform( @@ -26,9 +35,14 @@ export const transformFor = createStructuralDirectiveTransform( ) if (parseResult) { - context.helper(RENDER_LIST) + const { helper, addIdentifiers, removeIdentifiers } = context const { source, value, key, index } = parseResult + const codegenNode = createSequenceExpression([ + createCallExpression(helper(OPEN_BLOCK)) + // to be filled in on exit after children traverse + ]) + context.replaceNode({ type: NodeTypes.FOR, loc: dir.loc, @@ -36,19 +50,52 @@ export const transformFor = createStructuralDirectiveTransform( valueAlias: value, keyAlias: key, objectIndexAlias: index, - children: [node] + children: [node], + codegenNode }) if (!__BROWSER__) { // scope management - const { addIdentifiers, removeIdentifiers } = context - // inject identifiers to context value && addIdentifiers(value) key && addIdentifiers(key) index && addIdentifiers(index) + } - return () => { + return () => { + const params: ExpressionNode[] = [] + if (value) { + params.push(value) + } + if (key) { + if (!value) { + params.push(createSimpleExpression(`_`, false)) + } + params.push(key) + } + if (index) { + if (!key) { + params.push(createSimpleExpression(`__`, false)) + } + params.push(index) + } + + codegenNode.expressions.push( + createCallExpression(helper(CREATE_BLOCK), [ + helper(FRAGMENT), + `null`, + createCallExpression(helper(RENDER_LIST), [ + source, + createFunctionExpression( + params, + node.tagType === ElementTypes.TEMPLATE ? node.children : node, + true /* force newline to make it more readable */ + ) + ]) + ]) + ) + + if (!__BROWSER__) { value && removeIdentifiers(value) key && removeIdentifiers(key) index && removeIdentifiers(index) diff --git a/packages/compiler-core/src/transforms/vIf.ts b/packages/compiler-core/src/transforms/vIf.ts index 456cda0da..43aac0bce 100644 --- a/packages/compiler-core/src/transforms/vIf.ts +++ b/packages/compiler-core/src/transforms/vIf.ts @@ -20,7 +20,9 @@ import { ObjectExpression, createObjectProperty, Property, - ExpressionNode + ExpressionNode, + TemplateChildNode, + FunctionExpression } from '../ast' import { createCompilerError, ErrorCodes } from '../errors' import { processExpression } from './transformExpression' @@ -68,7 +70,7 @@ export const transformIf = createStructuralDirectiveTransform( // transformed. return () => { codegenNode.expressions.push( - createCodegenNodeForBranch(node, branch, 0, context) + createCodegenNodeForBranch(branch, 0, context) ) } } else { @@ -105,7 +107,6 @@ export const transformIf = createStructuralDirectiveTransform( parentCondition = parentCondition.alternate } else { parentCondition.alternate = createCodegenNodeForBranch( - node, branch, sibling.branches.length - 1, context @@ -139,7 +140,6 @@ function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode { } function createCodegenNodeForBranch( - node: ElementNode, branch: IfBranchNode, index: number, context: TransformContext @@ -147,41 +147,50 @@ function createCodegenNodeForBranch( if (branch.condition) { return createConditionalExpression( branch.condition, - createChildrenCodegenNode(node, branch, index, context), + createChildrenCodegenNode(branch, index, context), createCallExpression(context.helper(CREATE_BLOCK), [ context.helper(EMPTY) ]) ) } else { - return createChildrenCodegenNode(node, branch, index, context) + return createChildrenCodegenNode(branch, index, context) } } function createChildrenCodegenNode( - node: ElementNode, branch: IfBranchNode, index: number, { helper }: TransformContext ): CallExpression { - const isTemplate = node.tagType === ElementTypes.TEMPLATE const keyExp = `{ key: ${index} }` - if (isTemplate) { + const { children } = branch + const child = children[0] + const needFragmentWrapper = + children.length > 1 || child.type !== NodeTypes.ELEMENT + if (needFragmentWrapper) { + let fragmentChildren: TemplateChildNode[] | FunctionExpression = children + // optimize away nested fragments when child is a ForNode + if (children.length === 1 && child.type === NodeTypes.FOR) { + fragmentChildren = (child.codegenNode.expressions[1] as CallExpression) + .arguments[2] as FunctionExpression + } return createCallExpression(helper(CREATE_BLOCK), [ helper(FRAGMENT), keyExp, - branch.children + fragmentChildren ]) } else { - let childCodegen = node.codegenNode! - if (childCodegen.callee.includes(APPLY_DIRECTIVES)) { - childCodegen = childCodegen.arguments[0] as CallExpression + const childCodegen = (child as ElementNode).codegenNode! + let vnodeCall = childCodegen + if (vnodeCall.callee.includes(APPLY_DIRECTIVES)) { + vnodeCall = vnodeCall.arguments[0] as CallExpression } // change child to a block - childCodegen.callee = helper(CREATE_BLOCK) + vnodeCall.callee = helper(CREATE_BLOCK) // branch key - const existingProps = childCodegen.arguments[1] + const existingProps = vnodeCall.arguments[1] if (!existingProps || existingProps === `null`) { - childCodegen.arguments[1] = keyExp + vnodeCall.arguments[1] = keyExp } else { // inject branch key if not already have a key const props = existingProps as @@ -202,13 +211,13 @@ function createChildrenCodegenNode( props.properties.unshift(createKeyProperty(index)) } else { // single v-bind with expression - childCodegen.arguments[1] = createCallExpression(helper(MERGE_PROPS), [ + vnodeCall.arguments[1] = createCallExpression(helper(MERGE_PROPS), [ keyExp, props ]) } } - return node.codegenNode! + return childCodegen } } diff --git a/packages/compiler-core/src/transforms/vSlot.ts b/packages/compiler-core/src/transforms/vSlot.ts index 7a1a0f7d3..45f7f00ee 100644 --- a/packages/compiler-core/src/transforms/vSlot.ts +++ b/packages/compiler-core/src/transforms/vSlot.ts @@ -143,6 +143,7 @@ function buildSlot( createFunctionExpression( slotProps, children, + false, children.length ? children[0].loc : loc ) )