From fda2c9bdb78a00f7ffc9868111ae9083773071dd Mon Sep 17 00:00:00 2001
From: likui <2218301630@qq.com>
Date: Wed, 1 Jul 2020 10:53:19 +0800
Subject: [PATCH 1/7] feat: custom template compiler for sfc

close #480
---
 src/node/config.ts                 |  5 +++++
 src/node/server/serverPluginVue.ts | 28 +++++++++++++++++++++++-----
 2 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/src/node/config.ts b/src/node/config.ts
index 3a2fd3317cb0fe..829b049226b6d2 100644
--- a/src/node/config.ts
+++ b/src/node/config.ts
@@ -18,6 +18,7 @@ import { DepOptimizationOptions } from './optimizer'
 import { IKoaProxiesOptions } from 'koa-proxies'
 import { ServerOptions } from 'https'
 import { lookupFile } from './utils'
+import { TemplateCompilerOptions } from './server/serverPluginVue'
 
 export { Resolver, Transform }
 
@@ -80,6 +81,10 @@ export interface SharedConfig {
    * https://github.com/vuejs/vue-next/blob/master/packages/compiler-core/src/options.ts
    */
   vueCompilerOptions?: CompilerOptions
+  /**
+   * Customer template compiler.
+   */
+  templateCompilers?: Record<string, TemplateCompilerOptions>
   /**
    * Transform functions for Vue custom blocks.
    *
diff --git a/src/node/server/serverPluginVue.ts b/src/node/server/serverPluginVue.ts
index 61094f2cd7fd8e..8730c972e094e8 100644
--- a/src/node/server/serverPluginVue.ts
+++ b/src/node/server/serverPluginVue.ts
@@ -1,7 +1,7 @@
 import qs from 'querystring'
 import chalk from 'chalk'
 import path from 'path'
-import { Context, ServerPlugin } from '.'
+import { Context, ServerPlugin, ServerPluginContext } from '.'
 import {
   SFCBlock,
   SFCDescriptor,
@@ -9,7 +9,8 @@ import {
   SFCStyleBlock,
   SFCStyleCompileResults,
   CompilerOptions,
-  SFCStyleCompileOptions
+  SFCStyleCompileOptions,
+  TemplateCompiler
 } from '@vue/compiler-sfc'
 import { resolveCompiler, resolveVue } from '../utils/resolveVue'
 import hash_sum from 'hash-sum'
@@ -124,7 +125,7 @@ export const vuePlugin: ServerPlugin = ({
         filePath,
         publicPath,
         descriptor.styles.some((s) => s.scoped),
-        config.vueCompilerOptions
+        config
       )
       ctx.body = code
       ctx.map = map
@@ -498,13 +499,15 @@ async function compileSFCMain(
   return result
 }
 
+export type TemplateCompilerOptions = [TemplateCompiler, CompilerOptions]
+
 function compileSFCTemplate(
   root: string,
   template: SFCTemplateBlock,
   filePath: string,
   publicPath: string,
   scoped: boolean,
-  userOptions: CompilerOptions | undefined
+  { vueCompilerOptions, templateCompilers = {} }: ServerPluginContext['config']
 ): ResultWithMap {
   let cached = vueCache.get(filePath)
   if (cached && cached.template) {
@@ -512,6 +515,19 @@ function compileSFCTemplate(
     return cached.template
   }
 
+  const compilerKey = (template as any).compiler
+  let compiler
+  let compilerOptions = {}
+  if (compilerKey) {
+    if (templateCompilers[compilerKey]) {
+      ;[compiler, compilerOptions] = templateCompilers[compilerKey]
+    } else {
+      console.error(
+        `The "${compilerKey}" compiler not found.Please add "templateCompilers" options.`
+      )
+    }
+  }
+
   const start = Date.now()
   const { compileTemplate, generateCodeFrame } = resolveCompiler(root)
   const { code, map, errors } = compileTemplate({
@@ -521,8 +537,10 @@ function compileSFCTemplate(
     transformAssetUrls: {
       base: path.posix.dirname(publicPath)
     },
+    compiler: compiler,
     compilerOptions: {
-      ...userOptions,
+      ...vueCompilerOptions,
+      ...compilerOptions,
       scopeId: scoped ? `data-v-${hash_sum(publicPath)}` : null,
       runtimeModuleName: resolveVue(root).isLocal
         ? // in local mode, vue would have been optimized so must be referenced

From 74e47edfaf64c074c6707f2d668e5eaf3ddee5d6 Mon Sep 17 00:00:00 2001
From: likui <2218301630@qq.com>
Date: Wed, 1 Jul 2020 11:26:31 +0800
Subject: [PATCH 2/7] fix: add global template compiler option

---
 src/node/config.ts                 | 12 ++++++++++--
 src/node/server/serverPluginVue.ts |  9 ++++++---
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/src/node/config.ts b/src/node/config.ts
index 829b049226b6d2..385e7cb1a40bd2 100644
--- a/src/node/config.ts
+++ b/src/node/config.ts
@@ -4,7 +4,11 @@ import chalk from 'chalk'
 import dotenv, { DotenvParseOutput } from 'dotenv'
 import dotenvExpand from 'dotenv-expand'
 import { Options as RollupPluginVueOptions } from 'rollup-plugin-vue'
-import { CompilerOptions, SFCStyleCompileOptions } from '@vue/compiler-sfc'
+import {
+  CompilerOptions,
+  SFCStyleCompileOptions,
+  TemplateCompiler
+} from '@vue/compiler-sfc'
 import Rollup, {
   InputOptions as RollupInputOptions,
   OutputOptions as RollupOutputOptions,
@@ -82,7 +86,11 @@ export interface SharedConfig {
    */
   vueCompilerOptions?: CompilerOptions
   /**
-   * Customer template compiler.
+   * Customer template compiler for global sfc template block.
+   */
+  compiler?: TemplateCompiler
+  /**
+   * Customer template compiler for special sfc template block.
    */
   templateCompilers?: Record<string, TemplateCompilerOptions>
   /**
diff --git a/src/node/server/serverPluginVue.ts b/src/node/server/serverPluginVue.ts
index 8730c972e094e8..f12ad077ea54fe 100644
--- a/src/node/server/serverPluginVue.ts
+++ b/src/node/server/serverPluginVue.ts
@@ -507,7 +507,11 @@ function compileSFCTemplate(
   filePath: string,
   publicPath: string,
   scoped: boolean,
-  { vueCompilerOptions, templateCompilers = {} }: ServerPluginContext['config']
+  {
+    vueCompilerOptions,
+    templateCompilers = {},
+    compiler
+  }: ServerPluginContext['config']
 ): ResultWithMap {
   let cached = vueCache.get(filePath)
   if (cached && cached.template) {
@@ -516,7 +520,6 @@ function compileSFCTemplate(
   }
 
   const compilerKey = (template as any).compiler
-  let compiler
   let compilerOptions = {}
   if (compilerKey) {
     if (templateCompilers[compilerKey]) {
@@ -537,7 +540,7 @@ function compileSFCTemplate(
     transformAssetUrls: {
       base: path.posix.dirname(publicPath)
     },
-    compiler: compiler,
+    compiler,
     compilerOptions: {
       ...vueCompilerOptions,
       ...compilerOptions,

From 4cefde4562d24218946485ffceaf6fac1f23f24a Mon Sep 17 00:00:00 2001
From: likui <2218301630@qq.com>
Date: Wed, 1 Jul 2020 11:30:03 +0800
Subject: [PATCH 3/7] refactor: reduce various

---
 src/node/server/serverPluginVue.ts | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/node/server/serverPluginVue.ts b/src/node/server/serverPluginVue.ts
index f12ad077ea54fe..893db2693c33df 100644
--- a/src/node/server/serverPluginVue.ts
+++ b/src/node/server/serverPluginVue.ts
@@ -509,7 +509,7 @@ function compileSFCTemplate(
   scoped: boolean,
   {
     vueCompilerOptions,
-    templateCompilers = {},
+    templateCompilers,
     compiler
   }: ServerPluginContext['config']
 ): ResultWithMap {
@@ -520,10 +520,9 @@ function compileSFCTemplate(
   }
 
   const compilerKey = (template as any).compiler
-  let compilerOptions = {}
   if (compilerKey) {
-    if (templateCompilers[compilerKey]) {
-      ;[compiler, compilerOptions] = templateCompilers[compilerKey]
+    if (templateCompilers && templateCompilers[compilerKey]) {
+      ;[compiler, vueCompilerOptions] = templateCompilers[compilerKey]
     } else {
       console.error(
         `The "${compilerKey}" compiler not found.Please add "templateCompilers" options.`
@@ -543,7 +542,6 @@ function compileSFCTemplate(
     compiler,
     compilerOptions: {
       ...vueCompilerOptions,
-      ...compilerOptions,
       scopeId: scoped ? `data-v-${hash_sum(publicPath)}` : null,
       runtimeModuleName: resolveVue(root).isLocal
         ? // in local mode, vue would have been optimized so must be referenced

From 3eca973af8ef9c257da6b832603a9b02aa34963b Mon Sep 17 00:00:00 2001
From: likui <2218301630@qq.com>
Date: Wed, 1 Jul 2020 11:34:09 +0800
Subject: [PATCH 4/7] fix: add miss default value for `vueCompilerOptions`

---
 src/node/server/serverPluginVue.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/node/server/serverPluginVue.ts b/src/node/server/serverPluginVue.ts
index 893db2693c33df..8ef992739bc3f7 100644
--- a/src/node/server/serverPluginVue.ts
+++ b/src/node/server/serverPluginVue.ts
@@ -508,7 +508,7 @@ function compileSFCTemplate(
   publicPath: string,
   scoped: boolean,
   {
-    vueCompilerOptions,
+    vueCompilerOptions = {},
     templateCompilers,
     compiler
   }: ServerPluginContext['config']

From 77a6f6aab6d8943e889609bc255dd3ed02e84fdb Mon Sep 17 00:00:00 2001
From: likui <2218301630@qq.com>
Date: Wed, 1 Jul 2020 11:44:01 +0800
Subject: [PATCH 5/7] fix: read compiler form block attr

---
 src/node/server/serverPluginVue.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/node/server/serverPluginVue.ts b/src/node/server/serverPluginVue.ts
index 8ef992739bc3f7..0376f3f8860e59 100644
--- a/src/node/server/serverPluginVue.ts
+++ b/src/node/server/serverPluginVue.ts
@@ -519,7 +519,7 @@ function compileSFCTemplate(
     return cached.template
   }
 
-  const compilerKey = (template as any).compiler
+  const compilerKey = template.attrs.compiler as string
   if (compilerKey) {
     if (templateCompilers && templateCompilers[compilerKey]) {
       ;[compiler, vueCompilerOptions] = templateCompilers[compilerKey]

From 7ca7653519abb68da7843d5a26ee8cf99bfd1694 Mon Sep 17 00:00:00 2001
From: likui <2218301630@qq.com>
Date: Thu, 2 Jul 2020 11:50:05 +0800
Subject: [PATCH 6/7] fix: fix some error and add test

---
 playground/App.vue                            |  5 ++-
 .../test-custom-compiler.vue                  |  9 ++++++
 playground/vite.config.ts                     | 13 ++++++++
 src/node/build/index.ts                       |  2 ++
 src/node/config.ts                            | 11 +++++--
 src/node/server/serverPluginVue.ts            | 31 +++++++++++++------
 test/test.js                                  |  7 +++++
 7 files changed, 64 insertions(+), 14 deletions(-)
 create mode 100644 playground/test-custom-compiler/test-custom-compiler.vue

diff --git a/playground/App.vue b/playground/App.vue
index e8390c1ba05002..c5443c025e93b0 100644
--- a/playground/App.vue
+++ b/playground/App.vue
@@ -27,6 +27,7 @@
   <Suspense>
     <TestWasm />
   </Suspense>
+  <TestCustomCompiler/>
 </template>
 
 <script>
@@ -54,6 +55,7 @@ import TestRewriteOptimized from './resolve/rewrite-optimized/TestRewriteOptimiz
 import TestDynamicImport from './dynamic-import/TestDynamicImport.vue'
 import TestWebWorker from './worker/TestWorker.vue'
 import TestWasm from './wasm/TestWasm.vue'
+import TestCustomCompiler from './test-custom-compiler/test-custom-compiler.vue'
 
 export default {
   components: {
@@ -80,7 +82,8 @@ export default {
     TestNormalizePublicPath,
     TestDynamicImport,
     TestWebWorker,
-    TestWasm
+    TestWasm,
+    TestCustomCompiler
   }
 }
 </script>
diff --git a/playground/test-custom-compiler/test-custom-compiler.vue b/playground/test-custom-compiler/test-custom-compiler.vue
new file mode 100644
index 00000000000000..4baac5e135b1ef
--- /dev/null
+++ b/playground/test-custom-compiler/test-custom-compiler.vue
@@ -0,0 +1,9 @@
+<template compiler="custom">
+  <div>111</div>
+</template>
+
+<script>
+	export default {
+		name: "test-customer-compiler"
+	}
+</script>
diff --git a/playground/vite.config.ts b/playground/vite.config.ts
index 3f8df559084933..e924b7cf8d6454 100644
--- a/playground/vite.config.ts
+++ b/playground/vite.config.ts
@@ -2,6 +2,16 @@ import type { UserConfig } from 'vite'
 import { jsPlugin } from './plugins/jsPlugin'
 import { i18nTransform } from './custom-blocks/i18nTransform'
 
+const customCompiler = {
+  compile: () => {
+    return {
+      code: `import {h} from '/@modules/vue'\n export function render () { return [[h('h2', 'Custom Compiler'), h('div', {class: 'custom-compiler'}, 'custom compiler works!')]]}`,
+      ast: null
+    }
+  },
+  parse: () => null
+}
+
 const config: UserConfig = {
   alias: {
     alias: '/alias/aliased',
@@ -11,6 +21,9 @@ const config: UserConfig = {
   minify: false,
   serviceWorker: !!process.env.USE_SW,
   plugins: [jsPlugin],
+  vueTemplateCompilers: {
+    custom: customCompiler
+  },
   vueCustomBlockTransforms: { i18n: i18nTransform },
   optimizeDeps: {
     exclude: ['bootstrap', 'rewrite-unoptimized-test-package'],
diff --git a/src/node/build/index.ts b/src/node/build/index.ts
index d8d41710fc2af1..d8cc5ac7671eac 100644
--- a/src/node/build/index.ts
+++ b/src/node/build/index.ts
@@ -123,7 +123,9 @@ export async function createBaseRollupPlugins(
         ...cssPreprocessOptions
       },
       preprocessCustomRequire: (id: string) => require(resolveFrom(root, id)),
+      compiler: options.vueCompiler,
       compilerOptions: options.vueCompilerOptions,
+      templateCompilers: options.vueTemplateCompilers,
       cssModulesOptions: {
         generateScopedName: (local: string, filename: string) =>
           `${local}_${hash_sum(filename)}`,
diff --git a/src/node/config.ts b/src/node/config.ts
index 385e7cb1a40bd2..f1114a9d27a56f 100644
--- a/src/node/config.ts
+++ b/src/node/config.ts
@@ -22,7 +22,7 @@ import { DepOptimizationOptions } from './optimizer'
 import { IKoaProxiesOptions } from 'koa-proxies'
 import { ServerOptions } from 'https'
 import { lookupFile } from './utils'
-import { TemplateCompilerOptions } from './server/serverPluginVue'
+import { TemplateCompilers } from './server/serverPluginVue'
 
 export { Resolver, Transform }
 
@@ -88,11 +88,11 @@ export interface SharedConfig {
   /**
    * Customer template compiler for global sfc template block.
    */
-  compiler?: TemplateCompiler
+  vueCompiler?: TemplateCompiler
   /**
    * Customer template compiler for special sfc template block.
    */
-  templateCompilers?: Record<string, TemplateCompilerOptions>
+  vueTemplateCompilers?: Record<string, TemplateCompilers>
   /**
    * Transform functions for Vue custom blocks.
    *
@@ -288,6 +288,7 @@ export interface Plugin
     | 'transforms'
     | 'resolvers'
     | 'configureServer'
+    | 'vueTemplateCompilers'
     | 'vueCompilerOptions'
     | 'vueCustomBlockTransforms'
     | 'rollupInputOptions'
@@ -443,6 +444,10 @@ function resolvePlugin(config: UserConfig, plugin: Plugin): UserConfig {
       config.configureServer || [],
       plugin.configureServer || []
     ),
+    vueTemplateCompilers: {
+      ...config.vueTemplateCompilers,
+      ...plugin.vueTemplateCompilers
+    },
     vueCompilerOptions: {
       ...config.vueCompilerOptions,
       ...plugin.vueCompilerOptions
diff --git a/src/node/server/serverPluginVue.ts b/src/node/server/serverPluginVue.ts
index 0376f3f8860e59..05f74db0f259f3 100644
--- a/src/node/server/serverPluginVue.ts
+++ b/src/node/server/serverPluginVue.ts
@@ -499,7 +499,9 @@ async function compileSFCMain(
   return result
 }
 
-export type TemplateCompilerOptions = [TemplateCompiler, CompilerOptions]
+export type TemplateCompilers =
+  | TemplateCompiler
+  | [TemplateCompiler, CompilerOptions]
 
 function compileSFCTemplate(
   root: string,
@@ -509,8 +511,8 @@ function compileSFCTemplate(
   scoped: boolean,
   {
     vueCompilerOptions = {},
-    templateCompilers,
-    compiler
+    vueTemplateCompilers,
+    vueCompiler
   }: ServerPluginContext['config']
 ): ResultWithMap {
   let cached = vueCache.get(filePath)
@@ -519,14 +521,23 @@ function compileSFCTemplate(
     return cached.template
   }
 
-  const compilerKey = template.attrs.compiler as string
+  const compilerKey = template.attrs.compiler
   if (compilerKey) {
-    if (templateCompilers && templateCompilers[compilerKey]) {
-      ;[compiler, vueCompilerOptions] = templateCompilers[compilerKey]
+    if (typeof compilerKey === 'string') {
+      if (vueTemplateCompilers && vueTemplateCompilers[compilerKey]) {
+        const compilers = vueTemplateCompilers[compilerKey]
+        if (Array.isArray(compilers)) {
+          ;[vueCompiler, vueCompilerOptions] = compilers
+        } else {
+          vueCompiler = compilers
+        }
+      } else {
+        console.error(
+          `The "${compilerKey}" compiler not found.Please add "vueTemplateCompilers" options.`
+        )
+      }
     } else {
-      console.error(
-        `The "${compilerKey}" compiler not found.Please add "templateCompilers" options.`
-      )
+      console.error(`Please ensure custom template compiler in ${filePath}`)
     }
   }
 
@@ -539,7 +550,7 @@ function compileSFCTemplate(
     transformAssetUrls: {
       base: path.posix.dirname(publicPath)
     },
-    compiler,
+    compiler: vueCompiler,
     compilerOptions: {
       ...vueCompilerOptions,
       scopeId: scoped ? `data-v-${hash_sum(publicPath)}` : null,
diff --git a/test/test.js b/test/test.js
index 97b23d43518483..d2729142fe1731 100644
--- a/test/test.js
+++ b/test/test.js
@@ -630,6 +630,13 @@ describe('vite', () => {
       await button.click()
       await expectByPolling(() => getText('.wasm-response'), '42')
     })
+
+    test('custom compiler', async () => {
+      await expectByPolling(
+        () => getText('.custom-compiler'),
+        'custom compiler works!'
+      )
+    })
   }
 
   // test build first since we are going to edit the fixtures when testing dev

From 350e3f76dbe69458eb1d52708a5c371229bc159a Mon Sep 17 00:00:00 2001
From: likui <2218301630@qq.com>
Date: Thu, 2 Jul 2020 11:57:25 +0800
Subject: [PATCH 7/7] fix: fix test content

---
 playground/test-custom-compiler/test-custom-compiler.vue | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/playground/test-custom-compiler/test-custom-compiler.vue b/playground/test-custom-compiler/test-custom-compiler.vue
index 4baac5e135b1ef..4f3ff471b2c00f 100644
--- a/playground/test-custom-compiler/test-custom-compiler.vue
+++ b/playground/test-custom-compiler/test-custom-compiler.vue
@@ -1,9 +1,7 @@
 <template compiler="custom">
-  <div>111</div>
+  <div></div>
 </template>
 
 <script>
-	export default {
-		name: "test-customer-compiler"
-	}
+	export default {}
 </script>