UNPKG

4.45 kBJavaScriptView Raw
1/* @flow */
2
3import { parse } from './parser/index'
4import { optimize } from './optimizer'
5import { generate } from './codegen/index'
6import { detectErrors } from './error-detector'
7import { extend, noop } from 'shared/util'
8import { warn, tip } from 'core/util/debug'
9
10function baseCompile (
11 template: string,
12 options: CompilerOptions
13): CompiledResult {
14 const ast = parse(template.trim(), options)
15 optimize(ast, options)
16 const code = generate(ast, options)
17 return {
18 ast,
19 render: code.render,
20 staticRenderFns: code.staticRenderFns
21 }
22}
23
24function makeFunction (code, errors) {
25 try {
26 return new Function(code)
27 } catch (err) {
28 errors.push({ err, code })
29 return noop
30 }
31}
32
33export function createCompiler (baseOptions: CompilerOptions) {
34 const functionCompileCache: {
35 [key: string]: CompiledFunctionResult;
36 } = Object.create(null)
37
38 function compile (
39 template: string,
40 options?: CompilerOptions
41 ): CompiledResult {
42 const finalOptions = Object.create(baseOptions)
43 const errors = []
44 const tips = []
45 finalOptions.warn = (msg, tip) => {
46 (tip ? tips : errors).push(msg)
47 }
48
49 if (options) {
50 // merge custom modules
51 if (options.modules) {
52 finalOptions.modules = (baseOptions.modules || []).concat(options.modules)
53 }
54 // merge custom directives
55 if (options.directives) {
56 finalOptions.directives = extend(
57 Object.create(baseOptions.directives),
58 options.directives
59 )
60 }
61 // copy other options
62 for (const key in options) {
63 if (key !== 'modules' && key !== 'directives') {
64 finalOptions[key] = options[key]
65 }
66 }
67 }
68
69 const compiled = baseCompile(template, finalOptions)
70 if (process.env.NODE_ENV !== 'production') {
71 errors.push.apply(errors, detectErrors(compiled.ast))
72 }
73 compiled.errors = errors
74 compiled.tips = tips
75 return compiled
76 }
77
78 function compileToFunctions (
79 template: string,
80 options?: CompilerOptions,
81 vm?: Component
82 ): CompiledFunctionResult {
83 options = options || {}
84
85 /* istanbul ignore if */
86 if (process.env.NODE_ENV !== 'production') {
87 // detect possible CSP restriction
88 try {
89 new Function('return 1')
90 } catch (e) {
91 if (e.toString().match(/unsafe-eval|CSP/)) {
92 warn(
93 'It seems you are using the standalone build of Vue.js in an ' +
94 'environment with Content Security Policy that prohibits unsafe-eval. ' +
95 'The template compiler cannot work in this environment. Consider ' +
96 'relaxing the policy to allow unsafe-eval or pre-compiling your ' +
97 'templates into render functions.'
98 )
99 }
100 }
101 }
102
103 // check cache
104 const key = options.delimiters
105 ? String(options.delimiters) + template
106 : template
107 if (functionCompileCache[key]) {
108 return functionCompileCache[key]
109 }
110
111 // compile
112 const compiled = compile(template, options)
113
114 // check compilation errors/tips
115 if (process.env.NODE_ENV !== 'production') {
116 if (compiled.errors && compiled.errors.length) {
117 warn(
118 `Error compiling template:\n\n${template}\n\n` +
119 compiled.errors.map(e => `- ${e}`).join('\n') + '\n',
120 vm
121 )
122 }
123 if (compiled.tips && compiled.tips.length) {
124 compiled.tips.forEach(msg => tip(msg, vm))
125 }
126 }
127
128 // turn code into functions
129 const res = {}
130 const fnGenErrors = []
131 res.render = makeFunction(compiled.render, fnGenErrors)
132 const l = compiled.staticRenderFns.length
133 res.staticRenderFns = new Array(l)
134 for (let i = 0; i < l; i++) {
135 res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], fnGenErrors)
136 }
137
138 // check function generation errors.
139 // this should only happen if there is a bug in the compiler itself.
140 // mostly for codegen development use
141 /* istanbul ignore if */
142 if (process.env.NODE_ENV !== 'production') {
143 if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) {
144 warn(
145 `Failed to generate render function:\n\n` +
146 fnGenErrors.map(({ err, code }) => `${err.toString()} in\n\n${code}\n`).join('\n'),
147 vm
148 )
149 }
150 }
151
152 return (functionCompileCache[key] = res)
153 }
154
155 return {
156 compile,
157 compileToFunctions
158 }
159}