diff --git a/packages/compiler-sfc/__tests__/compileTemplate.spec.ts b/packages/compiler-sfc/__tests__/compileTemplate.spec.ts
new file mode 100644
index 00000000..ebc431cb
--- /dev/null
+++ b/packages/compiler-sfc/__tests__/compileTemplate.spec.ts
@@ -0,0 +1,55 @@
+import { compileTemplate } from '../src/compileTemplate'
+import { compile } from '@vue/compiler-dom'
+import { parse, SFCTemplateBlock } from '../src/parse'
+
+const compiler = { compile }
+
+test('should work', () => {
+ const source = `
`
+
+ const result = compileTemplate({ filename: 'example.vue', source, compiler })
+
+ expect(result.errors.length).toBe(0)
+ expect(result.source).toBe(source)
+ // should expose render fn
+ expect(result.code).toMatch(`export default function render()`)
+})
+
+test('preprocess pug', () => {
+ const template = parse(
+ `
+
+body
+ h1 Pug Examples
+ div.container
+ p Cool Pug example!
+
+`,
+ { filename: 'example.vue', needMap: true }
+ ).template as SFCTemplateBlock
+
+ const result = compileTemplate({
+ filename: 'example.vue',
+ source: template.content,
+ preprocessLang: template.lang,
+ compiler
+ })
+
+ expect(result.errors.length).toBe(0)
+})
+
+test('warn missing preprocessor', () => {
+ const template = parse(`\n\n`, {
+ filename: 'example.vue',
+ needMap: true
+ }).template as SFCTemplateBlock
+
+ const result = compileTemplate({
+ filename: 'example.vue',
+ compiler,
+ source: template.content,
+ preprocessLang: template.lang
+ })
+
+ expect(result.errors.length).toBe(1)
+})
diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json
index 013fc7e2..0e129b81 100644
--- a/packages/compiler-sfc/package.json
+++ b/packages/compiler-sfc/package.json
@@ -37,6 +37,7 @@
"source-map": "^0.7.3"
},
"devDependencies": {
- "@types/lru-cache": "^5.1.0"
+ "@types/lru-cache": "^5.1.0",
+ "pug": "^2.0.4"
}
}
diff --git a/packages/compiler-sfc/src/compileTemplate.ts b/packages/compiler-sfc/src/compileTemplate.ts
index b970d915..52bb6d32 100644
--- a/packages/compiler-sfc/src/compileTemplate.ts
+++ b/packages/compiler-sfc/src/compileTemplate.ts
@@ -1,3 +1,101 @@
-export function compileTemplate() {
- // TODO
+import {
+ CompilerOptions,
+ CodegenResult,
+ CompilerError
+} from '@vue/compiler-core'
+import { RawSourceMap } from 'source-map'
+import { transformAssetUrl } from './templateTransformAssetUrl'
+import { transformSrcset } from './templateTransformSrcset'
+
+const consolidate = require('consolidate')
+
+export interface TemplateCompileResults {
+ code: string
+ source: string
+ tips: string[]
+ errors: (string | CompilerError)[]
+ map?: RawSourceMap
+}
+
+export interface TemplateCompiler {
+ compile(template: string, options: CompilerOptions): CodegenResult
+}
+
+export interface TemplateCompileOptions {
+ source: string
+ filename: string
+ compiler: TemplateCompiler
+ compilerOptions?: CompilerOptions
+ preprocessLang?: string
+ preprocessOptions?: any
+}
+
+function preprocess(
+ { source, filename, preprocessOptions }: TemplateCompileOptions,
+ preprocessor: any
+): string {
+ // Consolidate exposes a callback based API, but the callback is in fact
+ // called synchronously for most templating engines. In our case, we have to
+ // expose a synchronous API so that it is usable in Jest transforms (which
+ // have to be sync because they are applied via Node.js require hooks)
+ let res: any, err
+ preprocessor.render(
+ source,
+ { filename, ...preprocessOptions },
+ (_err: Error | null, _res: string) => {
+ if (_err) err = _err
+ res = _res
+ }
+ )
+
+ if (err) throw err
+ return res
+}
+
+export function compileTemplate(
+ options: TemplateCompileOptions
+): TemplateCompileResults {
+ const { preprocessLang } = options
+ const preprocessor = preprocessLang && consolidate[preprocessLang]
+ if (preprocessor) {
+ return doCompileTemplate({
+ ...options,
+ source: preprocess(options, preprocessor)
+ })
+ } else if (preprocessLang) {
+ return {
+ code: `export default function render() {}`,
+ source: options.source,
+ tips: [
+ `Component ${
+ options.filename
+ } uses lang ${preprocessLang} for template. Please install the language preprocessor.`
+ ],
+ errors: [
+ `Component ${
+ options.filename
+ } uses lang ${preprocessLang} for template, however it is not installed.`
+ ]
+ }
+ } else {
+ return doCompileTemplate(options)
+ }
+}
+
+function doCompileTemplate({
+ source,
+ compiler,
+ compilerOptions = {},
+ filename
+}: TemplateCompileOptions): TemplateCompileResults {
+ const errors: CompilerError[] = []
+ const { code, map } = compiler.compile(source, {
+ ...compilerOptions,
+ filename,
+ mode: 'module',
+ sourceMap: true,
+ nodeTransforms: [transformAssetUrl, transformSrcset],
+ onError: e => errors.push(e)
+ })
+ return { code, source, errors, tips: [], map }
}