Skip to content

Commit 5d12d52

Browse files
committed
refactor compiler creation
1 parent f66028b commit 5d12d52

File tree

3 files changed

+168
-217
lines changed

3 files changed

+168
-217
lines changed

Diff for: src/compiler/index.js

+141-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
import { parse } from './parser/index'
44
import { optimize } from './optimizer'
55
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'
69

7-
/**
8-
* Compile a template.
9-
*/
10-
export function compile (
10+
function baseCompile (
1111
template: string,
1212
options: CompilerOptions
1313
): CompiledResult {
@@ -20,3 +20,140 @@ export function compile (
2020
staticRenderFns: code.staticRenderFns
2121
}
2222
}
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+
// 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+
}

Diff for: src/platforms/web/compiler/index.js

+13-134
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,30 @@
11
/* @flow */
22

33
import { isUnaryTag } from './util'
4-
import { warn, tip } from 'core/util/debug'
5-
import { detectErrors } from 'compiler/error-detector'
6-
import { compile as baseCompile } from 'compiler/index'
7-
import { extend, genStaticKeys, noop } from 'shared/util'
8-
import { isReservedTag, mustUseProp, getTagNamespace, isPreTag } from '../util/index'
4+
import { genStaticKeys } from 'shared/util'
5+
import { createCompiler } from 'compiler/index'
96

107
import modules from './modules/index'
118
import directives from './directives/index'
129

13-
const cache: { [key: string]: CompiledFunctionResult } = Object.create(null)
10+
import {
11+
isPreTag,
12+
mustUseProp,
13+
isReservedTag,
14+
getTagNamespace
15+
} from '../util/index'
1416

1517
export const baseOptions: CompilerOptions = {
1618
expectHTML: true,
1719
modules,
18-
staticKeys: genStaticKeys(modules),
1920
directives,
20-
isReservedTag,
21+
isPreTag,
2122
isUnaryTag,
2223
mustUseProp,
24+
isReservedTag,
2325
getTagNamespace,
24-
isPreTag
25-
}
26-
27-
export function compile (
28-
template: string,
29-
options?: CompilerOptions
30-
): CompiledResult {
31-
const finalOptions = Object.create(baseOptions)
32-
const errors = []
33-
const tips = []
34-
finalOptions.warn = (msg, tip) => {
35-
(tip ? tips : errors).push(msg)
36-
}
37-
38-
if (options) {
39-
// merge custom modules
40-
if (options.modules) {
41-
finalOptions.modules = (baseOptions.modules || []).concat(options.modules)
42-
}
43-
// merge custom directives
44-
if (options.directives) {
45-
finalOptions.directives = extend(
46-
Object.create(baseOptions.directives),
47-
options.directives
48-
)
49-
}
50-
// copy other options
51-
for (const key in options) {
52-
if (key !== 'modules' && key !== 'directives') {
53-
finalOptions[key] = options[key]
54-
}
55-
}
56-
}
57-
58-
const compiled = baseCompile(template, finalOptions)
59-
if (process.env.NODE_ENV !== 'production') {
60-
errors.push.apply(errors, detectErrors(compiled.ast))
61-
}
62-
compiled.errors = errors
63-
compiled.tips = tips
64-
return compiled
65-
}
66-
67-
export function compileToFunctions (
68-
template: string,
69-
options?: CompilerOptions,
70-
vm?: Component
71-
): CompiledFunctionResult {
72-
options = extend({}, options)
73-
74-
/* istanbul ignore if */
75-
if (process.env.NODE_ENV !== 'production') {
76-
// detect possible CSP restriction
77-
try {
78-
new Function('return 1')
79-
} catch (e) {
80-
if (e.toString().match(/unsafe-eval|CSP/)) {
81-
warn(
82-
'It seems you are using the standalone build of Vue.js in an ' +
83-
'environment with Content Security Policy that prohibits unsafe-eval. ' +
84-
'The template compiler cannot work in this environment. Consider ' +
85-
'relaxing the policy to allow unsafe-eval or pre-compiling your ' +
86-
'templates into render functions.'
87-
)
88-
}
89-
}
90-
}
91-
92-
// check cache
93-
const key = options.delimiters
94-
? String(options.delimiters) + template
95-
: template
96-
if (cache[key]) {
97-
return cache[key]
98-
}
99-
100-
// compile
101-
const compiled = compile(template, options)
102-
103-
// check compilation errors/tips
104-
if (process.env.NODE_ENV !== 'production') {
105-
if (compiled.errors && compiled.errors.length) {
106-
warn(
107-
`Error compiling template:\n\n${template}\n\n` +
108-
compiled.errors.map(e => `- ${e}`).join('\n') + '\n',
109-
vm
110-
)
111-
}
112-
if (compiled.tips && compiled.tips.length) {
113-
compiled.tips.forEach(msg => tip(msg, vm))
114-
}
115-
}
116-
117-
// turn code into functions
118-
const res = {}
119-
const fnGenErrors = []
120-
res.render = makeFunction(compiled.render, fnGenErrors)
121-
const l = compiled.staticRenderFns.length
122-
res.staticRenderFns = new Array(l)
123-
for (let i = 0; i < l; i++) {
124-
res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], fnGenErrors)
125-
}
126-
127-
// check function generation errors.
128-
// this should only happen if there is a bug in the compiler itself.
129-
// mostly for codegen development use
130-
/* istanbul ignore if */
131-
if (process.env.NODE_ENV !== 'production') {
132-
if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) {
133-
warn(
134-
`Failed to generate render function:\n\n` +
135-
fnGenErrors.map(({ err, code }) => `${err.toString()} in\n\n${code}\n`).join('\n'),
136-
vm
137-
)
138-
}
139-
}
140-
141-
return (cache[key] = res)
26+
staticKeys: genStaticKeys(modules)
14227
}
14328

144-
function makeFunction (code, errors) {
145-
try {
146-
return new Function(code)
147-
} catch (err) {
148-
errors.push({ err, code })
149-
return noop
150-
}
151-
}
29+
const { compile, compileToFunctions } = createCompiler(baseOptions)
30+
export { compile, compileToFunctions }

0 commit comments

Comments
 (0)