]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-sfc): compile template WIP (#534)
author宋铄运 <fnlctrl@gmail.com>
Tue, 10 Dec 2019 17:01:56 +0000 (01:01 +0800)
committerEvan You <yyx990803@gmail.com>
Tue, 10 Dec 2019 17:01:56 +0000 (12:01 -0500)
packages/compiler-sfc/__tests__/compileTemplate.spec.ts [new file with mode: 0644]
packages/compiler-sfc/package.json
packages/compiler-sfc/src/compileTemplate.ts

diff --git a/packages/compiler-sfc/__tests__/compileTemplate.spec.ts b/packages/compiler-sfc/__tests__/compileTemplate.spec.ts
new file mode 100644 (file)
index 0000000..ebc431c
--- /dev/null
@@ -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 = `<div><p>{{ render }}</p></div>`
+
+  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(
+    `
+<template lang="pug">
+body
+  h1 Pug Examples
+  div.container
+    p Cool Pug example!
+</template>
+`,
+    { 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(`<template lang="unknownLang">\n</template>\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)
+})
index 013fc7e2c03212a1c781b81a47b6b972fb938020..0e129b819433d24af207759ebd31b0e70fb3781d 100644 (file)
@@ -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"
   }
 }
index b970d915500d3a8c51edabd1af305ce7e5339b37..52bb6d322385be6f45ab149f7868e2a07734d1a8 100644 (file)
@@ -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 }
 }