diff --git a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap index 8d7613ee..e1408a7a 100644 --- a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap @@ -9,7 +9,7 @@ const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"hello\\", -1 /* 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((_ctx, _cache) => { return (_openBlock(), _createBlock(\\"div\\", null, [ _hoisted_1, _createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */), @@ -22,7 +22,7 @@ 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\\") -export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) { +export const render = /*#__PURE__*/_withId((_ctx, _cache) => { const _component_Child = _resolveComponent(\\"Child\\") return (_openBlock(), _createBlock(_component_Child, null, { @@ -38,7 +38,7 @@ 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\\") -export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) { +export const render = /*#__PURE__*/_withId((_ctx, _cache) => { const _component_Child = _resolveComponent(\\"Child\\") return (_openBlock(), _createBlock(_component_Child, null, _createSlots({ _: 2 }, [ @@ -66,7 +66,7 @@ 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\\") -export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) { +export const render = /*#__PURE__*/_withId((_ctx, _cache) => { const _component_Child = _resolveComponent(\\"Child\\") return (_openBlock(), _createBlock(_component_Child, null, { @@ -85,7 +85,7 @@ 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(function render(_ctx, _cache) { +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 4ca5a585..710c65bf 100644 --- a/packages/compiler-core/__tests__/scopeId.spec.ts +++ b/packages/compiler-core/__tests__/scopeId.spec.ts @@ -22,7 +22,7 @@ describe('scopeId compiler support', () => { expect(ast.helpers).toContain(WITH_SCOPE_ID) expect(code).toMatch(`const _withId = /*#__PURE__*/_withScopeId("test")`) expect(code).toMatch( - `export const render = /*#__PURE__*/_withId(function render(` + `export const render = /*#__PURE__*/_withId((_ctx, _cache) => {` ) expect(code).toMatchSnapshot() }) diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index a92f2597..20f7e33e 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -203,7 +203,7 @@ export function generate( const hasHelpers = ast.helpers.length > 0 const useWithBlock = !prefixIdentifiers && mode !== 'module' const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module' - const isSetupInlined = !!options.inline + const isSetupInlined = !__BROWSER__ && !!options.inline // preambles // in setup() inline mode, the preamble is generated in a sub context @@ -217,6 +217,8 @@ export function generate( genFunctionPreamble(ast, preambleContext) } + // enter render function + const functionName = ssr ? `ssrRender` : `render` const args = ssr ? ['_ctx', '_push', '_parent', '_attrs'] : ['_ctx', '_cache'] if (!__BROWSER__ && options.bindingMetadata && !options.inline) { // binding optimization args @@ -226,24 +228,18 @@ export function generate( !__BROWSER__ && options.isTS ? args.map(arg => `${arg}: any`).join(',') : args.join(', ') - // enter render function - if (!ssr) { + + if (genScopeId) { if (isSetupInlined) { - if (genScopeId) { - push(`${PURE_ANNOTATION}_withId(`) - } - push(`(${signature}) => {`) + push(`${PURE_ANNOTATION}_withId(`) } else { - if (genScopeId) { - push(`const render = ${PURE_ANNOTATION}_withId(`) - } - push(`function render(${signature}) {`) + push(`const ${functionName} = ${PURE_ANNOTATION}_withId(`) } + } + if (isSetupInlined || genScopeId) { + push(`(${signature}) => {`) } else { - if (genScopeId) { - push(`const ssrRender = ${PURE_ANNOTATION}_withId(`) - } - push(`function ssrRender(${signature}) {`) + push(`function ${functionName}(${signature}) {`) } indent() diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap index c2407f8a..80a3bfa6 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap @@ -201,6 +201,40 @@ return (_ctx, _cache) => { }" `; +exports[`SFC compile + + + `, + { + inlineTemplate: true, + templateOptions: { + ssr: true + } + } + ) + expect(content).toMatch(`\n __ssrInlineRender: true,\n`) + expect(content).toMatch(`return (_ctx, _push`) + expect(content).toMatch(`ssrInterpolate`) + assertCode(content) + }) }) describe('with TypeScript', () => { diff --git a/packages/compiler-sfc/__tests__/compileStyle.spec.ts b/packages/compiler-sfc/__tests__/compileStyle.spec.ts index a1b3b21e..8e5db070 100644 --- a/packages/compiler-sfc/__tests__/compileStyle.spec.ts +++ b/packages/compiler-sfc/__tests__/compileStyle.spec.ts @@ -16,7 +16,7 @@ export function compileScoped( const res = compileStyle({ source, filename: 'test.css', - id: 'test', + id: 'data-v-test', scoped: true, ...options }) @@ -32,61 +32,61 @@ export function compileScoped( describe('SFC scoped CSS', () => { test('simple selectors', () => { expect(compileScoped(`h1 { color: red; }`)).toMatch( - `h1[test] { color: red;` + `h1[data-v-test] { color: red;` ) expect(compileScoped(`.foo { color: red; }`)).toMatch( - `.foo[test] { color: red;` + `.foo[data-v-test] { color: red;` ) }) test('descendent selector', () => { expect(compileScoped(`h1 .foo { color: red; }`)).toMatch( - `h1 .foo[test] { color: red;` + `h1 .foo[data-v-test] { color: red;` ) }) test('multiple selectors', () => { expect(compileScoped(`h1 .foo, .bar, .baz { color: red; }`)).toMatch( - `h1 .foo[test], .bar[test], .baz[test] { color: red;` + `h1 .foo[data-v-test], .bar[data-v-test], .baz[data-v-test] { color: red;` ) }) test('pseudo class', () => { expect(compileScoped(`.foo:after { color: red; }`)).toMatch( - `.foo[test]:after { color: red;` + `.foo[data-v-test]:after { color: red;` ) }) test('pseudo element', () => { expect(compileScoped(`::selection { display: none; }`)).toMatch( - '[test]::selection {' + '[data-v-test]::selection {' ) }) test('spaces before pseudo element', () => { const code = compileScoped(`.abc, ::selection { color: red; }`) - expect(code).toMatch('.abc[test],') - expect(code).toMatch('[test]::selection {') + expect(code).toMatch('.abc[data-v-test],') + expect(code).toMatch('[data-v-test]::selection {') }) test('::v-deep', () => { expect(compileScoped(`:deep(.foo) { color: red; }`)).toMatchInlineSnapshot(` - "[test] .foo { color: red; + "[data-v-test] .foo { color: red; }" `) expect(compileScoped(`::v-deep(.foo) { color: red; }`)) .toMatchInlineSnapshot(` - "[test] .foo { color: red; + "[data-v-test] .foo { color: red; }" `) expect(compileScoped(`::v-deep(.foo .bar) { color: red; }`)) .toMatchInlineSnapshot(` - "[test] .foo .bar { color: red; + "[data-v-test] .foo .bar { color: red; }" `) expect(compileScoped(`.baz .qux ::v-deep(.foo .bar) { color: red; }`)) .toMatchInlineSnapshot(` - ".baz .qux[test] .foo .bar { color: red; + ".baz .qux[data-v-test] .foo .bar { color: red; }" `) }) @@ -94,22 +94,22 @@ describe('SFC scoped CSS', () => { test('::v-slotted', () => { expect(compileScoped(`:slotted(.foo) { color: red; }`)) .toMatchInlineSnapshot(` - ".foo[test-s] { color: red; + ".foo[data-v-test-s] { color: red; }" `) expect(compileScoped(`::v-slotted(.foo) { color: red; }`)) .toMatchInlineSnapshot(` - ".foo[test-s] { color: red; + ".foo[data-v-test-s] { color: red; }" `) expect(compileScoped(`::v-slotted(.foo .bar) { color: red; }`)) .toMatchInlineSnapshot(` - ".foo .bar[test-s] { color: red; + ".foo .bar[data-v-test-s] { color: red; }" `) expect(compileScoped(`.baz .qux ::v-slotted(.foo .bar) { color: red; }`)) .toMatchInlineSnapshot(` - ".baz .qux .foo .bar[test-s] { color: red; + ".baz .qux .foo .bar[data-v-test-s] { color: red; }" `) }) @@ -142,7 +142,7 @@ describe('SFC scoped CSS', () => { expect(compileScoped(`@media print { .foo { color: red }}`)) .toMatchInlineSnapshot(` "@media print { - .foo[test] { color: red + .foo[data-v-test] { color: red }}" `) }) @@ -151,7 +151,7 @@ describe('SFC scoped CSS', () => { expect(compileScoped(`@supports(display: grid) { .foo { display: grid }}`)) .toMatchInlineSnapshot(` "@supports(display: grid) { - .foo[test] { display: grid + .foo[data-v-test] { display: grid }}" `) }) @@ -222,7 +222,7 @@ describe('SFC scoped CSS', () => { // vue-loader/#1370 test('spaces after selector', () => { expect(compileScoped(`.foo , .bar { color: red; }`)).toMatchInlineSnapshot(` - ".foo[test], .bar[test] { color: red; + ".foo[data-v-test], .bar[data-v-test] { color: red; }" `) }) @@ -231,12 +231,12 @@ describe('SFC scoped CSS', () => { test('::v-deep as combinator', () => { expect(compileScoped(`::v-deep .foo { color: red; }`)) .toMatchInlineSnapshot(` - "[test] .foo { color: red; + "[data-v-test] .foo { color: red; }" `) expect(compileScoped(`.bar ::v-deep .foo { color: red; }`)) .toMatchInlineSnapshot(` - ".bar[test] .foo { color: red; + ".bar[data-v-test] .foo { color: red; }" `) expect( @@ -247,7 +247,7 @@ describe('SFC scoped CSS', () => { test('>>> (deprecated syntax)', () => { const code = compileScoped(`>>> .foo { color: red; }`) expect(code).toMatchInlineSnapshot(` - "[test] .foo { color: red; + "[data-v-test] .foo { color: red; }" `) expect( @@ -258,7 +258,7 @@ describe('SFC scoped CSS', () => { test('/deep/ (deprecated syntax)', () => { const code = compileScoped(`/deep/ .foo { color: red; }`) expect(code).toMatchInlineSnapshot(` - "[test] .foo { color: red; + "[data-v-test] .foo { color: red; }" `) expect( diff --git a/packages/compiler-sfc/__tests__/compileTemplate.spec.ts b/packages/compiler-sfc/__tests__/compileTemplate.spec.ts index 2a456d8e..576240b5 100644 --- a/packages/compiler-sfc/__tests__/compileTemplate.spec.ts +++ b/packages/compiler-sfc/__tests__/compileTemplate.spec.ts @@ -1,10 +1,20 @@ -import { compileTemplate } from '../src/compileTemplate' +import { + compileTemplate, + SFCTemplateCompileOptions +} from '../src/compileTemplate' import { parse, SFCTemplateBlock } from '../src/parse' +function compile(opts: Omit) { + return compileTemplate({ + ...opts, + id: '' + }) +} + test('should work', () => { const source = `

{{ render }}

` - const result = compileTemplate({ filename: 'example.vue', source }) + const result = compile({ filename: 'example.vue', source }) expect(result.errors.length).toBe(0) expect(result.source).toBe(source) @@ -25,7 +35,7 @@ body { filename: 'example.vue', sourceMap: true } ).descriptor.template as SFCTemplateBlock - const result = compileTemplate({ + const result = compile({ filename: 'example.vue', source: template.content, preprocessLang: template.lang @@ -40,7 +50,7 @@ test('warn missing preprocessor', () => { sourceMap: true }).descriptor.template as SFCTemplateBlock - const result = compileTemplate({ + const result = compile({ filename: 'example.vue', source: template.content, preprocessLang: template.lang @@ -52,7 +62,7 @@ test('warn missing preprocessor', () => { test('transform asset url options', () => { const input = { source: ``, filename: 'example.vue' } // Object option - const { code: code1 } = compileTemplate({ + const { code: code1 } = compile({ ...input, transformAssetUrls: { tags: { foo: ['bar'] } @@ -61,7 +71,7 @@ test('transform asset url options', () => { expect(code1).toMatch(`import _imports_0 from 'baz'\n`) // legacy object option (direct tags config) - const { code: code2 } = compileTemplate({ + const { code: code2 } = compile({ ...input, transformAssetUrls: { foo: ['bar'] @@ -70,7 +80,7 @@ test('transform asset url options', () => { expect(code2).toMatch(`import _imports_0 from 'baz'\n`) // false option - const { code: code3 } = compileTemplate({ + const { code: code3 } = compile({ ...input, transformAssetUrls: false }) @@ -87,7 +97,7 @@ test('source map', () => { { filename: 'example.vue', sourceMap: true } ).descriptor.template as SFCTemplateBlock - const result = compileTemplate({ + const result = compile({ filename: 'example.vue', source: template.content }) @@ -96,7 +106,7 @@ test('source map', () => { }) test('template errors', () => { - const result = compileTemplate({ + const result = compile({ filename: 'example.vue', source: `
` @@ -114,7 +124,7 @@ test('preprocessor errors', () => { { filename: 'example.vue', sourceMap: true } ).descriptor.template as SFCTemplateBlock - const result = compileTemplate({ + const result = compile({ filename: 'example.vue', source: template.content, preprocessLang: template.lang diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index f0f2b511..2e06b5fd 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -167,6 +167,7 @@ export function compileScript( let optionsArg: ObjectExpression | undefined let optionsType: TSTypeLiteral | undefined let hasAwait = false + let hasInlinedSsrRenderFn = false // context types to generate let propsType = `{}` let emitType = `(e: string, ...args: any[]) => void` @@ -820,15 +821,24 @@ export function compileScript( // 10. generate return statement let returned if (options.inlineTemplate) { - if (sfc.template) { + if (sfc.template && !sfc.template.src) { + if (options.templateOptions && options.templateOptions.ssr) { + hasInlinedSsrRenderFn = true + } // inline render function mode - we are going to compile the template and // inline it right here const { code, ast, preamble, tips, errors } = compileTemplate({ - ...options.templateOptions, filename, source: sfc.template.content, inMap: sfc.template.map, + ...options.templateOptions, + id: scopeId, + scoped: sfc.styles.some(s => s.scoped), + isProd: options.isProd, + ssrCssVars: sfc.cssVars, compilerOptions: { + ...(options.templateOptions && + options.templateOptions.compilerOptions), inline: true, isTS, bindingMetadata @@ -883,6 +893,9 @@ export function compileScript( // 11. finalize default export // expose: [] makes