--- /dev/null
+/**
+ * @jest-environment node
+ */
+
+import { createApp } from 'vue'
+import { renderToString } from '../src/renderToString'
+
+describe('ssr: compiler options', () => {
+ test('config.isCustomElement (deprecated)', async () => {
+ const app = createApp({
+ template: `<div><x-button/></div>`
+ })
+ app.config.isCustomElement = tag => tag.startsWith('x-')
+ expect(await renderToString(app)).toBe(`<div><x-button></x-button></div>`)
+ })
+
+ test('config.compilerOptions.isCustomElement', async () => {
+ const app = createApp({
+ template: `<div><x-panel/></div>`
+ })
+ app.config.compilerOptions.isCustomElement = tag => tag.startsWith('x-')
+ expect(await renderToString(app)).toBe(`<div><x-panel></x-panel></div>`)
+ })
+
+ test('component.compilerOptions.isCustomElement', async () => {
+ const app = createApp({
+ template: `<div><x-card/><y-child/></div>`,
+ compilerOptions: {
+ isCustomElement: (tag: string) => tag.startsWith('x-')
+ },
+ components: {
+ YChild: {
+ template: `<div><y-button/></div>`
+ }
+ }
+ })
+ app.config.compilerOptions.isCustomElement = tag => tag.startsWith('y-')
+ expect(await renderToString(app)).toBe(
+ `<div><x-card></x-card><div><y-button></y-button></div></div>`
+ )
+ })
+
+ test('component.delimiters (deprecated)', async () => {
+ const app = createApp({
+ template: `<div>[[ 1 + 1 ]]</div>`,
+ delimiters: ['[[', ']]']
+ })
+ expect(await renderToString(app)).toBe(`<div>2</div>`)
+ })
+
+ test('config.compilerOptions.delimiters', async () => {
+ const app = createApp({
+ template: `<div>[( 1 + 1 )]</div>`
+ })
+ app.config.compilerOptions.delimiters = ['[(', ')]']
+ expect(await renderToString(app)).toBe(`<div>2</div>`)
+ })
+
+ test('component.compilerOptions.delimiters', async () => {
+ const app = createApp({
+ template: `<div>[[ 1 + 1 ]]<ChildComponent/></div>`,
+ compilerOptions: {
+ delimiters: ['[[', ']]']
+ },
+ components: {
+ ChildComponent: {
+ template: `<div>(( 2 + 2 ))</div>`
+ }
+ }
+ })
+ app.config.compilerOptions.delimiters = ['((', '))']
+ expect(await renderToString(app)).toBe(`<div>2<div>4</div></div>`)
+ })
+
+ test('compilerOptions.whitespace', async () => {
+ const app = createApp({
+ template: `<div><span>Hello world</span><ChildComponent/></div>`,
+ compilerOptions: {
+ whitespace: 'condense'
+ },
+ components: {
+ ChildComponent: {
+ template: `<span>Hello world</span>`
+ }
+ }
+ })
+ app.config.compilerOptions.whitespace = 'preserve'
+ expect(await renderToString(app)).toBe(
+ `<div><span>Hello world</span><span>Hello world</span></div>`
+ )
+ })
+
+ test('caching with compilerOptions', async () => {
+ const template = `<div>{{1 + 1}} [[1 + 1]]</div>`
+
+ const app = createApp({
+ template: `<div><ChildOne/><ChildTwo/><ChildThree/></div>`,
+ components: {
+ ChildOne: {
+ template
+ },
+ ChildTwo: {
+ template,
+ compilerOptions: {
+ whitespace: 'preserve'
+ }
+ },
+ ChildThree: {
+ template,
+ compilerOptions: {
+ delimiters: ['[[', ']]']
+ }
+ }
+ }
+ })
+ expect(await renderToString(app)).toBe(
+ `<div><div>2 [[1 + 1]]</div><div>2 [[1 + 1]]</div><div>{{1 + 1}} 2</div></div>`
+ )
+ })
+
+ test('caching with isCustomElement', async () => {
+ const template = `<div><MyChild/></div>`
+
+ const app = createApp({
+ template,
+ // No compilerOptions on the root
+ components: {
+ MyChild: {
+ template,
+ compilerOptions: {
+ isCustomElement: tag => tag.startsWith('x-')
+ },
+ components: {
+ MyChild: {
+ template,
+ compilerOptions: {
+ isCustomElement: tag => tag.startsWith('My')
+ }
+ }
+ }
+ }
+ }
+ })
+ expect(await renderToString(app)).toBe(
+ `<div><div><div><MyChild></MyChild></div></div></div>`
+ )
+ })
+})
-import { ComponentInternalInstance, warn } from 'vue'
+import { ComponentInternalInstance, ComponentOptions, warn } from 'vue'
import { compile } from '@vue/compiler-ssr'
-import { generateCodeFrame, NO } from '@vue/shared'
-import { CompilerError } from '@vue/compiler-core'
+import { extend, generateCodeFrame, isFunction, NO } from '@vue/shared'
+import { CompilerError, CompilerOptions } from '@vue/compiler-core'
import { PushFn } from '../render'
type SSRRenderFunction = (
)
}
- const cached = compileCache[template]
+ // TODO: This is copied from runtime-core/src/component.ts and should probably be refactored
+ const Component = instance.type as ComponentOptions
+ const { isCustomElement, compilerOptions } = instance.appContext.config
+ const { delimiters, compilerOptions: componentCompilerOptions } = Component
+
+ const finalCompilerOptions: CompilerOptions = extend(
+ extend(
+ {
+ isCustomElement,
+ delimiters
+ },
+ compilerOptions
+ ),
+ componentCompilerOptions
+ )
+
+ finalCompilerOptions.isCustomElement =
+ finalCompilerOptions.isCustomElement || NO
+ finalCompilerOptions.isNativeTag = finalCompilerOptions.isNativeTag || NO
+
+ const cacheKey = JSON.stringify(
+ {
+ template,
+ compilerOptions: finalCompilerOptions
+ },
+ (key, value) => {
+ return isFunction(value) ? value.toString() : value
+ }
+ )
+
+ const cached = compileCache[cacheKey]
if (cached) {
return cached
}
- const { code } = compile(template, {
- isCustomElement: instance.appContext.config.isCustomElement || NO,
- isNativeTag: instance.appContext.config.isNativeTag || NO,
- onError(err: CompilerError) {
- if (__DEV__) {
- const message = `[@vue/server-renderer] Template compilation error: ${err.message}`
- const codeFrame =
- err.loc &&
- generateCodeFrame(
- template as string,
- err.loc.start.offset,
- err.loc.end.offset
- )
- warn(codeFrame ? `${message}\n${codeFrame}` : message)
- } else {
- throw err
- }
+ finalCompilerOptions.onError = (err: CompilerError) => {
+ if (__DEV__) {
+ const message = `[@vue/server-renderer] Template compilation error: ${err.message}`
+ const codeFrame =
+ err.loc &&
+ generateCodeFrame(
+ template as string,
+ err.loc.start.offset,
+ err.loc.end.offset
+ )
+ warn(codeFrame ? `${message}\n${codeFrame}` : message)
+ } else {
+ throw err
}
- })
- return (compileCache[template] = Function('require', code)(require))
+ }
+
+ const { code } = compile(template, finalCompilerOptions)
+ return (compileCache[cacheKey] = Function('require', code)(require))
}