Browse Source

make function compilation reusable

dev
Evan You 8 years ago
parent
commit
303780acd2
  1. 2
      src/compiler/codegen/index.js
  2. 96
      src/compiler/index.js
  3. 96
      src/compiler/to-function.js
  4. 27
      src/platforms/web/compiler/index.js
  5. 26
      src/platforms/web/compiler/options.js
  6. 2
      test/unit/modules/compiler/codegen.spec.js
  7. 2
      test/unit/modules/compiler/optimizer.spec.js
  8. 2
      test/unit/modules/compiler/parser.spec.js

2
src/compiler/codegen/index.js

@ -49,7 +49,7 @@ export function generate (
}
}
function genElement (el: ASTElement, state: CodegenState): string {
export function genElement (el: ASTElement, state: CodegenState): string {
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {

96
src/compiler/index.js

@ -1,11 +1,11 @@
/* @flow */
import { extend } from 'shared/util'
import { parse } from './parser/index'
import { optimize } from './optimizer'
import { generate } from './codegen/index'
import { detectErrors } from './error-detector'
import { extend, noop } from 'shared/util'
import { warn, tip } from 'core/util/debug'
import { createCompileToFunctionFn } from './to-function'
function baseCompile (
template: string,
@ -21,20 +21,7 @@ function baseCompile (
}
}
function makeFunction (code, errors) {
try {
return new Function(code)
} catch (err) {
errors.push({ err, code })
return noop
}
}
export function createCompiler (baseOptions: CompilerOptions) {
const functionCompileCache: {
[key: string]: CompiledFunctionResult;
} = Object.create(null)
function compile (
template: string,
options?: CompilerOptions
@ -75,85 +62,8 @@ export function createCompiler (baseOptions: CompilerOptions) {
return compiled
}
function compileToFunctions (
template: string,
options?: CompilerOptions,
vm?: Component
): CompiledFunctionResult {
options = options || {}
/* 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 (functionCompileCache[key]) {
return functionCompileCache[key]
}
// compile
const compiled = compile(template, options)
// check compilation errors/tips
if (process.env.NODE_ENV !== 'production') {
if (compiled.errors && compiled.errors.length) {
warn(
`Error compiling template:\n\n${template}\n\n` +
compiled.errors.map(e => `- ${e}`).join('\n') + '\n',
vm
)
}
if (compiled.tips && compiled.tips.length) {
compiled.tips.forEach(msg => tip(msg, vm))
}
}
// turn code into functions
const res = {}
const fnGenErrors = []
res.render = makeFunction(compiled.render, fnGenErrors)
const l = compiled.staticRenderFns.length
res.staticRenderFns = new Array(l)
for (let i = 0; i < l; i++) {
res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], 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 (functionCompileCache[key] = res)
}
return {
compile,
compileToFunctions
compileToFunctions: createCompileToFunctionFn(compile)
}
}

96
src/compiler/to-function.js

@ -0,0 +1,96 @@
/* @flow */
import { noop } from 'shared/util'
import { warn, tip } from 'core/util/debug'
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: {
[key: string]: CompiledFunctionResult;
} = Object.create(null)
return function compileToFunctions (
template: string,
options?: CompilerOptions,
vm?: Component
): CompiledFunctionResult {
options = options || {}
/* 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) {
warn(
`Error compiling template:\n\n${template}\n\n` +
compiled.errors.map(e => `- ${e}`).join('\n') + '\n',
vm
)
}
if (compiled.tips && compiled.tips.length) {
compiled.tips.forEach(msg => tip(msg, vm))
}
}
// turn code into functions
const res = {}
const fnGenErrors = []
res.render = createFunction(compiled.render, fnGenErrors)
const l = compiled.staticRenderFns.length
res.staticRenderFns = new Array(l)
for (let i = 0; i < l; i++) {
res.staticRenderFns[i] = createFunction(compiled.staticRenderFns[i], 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)
}
}

27
src/platforms/web/compiler/index.js

@ -1,31 +1,8 @@
/* @flow */
import { isUnaryTag, canBeLeftOpenTag } from './util'
import { genStaticKeys } from 'shared/util'
import { baseOptions } from './options'
import { createCompiler } from 'compiler/index'
import modules from './modules/index'
import directives from './directives/index'
import {
isPreTag,
mustUseProp,
isReservedTag,
getTagNamespace
} from '../util/index'
export const baseOptions: CompilerOptions = {
expectHTML: true,
modules,
directives,
isPreTag,
isUnaryTag,
mustUseProp,
canBeLeftOpenTag,
isReservedTag,
getTagNamespace,
staticKeys: genStaticKeys(modules)
}
const { compile, compileToFunctions } = createCompiler(baseOptions)
export { compile, compileToFunctions }

26
src/platforms/web/compiler/options.js

@ -0,0 +1,26 @@
/* @flow */
import {
isPreTag,
mustUseProp,
isReservedTag,
getTagNamespace
} from '../util/index'
import modules from './modules/index'
import directives from './directives/index'
import { genStaticKeys } from 'shared/util'
import { isUnaryTag, canBeLeftOpenTag } from './util'
export const baseOptions: CompilerOptions = {
expectHTML: true,
modules,
directives,
isPreTag,
isUnaryTag,
mustUseProp,
canBeLeftOpenTag,
isReservedTag,
getTagNamespace,
staticKeys: genStaticKeys(modules)
}

2
test/unit/modules/compiler/codegen.spec.js

@ -3,7 +3,7 @@ import { optimize } from 'compiler/optimizer'
import { generate } from 'compiler/codegen'
import { isObject } from 'shared/util'
import { isReservedTag } from 'web/util/index'
import { baseOptions } from 'web/compiler/index'
import { baseOptions } from 'web/compiler/options'
function assertCodegen (template, generatedCode, ...args) {
let staticRenderFnCodes = []

2
test/unit/modules/compiler/optimizer.spec.js

@ -1,6 +1,6 @@
import { parse } from 'compiler/parser/index'
import { optimize } from 'compiler/optimizer'
import { baseOptions } from 'web/compiler/index'
import { baseOptions } from 'web/compiler/options'
describe('optimizer', () => {
it('simple', () => {

2
test/unit/modules/compiler/parser.spec.js

@ -1,6 +1,6 @@
import { parse } from 'compiler/parser/index'
import { extend } from 'shared/util'
import { baseOptions } from 'web/compiler/index'
import { baseOptions } from 'web/compiler/options'
import { isIE, isEdge } from 'core/util/env'
describe('parser', () => {

Loading…
Cancel
Save