diff --git a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap index 3f9efc6e..ba004bb4 100644 --- a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap @@ -2,14 +2,14 @@ exports[`scopeId compiler support should push scopeId for hoisted nodes 1`] = ` "import { createVNode as _createVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \\"vue\\" -const _withId = /*#__PURE__*/ _withScopeId(\\"test\\") +const _withId = /*#__PURE__*/_withScopeId(\\"test\\") _pushScopeId(\\"test\\") -const _hoisted_1 = /*#__PURE__*/ _createVNode(\\"div\\", null, \\"hello\\", -1 /* HOISTED */) -const _hoisted_2 = /*#__PURE__*/ _createVNode(\\"div\\", null, \\"world\\", -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"hello\\", -1 /* HOISTED */) +const _hoisted_2 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"world\\", -1 /* HOISTED */) _popScopeId() -export const render = /*#__PURE__*/ _withId(function render(_ctx, _cache) { +export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) { return (_openBlock(), _createBlock(\\"div\\", null, [ _hoisted_1, _createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */), @@ -20,9 +20,9 @@ export const render = /*#__PURE__*/ _withId(function render(_ctx, _cache) { exports[`scopeId compiler support should wrap default slot 1`] = ` "import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" -const _withId = /*#__PURE__*/ _withScopeId(\\"test\\") +const _withId = /*#__PURE__*/_withScopeId(\\"test\\") -export const render = /*#__PURE__*/ _withId(function render(_ctx, _cache) { +export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) { const _component_Child = _resolveComponent(\\"Child\\") return (_openBlock(), _createBlock(_component_Child, null, { @@ -36,9 +36,9 @@ export const render = /*#__PURE__*/ _withId(function render(_ctx, _cache) { exports[`scopeId compiler support should wrap dynamic slots 1`] = ` "import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" -const _withId = /*#__PURE__*/ _withScopeId(\\"test\\") +const _withId = /*#__PURE__*/_withScopeId(\\"test\\") -export const render = /*#__PURE__*/ _withId(function render(_ctx, _cache) { +export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) { const _component_Child = _resolveComponent(\\"Child\\") return (_openBlock(), _createBlock(_component_Child, null, _createSlots({ _: 1 }, [ @@ -64,9 +64,9 @@ export const render = /*#__PURE__*/ _withId(function render(_ctx, _cache) { exports[`scopeId compiler support should wrap named slots 1`] = ` "import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" -const _withId = /*#__PURE__*/ _withScopeId(\\"test\\") +const _withId = /*#__PURE__*/_withScopeId(\\"test\\") -export const render = /*#__PURE__*/ _withId(function render(_ctx, _cache) { +export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) { const _component_Child = _resolveComponent(\\"Child\\") return (_openBlock(), _createBlock(_component_Child, null, { @@ -83,9 +83,9 @@ export const render = /*#__PURE__*/ _withId(function render(_ctx, _cache) { exports[`scopeId compiler support should wrap render function 1`] = ` "import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" -const _withId = /*#__PURE__*/ _withScopeId(\\"test\\") +const _withId = /*#__PURE__*/_withScopeId(\\"test\\") -export const render = /*#__PURE__*/ _withId(function render(_ctx, _cache) { +export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) { return (_openBlock(), _createBlock(\\"div\\")) })" `; diff --git a/packages/compiler-core/__tests__/scopeId.spec.ts b/packages/compiler-core/__tests__/scopeId.spec.ts index 0cf24e60..4ca5a585 100644 --- a/packages/compiler-core/__tests__/scopeId.spec.ts +++ b/packages/compiler-core/__tests__/scopeId.spec.ts @@ -20,9 +20,9 @@ describe('scopeId compiler support', () => { scopeId: 'test' }) expect(ast.helpers).toContain(WITH_SCOPE_ID) - expect(code).toMatch(`const _withId = /*#__PURE__*/ _withScopeId("test")`) + expect(code).toMatch(`const _withId = /*#__PURE__*/_withScopeId("test")`) expect(code).toMatch( - `export const render = /*#__PURE__*/ _withId(function render(` + `export const render = /*#__PURE__*/_withId(function render(` ) expect(code).toMatchSnapshot() }) @@ -85,10 +85,10 @@ describe('scopeId compiler support', () => { expect(code).toMatch( [ `_pushScopeId("test")`, - `const _hoisted_1 = /*#__PURE__*/ _createVNode("div", null, "hello", ${genFlagText( + `const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "hello", ${genFlagText( PatchFlags.HOISTED )})`, - `const _hoisted_2 = /*#__PURE__*/ _createVNode("div", null, "world", ${genFlagText( + `const _hoisted_2 = /*#__PURE__*/_createVNode("div", null, "world", ${genFlagText( PatchFlags.HOISTED )})`, `_popScopeId()` diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap index 658e01c3..fcad21be 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap @@ -4,7 +4,7 @@ exports[`compiler: hoistStatic transform hoist element with static key 1`] = ` "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/ _createVNode(\\"div\\", { key: \\"foo\\" }, null, -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", { key: \\"foo\\" }, null, -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { @@ -21,9 +21,9 @@ exports[`compiler: hoistStatic transform hoist nested static tree 1`] = ` "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/ _createVNode(\\"p\\", null, [ - _createVNode(\\"span\\"), - _createVNode(\\"span\\") +const _hoisted_1 = /*#__PURE__*/_createVNode(\\"p\\", null, [ + /*#__PURE__*/_createVNode(\\"span\\"), + /*#__PURE__*/_createVNode(\\"span\\") ], -1 /* HOISTED */) return function render(_ctx, _cache) { @@ -41,8 +41,8 @@ exports[`compiler: hoistStatic transform hoist nested static tree with comments "const _Vue = Vue const { createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/ _createVNode(\\"div\\", null, [ - _createCommentVNode(\\"comment\\") +const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", null, [ + /*#__PURE__*/_createCommentVNode(\\"comment\\") ], -1 /* HOISTED */) return function render(_ctx, _cache) { @@ -60,8 +60,8 @@ exports[`compiler: hoistStatic transform hoist siblings with common non-hoistabl "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/ _createVNode(\\"span\\", null, null, -1 /* HOISTED */) -const _hoisted_2 = /*#__PURE__*/ _createVNode(\\"div\\", null, null, -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createVNode(\\"span\\", null, null, -1 /* HOISTED */) +const _hoisted_2 = /*#__PURE__*/_createVNode(\\"div\\", null, null, -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { @@ -79,7 +79,7 @@ exports[`compiler: hoistStatic transform hoist simple element 1`] = ` "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/ _createVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\", -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\", -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { @@ -172,7 +172,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/ _createVNode(\\"span\\", null, \\"foo \\" + _toDisplayString(1) + \\" \\" + _toDisplayString(true), -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createVNode(\\"span\\", null, \\"foo \\" + /*#__PURE__*/_toDisplayString(1) + \\" \\" + /*#__PURE__*/_toDisplayString(true), -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { @@ -189,7 +189,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/ _createVNode(\\"span\\", { foo: 0 }, _toDisplayString(1), -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createVNode(\\"span\\", { foo: 0 }, /*#__PURE__*/_toDisplayString(1), -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { @@ -346,7 +346,7 @@ exports[`compiler: hoistStatic transform should hoist v-for children if static 1 const { createVNode: _createVNode } = _Vue const _hoisted_1 = { id: \\"foo\\" } -const _hoisted_2 = /*#__PURE__*/ _createVNode(\\"span\\", null, null, -1 /* HOISTED */) +const _hoisted_2 = /*#__PURE__*/_createVNode(\\"span\\", null, null, -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { @@ -371,7 +371,7 @@ const _hoisted_1 = { key: 0, id: \\"foo\\" } -const _hoisted_2 = /*#__PURE__*/ _createVNode(\\"span\\", null, null, -1 /* HOISTED */) +const _hoisted_2 = /*#__PURE__*/_createVNode(\\"span\\", null, null, -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 45e9ef54..7982eeda 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -54,6 +54,8 @@ import { } from './runtimeHelpers' import { ImportItem } from './transform' +const PURE_ANNOTATION = `/*#__PURE__*/` + type CodegenNode = TemplateChildNode | JSChildNode | SSRCodegenNode export interface CodegenResult { @@ -69,6 +71,7 @@ export interface CodegenContext extends Required { column: number offset: number indentLevel: number + pure: boolean map?: SourceMapGenerator helper(key: symbol): string push(code: string, node?: CodegenNode): void @@ -107,6 +110,7 @@ function createCodegenContext( line: 1, offset: 0, indentLevel: 0, + pure: false, map: undefined, helper(key) { return `_${helperNameMap[key]}` @@ -201,7 +205,7 @@ export function generate( // enter render function if (genScopeId && !ssr) { - push(`const render = /*#__PURE__*/ _withId(`) + push(`const render = ${PURE_ANNOTATION}_withId(`) } if (!ssr) { push(`function render(_ctx, _cache) {`) @@ -400,7 +404,9 @@ function genModulePreamble( } if (genScopeId) { - push(`const _withId = /*#__PURE__*/ ${helper(WITH_SCOPE_ID)}("${scopeId}")`) + push( + `const _withId = ${PURE_ANNOTATION}${helper(WITH_SCOPE_ID)}("${scopeId}")` + ) newline() } @@ -432,6 +438,7 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) { if (!hoists.length) { return } + context.pure = true const { push, newline, helper, scopeId, mode } = context const genScopeId = !__BROWSER__ && scopeId != null && mode !== 'function' newline() @@ -445,13 +452,6 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) { hoists.forEach((exp, i) => { push(`const _hoisted_${i + 1} = `) - // make hosit function calls tree-shakable - if ( - exp.type === NodeTypes.VNODE_CALL || - exp.type === NodeTypes.JS_CALL_EXPRESSION - ) { - push(`/*#__PURE__*/ `) - } genNode(exp, context) newline() }) @@ -460,6 +460,7 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) { push(`${helper(POP_SCOPE_ID)}()`) newline() } + context.pure = false } function genImports(importsOptions: ImportItem[], context: CodegenContext) { @@ -634,7 +635,8 @@ function genExpression(node: SimpleExpressionNode, context: CodegenContext) { } function genInterpolation(node: InterpolationNode, context: CodegenContext) { - const { push, helper } = context + const { push, helper, pure } = context + if (pure) push(PURE_ANNOTATION) push(`${helper(TO_DISPLAY_STRING)}(`) genNode(node.content, context) push(`)`) @@ -676,13 +678,16 @@ function genExpressionAsPropertyKey( function genComment(node: CommentNode, context: CodegenContext) { if (__DEV__) { - const { push, helper } = context + const { push, helper, pure } = context + if (pure) { + push(PURE_ANNOTATION) + } push(`${helper(CREATE_COMMENT)}(${JSON.stringify(node.content)})`, node) } } function genVNodeCall(node: VNodeCall, context: CodegenContext) { - const { push, helper } = context + const { push, helper, pure } = context const { tag, props, @@ -699,6 +704,9 @@ function genVNodeCall(node: VNodeCall, context: CodegenContext) { if (isBlock) { push(`(${helper(OPEN_BLOCK)}(${isForBlock ? `true` : ``}), `) } + if (pure) { + push(PURE_ANNOTATION) + } push(helper(isBlock ? CREATE_BLOCK : CREATE_VNODE) + `(`, node) genNodeList( genNullableArgs([tag, props, children, patchFlag, dynamicProps]), @@ -725,12 +733,14 @@ function genNullableArgs(args: any[]): CallExpression['arguments'] { // JavaScript function genCallExpression(node: CallExpression, context: CodegenContext) { - const callee = isString(node.callee) - ? node.callee - : context.helper(node.callee) - context.push(callee + `(`, node) + const { push, helper, pure } = context + const callee = isString(node.callee) ? node.callee : helper(node.callee) + if (pure) { + push(PURE_ANNOTATION) + } + push(callee + `(`, node) genNodeList(node.arguments, context) - context.push(`)`) + push(`)`) } function genObjectExpression(node: ObjectExpression, context: CodegenContext) {