1 |
|
2 |
|
3 | import { parse } from './parser/index'
|
4 | import { optimize } from './optimizer'
|
5 | import { generate } from './codegen/index'
|
6 | import { detectErrors } from './error-detector'
|
7 | import { extend, noop } from 'shared/util'
|
8 | import { warn, tip } from 'core/util/debug'
|
9 |
|
10 | function 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 |
|
24 | function makeFunction (code, errors) {
|
25 | try {
|
26 | return new Function(code)
|
27 | } catch (err) {
|
28 | errors.push({ err, code })
|
29 | return noop
|
30 | }
|
31 | }
|
32 |
|
33 | export 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 |
|
51 | if (options.modules) {
|
52 | finalOptions.modules = (baseOptions.modules || []).concat(options.modules)
|
53 | }
|
54 |
|
55 | if (options.directives) {
|
56 | finalOptions.directives = extend(
|
57 | Object.create(baseOptions.directives),
|
58 | options.directives
|
59 | )
|
60 | }
|
61 |
|
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 |
|
86 | if (process.env.NODE_ENV !== 'production') {
|
87 |
|
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 |
|
104 | const key = options.delimiters
|
105 | ? String(options.delimiters) + template
|
106 | : template
|
107 | if (functionCompileCache[key]) {
|
108 | return functionCompileCache[key]
|
109 | }
|
110 |
|
111 |
|
112 | const compiled = compile(template, options)
|
113 |
|
114 |
|
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 |
|
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 |
|
139 |
|
140 |
|
141 |
|
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 | }
|