diff --git a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap index 7af32808b17..4076e61db6a 100644 --- a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap @@ -1,51 +1,48 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP 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\\") +"import { createVNode as _createVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createBlock as _createBlock, setScopeId as _setScopeId } from \\"vue\\" -_pushScopeId(\\"test\\") +_setScopeId(\\"test\\") const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"hello\\", -1 /* HOISTED */) const _hoisted_2 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"world\\", -1 /* HOISTED */) -_popScopeId() +_setScopeId(null) -export const render = /*#__PURE__*/_withId((_ctx, _cache) => { +export function render(_ctx, _cache) { return (_openBlock(), _createBlock(\\"div\\", null, [ _hoisted_1, _createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */), _hoisted_2 ])) -})" +}" `; 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\\") +"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\" -export const render = /*#__PURE__*/_withId((_ctx, _cache) => { +export function render(_ctx, _cache) { const _component_Child = _resolveComponent(\\"Child\\") return (_openBlock(), _createBlock(_component_Child, null, { - default: _withId(() => [ + default: _withCtx(() => [ _createVNode(\\"div\\") ]), _: 1 /* STABLE */ })) -})" +}" `; 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\\") +"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\" -export const render = /*#__PURE__*/_withId((_ctx, _cache) => { +export function render(_ctx, _cache) { const _component_Child = _resolveComponent(\\"Child\\") return (_openBlock(), _createBlock(_component_Child, null, _createSlots({ _: 2 /* DYNAMIC */ }, [ (_ctx.ok) ? { name: \\"foo\\", - fn: _withId(() => [ + fn: _withCtx(() => [ _createVNode(\\"div\\") ]) } @@ -53,39 +50,29 @@ export const render = /*#__PURE__*/_withId((_ctx, _cache) => { _renderList(_ctx.list, (i) => { return { name: i, - fn: _withId(() => [ + fn: _withCtx(() => [ _createVNode(\\"div\\") ]) } }) ]), 1024 /* DYNAMIC_SLOTS */)) -})" +}" `; 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\\") +"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\" -export const render = /*#__PURE__*/_withId((_ctx, _cache) => { +export function render(_ctx, _cache) { const _component_Child = _resolveComponent(\\"Child\\") return (_openBlock(), _createBlock(_component_Child, null, { - foo: _withId(({ msg }) => [ + foo: _withCtx(({ msg }) => [ _createTextVNode(_toDisplayString(msg), 1 /* TEXT */) ]), - bar: _withId(() => [ + bar: _withCtx(() => [ _createVNode(\\"div\\") ]), _: 1 /* STABLE */ })) -})" -`; - -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\\") - -export const render = /*#__PURE__*/_withId((_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 710c65bff8d..3c1d6d554b7 100644 --- a/packages/compiler-core/__tests__/scopeId.spec.ts +++ b/packages/compiler-core/__tests__/scopeId.spec.ts @@ -1,12 +1,13 @@ import { baseCompile } from '../src/compile' -import { - WITH_SCOPE_ID, - PUSH_SCOPE_ID, - POP_SCOPE_ID -} from '../src/runtimeHelpers' +import { SET_SCOPE_ID } from '../src/runtimeHelpers' import { PatchFlags } from '@vue/shared' import { genFlagText } from './testUtils' +/** + * Ensure all slot functions are wrapped with _withCtx + * which sets the currentRenderingInstance and currentScopeId when rendering + * the slot. + */ describe('scopeId compiler support', () => { test('should only work in module mode', () => { expect(() => { @@ -14,25 +15,12 @@ describe('scopeId compiler support', () => { }).toThrow(`"scopeId" option is only supported in module mode`) }) - test('should wrap render function', () => { - const { ast, code } = baseCompile(`
`, { - mode: 'module', - scopeId: 'test' - }) - expect(ast.helpers).toContain(WITH_SCOPE_ID) - expect(code).toMatch(`const _withId = /*#__PURE__*/_withScopeId("test")`) - expect(code).toMatch( - `export const render = /*#__PURE__*/_withId((_ctx, _cache) => {` - ) - expect(code).toMatchSnapshot() - }) - test('should wrap default slot', () => { const { code } = baseCompile(`
`, { mode: 'module', scopeId: 'test' }) - expect(code).toMatch(`default: _withId(() => [`) + expect(code).toMatch(`default: _withCtx(() => [`) expect(code).toMatchSnapshot() }) @@ -48,8 +36,8 @@ describe('scopeId compiler support', () => { scopeId: 'test' } ) - expect(code).toMatch(`foo: _withId(({ msg }) => [`) - expect(code).toMatch(`bar: _withId(() => [`) + expect(code).toMatch(`foo: _withCtx(({ msg }) => [`) + expect(code).toMatch(`bar: _withCtx(() => [`) expect(code).toMatchSnapshot() }) @@ -65,8 +53,8 @@ describe('scopeId compiler support', () => { scopeId: 'test' } ) - expect(code).toMatch(/name: "foo",\s+fn: _withId\(/) - expect(code).toMatch(/name: i,\s+fn: _withId\(/) + expect(code).toMatch(/name: "foo",\s+fn: _withCtx\(/) + expect(code).toMatch(/name: i,\s+fn: _withCtx\(/) expect(code).toMatchSnapshot() }) @@ -79,19 +67,18 @@ describe('scopeId compiler support', () => { hoistStatic: true } ) - expect(ast.helpers).toContain(PUSH_SCOPE_ID) - expect(ast.helpers).toContain(POP_SCOPE_ID) + expect(ast.helpers).toContain(SET_SCOPE_ID) expect(ast.hoists.length).toBe(2) expect(code).toMatch( [ - `_pushScopeId("test")`, + `_setScopeId("test")`, `const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "hello", ${genFlagText( PatchFlags.HOISTED )})`, `const _hoisted_2 = /*#__PURE__*/_createVNode("div", null, "world", ${genFlagText( PatchFlags.HOISTED )})`, - `_popScopeId()` + `_setScopeId(null)` ].join('\n') ) expect(code).toMatchSnapshot() diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap index aa98188dbf5..860db2b2e98 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -129,7 +129,7 @@ return function render(_ctx, _cache) { const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, renderSlot: _renderSlot } = _Vue return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item) => { - return _renderSlot($slots, \\"default\\") + return _renderSlot($slots, \\"default\\", {}, undefined, true) }), 256 /* UNKEYED_FRAGMENT */)) } }" @@ -143,7 +143,7 @@ return function render(_ctx, _cache) { const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, renderSlot: _renderSlot } = _Vue return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item) => { - return _renderSlot($slots, \\"default\\") + return _renderSlot($slots, \\"default\\", {}, undefined, true) }), 256 /* UNKEYED_FRAGMENT */)) } }" diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vIf.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vIf.spec.ts.snap index def7f945a51..ec8bf762621 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vIf.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vIf.spec.ts.snap @@ -80,7 +80,7 @@ return function render(_ctx, _cache) { const { renderSlot: _renderSlot, createCommentVNode: _createCommentVNode } = _Vue return ok - ? _renderSlot($slots, \\"default\\", { key: 0 }) + ? _renderSlot($slots, \\"default\\", { key: 0 }, undefined, true) : _createCommentVNode(\\"v-if\\", true) } }" @@ -140,7 +140,7 @@ return function render(_ctx, _cache) { const { renderSlot: _renderSlot, createCommentVNode: _createCommentVNode } = _Vue return ok - ? _renderSlot($slots, \\"default\\", { key: 0 }) + ? _renderSlot($slots, \\"default\\", { key: 0 }, undefined, true) : _createCommentVNode(\\"v-if\\", true) } }" diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap index 1fc484fe52c..aadd81434de 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap @@ -67,7 +67,7 @@ return function render(_ctx, _cache) { return (_openBlock(), _createBlock(\\"div\\", null, [ _cache[1] || ( _setBlockTracking(-1), - _cache[1] = _renderSlot($slots, \\"default\\"), + _cache[1] = _renderSlot($slots, \\"default\\", {}, undefined, true), _setBlockTracking(1), _cache[1] ) diff --git a/packages/compiler-core/__tests__/transforms/transformSlotOutlet.spec.ts b/packages/compiler-core/__tests__/transforms/transformSlotOutlet.spec.ts index 82a5c8e50e1..3cfa8d07757 100644 --- a/packages/compiler-core/__tests__/transforms/transformSlotOutlet.spec.ts +++ b/packages/compiler-core/__tests__/transforms/transformSlotOutlet.spec.ts @@ -16,6 +16,7 @@ import { transformSlotOutlet } from '../../src/transforms/transformSlotOutlet' function parseWithSlots(template: string, options: CompilerOptions = {}) { const ast = parse(template) transform(ast, { + slotted: false, nodeTransforms: [ ...(options.prefixIdentifiers ? [transformExpression] : []), transformSlotOutlet, @@ -339,6 +340,15 @@ describe('compiler: transform outlets', () => { }) }) + test('slot with slotted: true', async () => { + const ast = parseWithSlots(``, { slotted: true }) + expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({ + type: NodeTypes.JS_CALL_EXPRESSION, + callee: RENDER_SLOT, + arguments: [`$slots`, `"default"`, `{}`, `undefined`, `true`] + }) + }) + test(`error on unexpected custom directive on `, () => { const onError = jest.fn() const source = `` diff --git a/packages/compiler-core/__tests__/transforms/vIf.spec.ts b/packages/compiler-core/__tests__/transforms/vIf.spec.ts index f1b91cc4567..846a4d9a956 100644 --- a/packages/compiler-core/__tests__/transforms/vIf.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vIf.spec.ts @@ -404,7 +404,13 @@ describe('compiler: v-if', () => { expect(codegenNode.consequent).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, callee: RENDER_SLOT, - arguments: ['$slots', '"default"', createObjectMatcher({ key: `[0]` })] + arguments: [ + '$slots', + '"default"', + createObjectMatcher({ key: `[0]` }), + 'undefined', + 'true' + ] }) expect(generate(root).code).toMatchSnapshot() }) @@ -417,7 +423,13 @@ describe('compiler: v-if', () => { expect(codegenNode.consequent).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, callee: RENDER_SLOT, - arguments: ['$slots', '"default"', createObjectMatcher({ key: `[0]` })] + arguments: [ + '$slots', + '"default"', + createObjectMatcher({ key: `[0]` }), + 'undefined', + 'true' + ] }) expect(generate(root).code).toMatchSnapshot() }) diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 20f7e33ec88..cd572313286 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -43,9 +43,7 @@ import { SET_BLOCK_TRACKING, CREATE_COMMENT, CREATE_TEXT, - PUSH_SCOPE_ID, - POP_SCOPE_ID, - WITH_SCOPE_ID, + SET_SCOPE_ID, WITH_DIRECTIVES, CREATE_BLOCK, OPEN_BLOCK, @@ -197,12 +195,11 @@ export function generate( indent, deindent, newline, - scopeId, ssr } = context + const hasHelpers = ast.helpers.length > 0 const useWithBlock = !prefixIdentifiers && mode !== 'module' - const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module' const isSetupInlined = !__BROWSER__ && !!options.inline // preambles @@ -212,7 +209,7 @@ export function generate( ? createCodegenContext(ast, options) : context if (!__BROWSER__ && mode === 'module') { - genModulePreamble(ast, preambleContext, genScopeId, isSetupInlined) + genModulePreamble(ast, preambleContext, isSetupInlined) } else { genFunctionPreamble(ast, preambleContext) } @@ -229,14 +226,7 @@ export function generate( ? args.map(arg => `${arg}: any`).join(',') : args.join(', ') - if (genScopeId) { - if (isSetupInlined) { - push(`${PURE_ANNOTATION}_withId(`) - } else { - push(`const ${functionName} = ${PURE_ANNOTATION}_withId(`) - } - } - if (isSetupInlined || genScopeId) { + if (isSetupInlined) { push(`(${signature}) => {`) } else { push(`function ${functionName}(${signature}) {`) @@ -301,10 +291,6 @@ export function generate( deindent() push(`}`) - if (genScopeId) { - push(`)`) - } - return { ast, code: context.code, @@ -375,23 +361,20 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) { function genModulePreamble( ast: RootNode, context: CodegenContext, - genScopeId: boolean, inline?: boolean ) { const { push, - helper, newline, - scopeId, optimizeImports, - runtimeModuleName + runtimeModuleName, + scopeId, + mode } = context - if (genScopeId) { - ast.helpers.push(WITH_SCOPE_ID) - if (ast.hoists.length) { - ast.helpers.push(PUSH_SCOPE_ID, POP_SCOPE_ID) - } + const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module' + if (genScopeId && ast.hoists.length) { + ast.helpers.push(SET_SCOPE_ID) } // generate import statements for helpers @@ -434,13 +417,6 @@ function genModulePreamble( newline() } - if (genScopeId) { - push( - `const _withId = ${PURE_ANNOTATION}${helper(WITH_SCOPE_ID)}("${scopeId}")` - ) - newline() - } - genHoists(ast.hoists, context) newline() @@ -480,7 +456,7 @@ function genHoists(hoists: (JSChildNode | null)[], context: CodegenContext) { // push scope Id before initializing hoisted vnodes so that these vnodes // get the proper scopeId as well. if (genScopeId) { - push(`${helper(PUSH_SCOPE_ID)}("${scopeId}")`) + push(`${helper(SET_SCOPE_ID)}("${scopeId}")`) newline() } @@ -493,7 +469,7 @@ function genHoists(hoists: (JSChildNode | null)[], context: CodegenContext) { }) if (genScopeId) { - push(`${helper(POP_SCOPE_ID)}()`) + push(`${helper(SET_SCOPE_ID)}(null)`) newline() } context.pure = false @@ -817,15 +793,11 @@ function genFunctionExpression( node: FunctionExpression, context: CodegenContext ) { - const { push, indent, deindent, scopeId, mode } = context + const { push, indent, deindent } = context const { params, returns, body, newline, isSlot } = node - // slot functions also need to push scopeId before rendering its content - const genScopeId = - !__BROWSER__ && isSlot && scopeId != null && mode !== 'function' - if (genScopeId) { - push(`_withId(`) - } else if (isSlot) { + if (isSlot) { + // wrap slot functions with owner context push(`_${helperNameMap[WITH_CTX]}(`) } push(`(`, node) @@ -855,7 +827,7 @@ function genFunctionExpression( deindent() push(`}`) } - if (genScopeId || isSlot) { + if (isSlot) { push(`)`) } } diff --git a/packages/compiler-core/src/options.ts b/packages/compiler-core/src/options.ts index 2850da196b1..d8d2573a6e8 100644 --- a/packages/compiler-core/src/options.ts +++ b/packages/compiler-core/src/options.ts @@ -199,6 +199,12 @@ export interface TransformOptions extends SharedTransformCodegenOptions { * SFC scoped styles ID */ scopeId?: string | null + /** + * Indicates this SFC template has used :slotted in its styles + * Defaults to `true` for backwards compatibility - SFC tooling should set it + * to `false` if no `:slotted` usage is detected in ``).descriptor + .slotted + ).toBe(false) + expect( + parse( + `` + ).descriptor.slotted + ).toBe(true) + expect( + parse( + `` + ).descriptor.slotted + ).toBe(true) + }) + test('error tolerance', () => { const { errors } = parse(`