wip: more consistent compiler-sfc usage + inline mode for ssr
This commit is contained in:
parent
58227e88e9
commit
8ac2241b22
@ -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\\"))
|
||||
})"
|
||||
`;
|
||||
|
@ -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()
|
||||
})
|
||||
|
@ -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()
|
||||
|
||||
|
@ -201,6 +201,40 @@ return (_ctx, _cache) => {
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> inlineTemplate mode ssr codegen 1`] = `
|
||||
"import { useCssVars as _useCssVars, unref as _unref } from 'vue'
|
||||
import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from \\"@vue/server-renderer\\"
|
||||
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
expose: [],
|
||||
__ssrInlineRender: true,
|
||||
setup(__props) {
|
||||
|
||||
_useCssVars(_ctx => ({
|
||||
\\"xxxxxxxx-count\\": (count.value)
|
||||
}))
|
||||
|
||||
const count = ref(0)
|
||||
|
||||
return (_ctx, _push, _parent, _attrs) => {
|
||||
const _cssVars = { style: {
|
||||
\\"xxxxxxxx-count\\": (count.value)
|
||||
}}
|
||||
_push(\`<!--[--><div\${
|
||||
_ssrRenderAttrs(_cssVars)
|
||||
}>\${
|
||||
_ssrInterpolate(count.value)
|
||||
}</div><div\${
|
||||
_ssrRenderAttrs(_cssVars)
|
||||
}>static</div><!--]-->\`)
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> inlineTemplate mode template assignment expression codegen 1`] = `
|
||||
"import { createVNode as _createVNode, isRef as _isRef, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BindingTypes } from '@vue/compiler-dom/src'
|
||||
import { BindingTypes } from '@vue/compiler-dom'
|
||||
import { compileSFCScript as compile, assertCode } from './utils'
|
||||
|
||||
describe('SFC compile <script setup>', () => {
|
||||
@ -297,6 +297,34 @@ const bar = 1
|
||||
expect(content).toMatch(`{ lett: lett } = val`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('ssr codegen', () => {
|
||||
const { content } = compile(
|
||||
`
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const count = ref(0)
|
||||
</script>
|
||||
<template>
|
||||
<div>{{ count }}</div>
|
||||
<div>static</div>
|
||||
</template>
|
||||
<style>
|
||||
div { color: v-bind(count) }
|
||||
</style>
|
||||
`,
|
||||
{
|
||||
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', () => {
|
||||
|
@ -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(
|
||||
|
@ -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<SFCTemplateCompileOptions, 'id'>) {
|
||||
return compileTemplate({
|
||||
...opts,
|
||||
id: ''
|
||||
})
|
||||
}
|
||||
|
||||
test('should work', () => {
|
||||
const source = `<div><p>{{ render }}</p></div>`
|
||||
|
||||
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: `<foo bar="~baz"/>`, 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: `<div :foo
|
||||
:bar="a[" v-model="baz"/>`
|
||||
@ -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
|
||||
|
@ -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 <script setup> components "closed" by default.
|
||||
let runtimeOptions = `\n expose: [],`
|
||||
if (hasInlinedSsrRenderFn) {
|
||||
runtimeOptions += `\n __ssrInlineRender: true,`
|
||||
}
|
||||
if (optionsArg) {
|
||||
runtimeOptions += `\n ${scriptSetup.content
|
||||
.slice(optionsArg.start! + 1, optionsArg.end! - 1)
|
||||
|
@ -20,15 +20,19 @@ export interface SFCStyleCompileOptions {
|
||||
source: string
|
||||
filename: string
|
||||
id: string
|
||||
map?: RawSourceMap
|
||||
scoped?: boolean
|
||||
trim?: boolean
|
||||
isProd?: boolean
|
||||
inMap?: RawSourceMap
|
||||
preprocessLang?: PreprocessLang
|
||||
preprocessOptions?: any
|
||||
preprocessCustomRequire?: (id: string) => any
|
||||
postcssOptions?: any
|
||||
postcssPlugins?: any[]
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
map?: RawSourceMap
|
||||
}
|
||||
|
||||
export interface SFCAsyncStyleCompileOptions extends SFCStyleCompileOptions {
|
||||
@ -92,16 +96,21 @@ export function doCompileStyle(
|
||||
} = options
|
||||
const preprocessor = preprocessLang && processors[preprocessLang]
|
||||
const preProcessedSource = preprocessor && preprocess(options, preprocessor)
|
||||
const map = preProcessedSource ? preProcessedSource.map : options.map
|
||||
const map = preProcessedSource
|
||||
? preProcessedSource.map
|
||||
: options.inMap || options.map
|
||||
const source = preProcessedSource ? preProcessedSource.code : options.source
|
||||
|
||||
const shortId = id.replace(/^data-v-/, '')
|
||||
const longId = `data-v-${shortId}`
|
||||
|
||||
const plugins = (postcssPlugins || []).slice()
|
||||
plugins.unshift(cssVarsPlugin({ id, isProd }))
|
||||
plugins.unshift(cssVarsPlugin({ id: shortId, isProd }))
|
||||
if (trim) {
|
||||
plugins.push(trimPlugin())
|
||||
}
|
||||
if (scoped) {
|
||||
plugins.push(scopedPlugin(id))
|
||||
plugins.push(scopedPlugin(longId))
|
||||
}
|
||||
let cssModules: Record<string, string> | undefined
|
||||
if (modules) {
|
||||
|
@ -23,6 +23,7 @@ import * as CompilerDOM from '@vue/compiler-dom'
|
||||
import * as CompilerSSR from '@vue/compiler-ssr'
|
||||
import consolidate from 'consolidate'
|
||||
import { warnOnce } from './warn'
|
||||
import { genCssVarsFromList } from './cssVars'
|
||||
|
||||
export interface TemplateCompiler {
|
||||
compile(template: string, options: CompilerOptions): CodegenResult
|
||||
@ -42,7 +43,11 @@ export interface SFCTemplateCompileResults {
|
||||
export interface SFCTemplateCompileOptions {
|
||||
source: string
|
||||
filename: string
|
||||
id: string
|
||||
scoped?: boolean
|
||||
isProd?: boolean
|
||||
ssr?: boolean
|
||||
ssrCssVars?: string[]
|
||||
inMap?: RawSourceMap
|
||||
compiler?: TemplateCompiler
|
||||
compilerOptions?: CompilerOptions
|
||||
@ -151,9 +156,13 @@ export function compileTemplate(
|
||||
|
||||
function doCompileTemplate({
|
||||
filename,
|
||||
id,
|
||||
scoped,
|
||||
inMap,
|
||||
source,
|
||||
ssr = false,
|
||||
ssrCssVars,
|
||||
isProd = false,
|
||||
compiler = ssr ? (CompilerSSR as TemplateCompiler) : CompilerDOM,
|
||||
compilerOptions = {},
|
||||
transformAssetUrls
|
||||
@ -171,19 +180,30 @@ function doCompileTemplate({
|
||||
nodeTransforms = [transformAssetUrl, transformSrcset]
|
||||
}
|
||||
|
||||
if (ssr && compilerOptions.ssrCssVars == null) {
|
||||
if (ssr && !ssrCssVars) {
|
||||
warnOnce(
|
||||
`compileTemplate is called with \`ssr: true\` but no ` +
|
||||
`corresponding \`ssrCssVars\` option. The value can be generated by ` +
|
||||
`calling \`generateCssVars(sfcDescriptor, scopeId, isProduction)\`.`
|
||||
`corresponding \`cssVars\` option.\`.`
|
||||
)
|
||||
}
|
||||
if (!id) {
|
||||
warnOnce(`compileTemplate now requires the \`id\` option.\`.`)
|
||||
id = ''
|
||||
}
|
||||
|
||||
const shortId = id.replace(/^data-v-/, '')
|
||||
const longId = `data-v-${shortId}`
|
||||
|
||||
let { code, ast, preamble, map } = compiler.compile(source, {
|
||||
mode: 'module',
|
||||
prefixIdentifiers: true,
|
||||
hoistStatic: true,
|
||||
cacheHandlers: true,
|
||||
ssrCssVars:
|
||||
ssr && ssrCssVars && ssrCssVars.length
|
||||
? genCssVarsFromList(ssrCssVars, shortId, isProd)
|
||||
: '',
|
||||
scopeId: scoped ? longId : undefined,
|
||||
...compilerOptions,
|
||||
nodeTransforms: nodeTransforms.concat(compilerOptions.nodeTransforms || []),
|
||||
filename,
|
||||
|
@ -16,26 +16,13 @@ import hash from 'hash-sum'
|
||||
export const CSS_VARS_HELPER = `useCssVars`
|
||||
export const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g
|
||||
|
||||
/**
|
||||
* Given an SFC descriptor, generate the CSS variables object string that can be
|
||||
* passed to `compileTemplate` as `compilerOptions.ssrCssVars`.
|
||||
* @public
|
||||
*/
|
||||
export function generateCssVars(
|
||||
sfc: SFCDescriptor,
|
||||
id: string,
|
||||
isProd: boolean
|
||||
): string {
|
||||
return sfc.cssVars.length ? genCssVarsFromList(sfc.cssVars, id, isProd) : ''
|
||||
}
|
||||
|
||||
function genCssVarsFromList(
|
||||
export function genCssVarsFromList(
|
||||
vars: string[],
|
||||
id: string,
|
||||
isProd: boolean
|
||||
): string {
|
||||
return `{\n ${vars
|
||||
.map(v => `"${genVarName(id, v, isProd)}": (${v})`)
|
||||
.map(key => `"${genVarName(id, key, isProd)}": (${key})`)
|
||||
.join(',\n ')}\n}`
|
||||
}
|
||||
|
||||
@ -68,12 +55,11 @@ export const cssVarsPlugin = postcss.plugin<CssVarsPluginOptions>(
|
||||
'vue-scoped',
|
||||
opts => (root: Root) => {
|
||||
const { id, isProd } = opts!
|
||||
const shortId = id.replace(/^data-v-/, '')
|
||||
root.walkDecls(decl => {
|
||||
// rewrite CSS variables
|
||||
if (cssVarRE.test(decl.value)) {
|
||||
decl.value = decl.value.replace(cssVarRE, (_, $1, $2, $3) => {
|
||||
return `var(--${genVarName(shortId, $1 || $2 || $3, isProd)})`
|
||||
return `var(--${genVarName(id, $1 || $2 || $3, isProd)})`
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -5,7 +5,6 @@ export { compileStyle, compileStyleAsync } from './compileStyle'
|
||||
export { compileScript } from './compileScript'
|
||||
export { rewriteDefault } from './rewriteDefault'
|
||||
export { generateCodeFrame } from '@vue/compiler-core'
|
||||
export { generateCssVars } from './cssVars'
|
||||
|
||||
// Types
|
||||
export {
|
||||
|
@ -14,7 +14,7 @@ describe('ssr: scopeId', () => {
|
||||
import { ssrRenderAttrs as _ssrRenderAttrs } from \\"@vue/server-renderer\\"
|
||||
const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
|
||||
|
||||
export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
export const ssrRender = /*#__PURE__*/_withId((_ctx, _push, _parent, _attrs) => {
|
||||
_push(\`<div\${_ssrRenderAttrs(_attrs)} data-v-xxxxxxx><span data-v-xxxxxxx>hello</span></div>\`)
|
||||
})"
|
||||
`)
|
||||
@ -32,7 +32,7 @@ describe('ssr: scopeId', () => {
|
||||
import { ssrRenderComponent as _ssrRenderComponent } from \\"@vue/server-renderer\\"
|
||||
const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
|
||||
|
||||
export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
export const ssrRender = /*#__PURE__*/_withId((_ctx, _push, _parent, _attrs) => {
|
||||
const _component_foo = _resolveComponent(\\"foo\\")
|
||||
|
||||
_push(_ssrRenderComponent(_component_foo, _attrs, {
|
||||
@ -62,7 +62,7 @@ describe('ssr: scopeId', () => {
|
||||
import { ssrRenderComponent as _ssrRenderComponent } from \\"@vue/server-renderer\\"
|
||||
const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
|
||||
|
||||
export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
export const ssrRender = /*#__PURE__*/_withId((_ctx, _push, _parent, _attrs) => {
|
||||
const _component_foo = _resolveComponent(\\"foo\\")
|
||||
|
||||
_push(_ssrRenderComponent(_component_foo, _attrs, {
|
||||
@ -92,7 +92,7 @@ describe('ssr: scopeId', () => {
|
||||
import { ssrRenderComponent as _ssrRenderComponent } from \\"@vue/server-renderer\\"
|
||||
const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
|
||||
|
||||
export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
export const ssrRender = /*#__PURE__*/_withId((_ctx, _push, _parent, _attrs) => {
|
||||
const _component_foo = _resolveComponent(\\"foo\\")
|
||||
const _component_bar = _resolveComponent(\\"bar\\")
|
||||
|
||||
|
@ -223,6 +223,11 @@ export interface ComponentInternalInstance {
|
||||
* @internal
|
||||
*/
|
||||
render: InternalRenderFunction | null
|
||||
/**
|
||||
* SSR render function
|
||||
* @internal
|
||||
*/
|
||||
ssrRender?: Function | null
|
||||
/**
|
||||
* Object containing values this component provides for its descendents
|
||||
* @internal
|
||||
@ -610,7 +615,13 @@ export function handleSetupResult(
|
||||
) {
|
||||
if (isFunction(setupResult)) {
|
||||
// setup returned an inline render function
|
||||
instance.render = setupResult as InternalRenderFunction
|
||||
if (!__BROWSER__ && (instance.type as ComponentOptions).__ssrInlineRender) {
|
||||
// when the function's name is `ssrRender` (compiled by SFC inline mode),
|
||||
// set it as ssrRender instead.
|
||||
instance.ssrRender = setupResult
|
||||
} else {
|
||||
instance.render = setupResult as InternalRenderFunction
|
||||
}
|
||||
} else if (isObject(setupResult)) {
|
||||
if (__DEV__ && isVNode(setupResult)) {
|
||||
warn(
|
||||
|
@ -121,7 +121,6 @@ export interface ComponentOptionsBase<
|
||||
/**
|
||||
* SSR only. This is produced by compiler-ssr and attached in compiler-sfc
|
||||
* not user facing, so the typing is lax and for test only.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
ssrRender?: (
|
||||
@ -136,6 +135,13 @@ export interface ComponentOptionsBase<
|
||||
$options: ComponentInternalInstance['ctx']
|
||||
) => void
|
||||
|
||||
/**
|
||||
* Only generated by compiler-sfc to mark a ssr render function inlined and
|
||||
* returned from setup()
|
||||
* @internal
|
||||
*/
|
||||
__ssrInlineRender?: boolean
|
||||
|
||||
/**
|
||||
* marker for AsyncComponentWrapper
|
||||
* @internal
|
||||
|
@ -14,6 +14,8 @@ import { ShapeFlags } from '@vue/shared'
|
||||
* @private
|
||||
*/
|
||||
export function useCssVars(getter: (ctx: any) => Record<string, string>) {
|
||||
if (!__BROWSER__ && !__TEST__) return
|
||||
|
||||
const instance = getCurrentInstance()
|
||||
/* istanbul ignore next */
|
||||
if (!instance) {
|
||||
|
@ -115,11 +115,17 @@ function renderComponentSubTree(
|
||||
instance
|
||||
)
|
||||
} else {
|
||||
if (!instance.render && !comp.ssrRender && isString(comp.template)) {
|
||||
if (
|
||||
!instance.render &&
|
||||
!instance.ssrRender &&
|
||||
!comp.ssrRender &&
|
||||
isString(comp.template)
|
||||
) {
|
||||
comp.ssrRender = ssrCompile(comp.template, instance)
|
||||
}
|
||||
|
||||
if (comp.ssrRender) {
|
||||
const ssrRender = instance.ssrRender || comp.ssrRender
|
||||
if (ssrRender) {
|
||||
// optimized
|
||||
// resolve fallthrough attrs
|
||||
let attrs =
|
||||
@ -138,7 +144,7 @@ function renderComponentSubTree(
|
||||
|
||||
// set current rendering instance for asset resolution
|
||||
setCurrentRenderingInstance(instance)
|
||||
comp.ssrRender(
|
||||
ssrRender(
|
||||
instance.proxy,
|
||||
push,
|
||||
instance,
|
||||
|
@ -129,7 +129,7 @@ function createConfig(format, output, plugins = []) {
|
||||
[
|
||||
...Object.keys(pkg.dependencies || {}),
|
||||
...Object.keys(pkg.peerDependencies || {}),
|
||||
...['path', 'url'] // for @vue/compiler-sfc
|
||||
...['path', 'url', 'stream'] // for @vue/compiler-sfc / server-renderer
|
||||
]
|
||||
|
||||
// the browser builds of @vue/compiler-sfc requires postcss to be available
|
||||
|
Loading…
Reference in New Issue
Block a user