/* @flow */
|
|
import { noop, extend } from 'shared/util'
|
import { warn as baseWarn, tip } from 'core/util/debug'
|
import { generateCodeFrame } from './codeframe'
|
|
type CompiledFunctionResult = {
|
render: Function;
|
staticRenderFns: Array<Function>;
|
};
|
|
function createFunction (code, errors) {
|
try {
|
return new Function(code)
|
} catch (err) {
|
errors.push({ err, code })
|
return noop
|
}
|
}
|
|
export function createCompileToFunctionFn (compile: Function): Function {
|
const cache = Object.create(null)
|
|
return function compileToFunctions (
|
template: string,
|
options?: CompilerOptions,
|
vm?: Component
|
): CompiledFunctionResult {
|
options = extend({}, options)
|
const warn = options.warn || baseWarn
|
delete options.warn
|
|
/* istanbul ignore if */
|
if (process.env.NODE_ENV !== 'production') {
|
// detect possible CSP restriction
|
try {
|
new Function('return 1')
|
} catch (e) {
|
if (e.toString().match(/unsafe-eval|CSP/)) {
|
warn(
|
'It seems you are using the standalone build of Vue.js in an ' +
|
'environment with Content Security Policy that prohibits unsafe-eval. ' +
|
'The template compiler cannot work in this environment. Consider ' +
|
'relaxing the policy to allow unsafe-eval or pre-compiling your ' +
|
'templates into render functions.'
|
)
|
}
|
}
|
}
|
|
// check cache
|
const key = options.delimiters
|
? String(options.delimiters) + template
|
: template
|
if (cache[key]) {
|
return cache[key]
|
}
|
|
// compile
|
const compiled = compile(template, options)
|
|
// check compilation errors/tips
|
if (process.env.NODE_ENV !== 'production') {
|
if (compiled.errors && compiled.errors.length) {
|
if (options.outputSourceRange) {
|
compiled.errors.forEach(e => {
|
warn(
|
`Error compiling template:\n\n${e.msg}\n\n` +
|
generateCodeFrame(template, e.start, e.end),
|
vm
|
)
|
})
|
} else {
|
warn(
|
`Error compiling template:\n\n${template}\n\n` +
|
compiled.errors.map(e => `- ${e}`).join('\n') + '\n',
|
vm
|
)
|
}
|
}
|
if (compiled.tips && compiled.tips.length) {
|
if (options.outputSourceRange) {
|
compiled.tips.forEach(e => tip(e.msg, vm))
|
} else {
|
compiled.tips.forEach(msg => tip(msg, vm))
|
}
|
}
|
}
|
|
// turn code into functions
|
const res = {}
|
const fnGenErrors = []
|
res.render = createFunction(compiled.render, fnGenErrors)
|
res.staticRenderFns = compiled.staticRenderFns.map(code => {
|
return createFunction(code, fnGenErrors)
|
})
|
|
// check function generation errors.
|
// this should only happen if there is a bug in the compiler itself.
|
// mostly for codegen development use
|
/* istanbul ignore if */
|
if (process.env.NODE_ENV !== 'production') {
|
if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) {
|
warn(
|
`Failed to generate render function:\n\n` +
|
fnGenErrors.map(({ err, code }) => `${err.toString()} in\n\n${code}\n`).join('\n'),
|
vm
|
)
|
}
|
}
|
|
return (cache[key] = res)
|
}
|
}
|