204 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
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
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |