|
|
import { VueTemplateCompiler, VueTemplateCompilerOptions, ErrorWithRange } from './types'
import assetUrlsModule, { AssetURLOptions, TransformAssetUrlsOptions } from './templateCompilerModules/assetUrl' import srcsetModule from './templateCompilerModules/srcset'
const consolidate = require('consolidate') const transpile = require('vue-template-es2015-compiler')
export interface TemplateCompileOptions { source: string filename: string compiler: VueTemplateCompiler compilerOptions?: VueTemplateCompilerOptions transformAssetUrls?: AssetURLOptions | boolean transformAssetUrlsOptions?: TransformAssetUrlsOptions preprocessLang?: string preprocessOptions?: any transpileOptions?: any isProduction?: boolean isFunctional?: boolean optimizeSSR?: boolean prettify?: boolean }
export interface TemplateCompileResult { ast: Object | undefined code: string source: string tips: (string | ErrorWithRange)[] errors: (string | ErrorWithRange)[] }
export function compileTemplate( options: TemplateCompileOptions ): TemplateCompileResult { const { preprocessLang } = options const preprocessor = preprocessLang && consolidate[preprocessLang] if (preprocessor) { return actuallyCompile( Object.assign({}, options, { source: preprocess(options, preprocessor) }) ) } else if (preprocessLang) { return { ast: {}, code: `var render = function () {}\n` + `var staticRenderFns = []\n`, 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 actuallyCompile(options) } }
function preprocess( options: TemplateCompileOptions, preprocessor: any ): string { const { source, filename, preprocessOptions } = options
const finalPreprocessOptions = Object.assign( { filename }, preprocessOptions )
// 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, finalPreprocessOptions, (_err: Error | null, _res: string) => { if (_err) err = _err res = _res } )
if (err) throw err return res }
function actuallyCompile( options: TemplateCompileOptions ): TemplateCompileResult { const { source, compiler, compilerOptions = {}, transpileOptions = {}, transformAssetUrls, transformAssetUrlsOptions, isProduction = process.env.NODE_ENV === 'production', isFunctional = false, optimizeSSR = false, prettify = true } = options
const compile = optimizeSSR && compiler.ssrCompile ? compiler.ssrCompile : compiler.compile
let finalCompilerOptions = compilerOptions if (transformAssetUrls) { const builtInModules = [ transformAssetUrls === true ? assetUrlsModule(undefined, transformAssetUrlsOptions) : assetUrlsModule(transformAssetUrls, transformAssetUrlsOptions), srcsetModule(transformAssetUrlsOptions) ] finalCompilerOptions = Object.assign({}, compilerOptions, { modules: [...builtInModules, ...(compilerOptions.modules || [])], filename: options.filename }) }
const { ast, render, staticRenderFns, tips, errors } = compile( source, finalCompilerOptions )
if (errors && errors.length) { return { ast, code: `var render = function () {}\n` + `var staticRenderFns = []\n`, source, tips, errors } } else { const finalTranspileOptions = Object.assign({}, transpileOptions, { transforms: Object.assign({}, transpileOptions.transforms, { stripWithFunctional: isFunctional }) })
const toFunction = (code: string): string => { return `function (${isFunctional ? `_h,_vm` : ``}) {${code}}` }
// transpile code with vue-template-es2015-compiler, which is a forked
// version of Buble that applies ES2015 transforms + stripping `with` usage
let code = transpile( `var __render__ = ${toFunction(render)}\n` + `var __staticRenderFns__ = [${staticRenderFns.map(toFunction)}]`, finalTranspileOptions ) + `\n`
// #23 we use __render__ to avoid `render` not being prefixed by the
// transpiler when stripping with, but revert it back to `render` to
// maintain backwards compat
code = code.replace(/\s__(render|staticRenderFns)__\s/g, ' $1 ')
if (!isProduction) { // mark with stripped (this enables Vue to use correct runtime proxy
// detection)
code += `render._withStripped = true`
if (prettify) { try { code = require('prettier').format(code, { semi: false, parser: 'babel' }) } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { tips.push( 'The `prettify` option is on, but the dependency `prettier` is not found.\n' + 'Please either turn off `prettify` or manually install `prettier`.' ) } tips.push( `Failed to prettify component ${options.filename} template source after compilation.` ) } } }
return { ast, code, source, tips, errors } } }
|