123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- 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
- }
- }
- }
|