modified .gitignore
This commit is contained in:
+166
-103
@@ -1,73 +1,76 @@
|
||||
/* @flow */
|
||||
|
||||
import { genHandlers } from './events'
|
||||
import { baseWarn, pluckModuleFunction } from '../helpers'
|
||||
import baseDirectives from '../directives/index'
|
||||
import { camelize, no } from 'shared/util'
|
||||
import { camelize, no, extend } from 'shared/util'
|
||||
import { baseWarn, pluckModuleFunction } from '../helpers'
|
||||
|
||||
type TransformFunction = (el: ASTElement, code: string) => string;
|
||||
type DataGenFunction = (el: ASTElement) => string;
|
||||
type DirectiveFunction = (el: ASTElement, dir: ASTDirective, warn: Function) => boolean;
|
||||
|
||||
// configurable state
|
||||
let warn
|
||||
let transforms: Array<TransformFunction>
|
||||
let dataGenFns: Array<DataGenFunction>
|
||||
let platformDirectives
|
||||
let isPlatformReservedTag
|
||||
let staticRenderFns
|
||||
let onceCount
|
||||
let currentOptions
|
||||
export class CodegenState {
|
||||
options: CompilerOptions;
|
||||
warn: Function;
|
||||
transforms: Array<TransformFunction>;
|
||||
dataGenFns: Array<DataGenFunction>;
|
||||
directives: { [key: string]: DirectiveFunction };
|
||||
maybeComponent: (el: ASTElement) => boolean;
|
||||
onceId: number;
|
||||
staticRenderFns: Array<string>;
|
||||
|
||||
constructor (options: CompilerOptions) {
|
||||
this.options = options
|
||||
this.warn = options.warn || baseWarn
|
||||
this.transforms = pluckModuleFunction(options.modules, 'transformCode')
|
||||
this.dataGenFns = pluckModuleFunction(options.modules, 'genData')
|
||||
this.directives = extend(extend({}, baseDirectives), options.directives)
|
||||
const isReservedTag = options.isReservedTag || no
|
||||
this.maybeComponent = (el: ASTElement) => !isReservedTag(el.tag)
|
||||
this.onceId = 0
|
||||
this.staticRenderFns = []
|
||||
}
|
||||
}
|
||||
|
||||
export type CodegenResult = {
|
||||
render: string,
|
||||
staticRenderFns: Array<string>
|
||||
};
|
||||
|
||||
export function generate (
|
||||
ast: ASTElement | void,
|
||||
options: CompilerOptions
|
||||
): {
|
||||
render: string,
|
||||
staticRenderFns: Array<string>
|
||||
} {
|
||||
// save previous staticRenderFns so generate calls can be nested
|
||||
const prevStaticRenderFns: Array<string> = staticRenderFns
|
||||
const currentStaticRenderFns: Array<string> = staticRenderFns = []
|
||||
const prevOnceCount = onceCount
|
||||
onceCount = 0
|
||||
currentOptions = options
|
||||
warn = options.warn || baseWarn
|
||||
transforms = pluckModuleFunction(options.modules, 'transformCode')
|
||||
dataGenFns = pluckModuleFunction(options.modules, 'genData')
|
||||
platformDirectives = options.directives || {}
|
||||
isPlatformReservedTag = options.isReservedTag || no
|
||||
const code = ast ? genElement(ast) : '_c("div")'
|
||||
staticRenderFns = prevStaticRenderFns
|
||||
onceCount = prevOnceCount
|
||||
): CodegenResult {
|
||||
const state = new CodegenState(options)
|
||||
const code = ast ? genElement(ast, state) : '_c("div")'
|
||||
return {
|
||||
render: `with(this){return ${code}}`,
|
||||
staticRenderFns: currentStaticRenderFns
|
||||
staticRenderFns: state.staticRenderFns
|
||||
}
|
||||
}
|
||||
|
||||
function genElement (el: ASTElement): string {
|
||||
export function genElement (el: ASTElement, state: CodegenState): string {
|
||||
if (el.staticRoot && !el.staticProcessed) {
|
||||
return genStatic(el)
|
||||
return genStatic(el, state)
|
||||
} else if (el.once && !el.onceProcessed) {
|
||||
return genOnce(el)
|
||||
return genOnce(el, state)
|
||||
} else if (el.for && !el.forProcessed) {
|
||||
return genFor(el)
|
||||
return genFor(el, state)
|
||||
} else if (el.if && !el.ifProcessed) {
|
||||
return genIf(el)
|
||||
return genIf(el, state)
|
||||
} else if (el.tag === 'template' && !el.slotTarget) {
|
||||
return genChildren(el) || 'void 0'
|
||||
return genChildren(el, state) || 'void 0'
|
||||
} else if (el.tag === 'slot') {
|
||||
return genSlot(el)
|
||||
return genSlot(el, state)
|
||||
} else {
|
||||
// component or element
|
||||
let code
|
||||
if (el.component) {
|
||||
code = genComponent(el.component, el)
|
||||
code = genComponent(el.component, el, state)
|
||||
} else {
|
||||
const data = el.plain ? undefined : genData(el)
|
||||
const data = el.plain ? undefined : genData(el, state)
|
||||
|
||||
const children = el.inlineTemplate ? null : genChildren(el, true)
|
||||
const children = el.inlineTemplate ? null : genChildren(el, state, true)
|
||||
code = `_c('${el.tag}'${
|
||||
data ? `,${data}` : '' // data
|
||||
}${
|
||||
@@ -75,25 +78,25 @@ function genElement (el: ASTElement): string {
|
||||
})`
|
||||
}
|
||||
// module transforms
|
||||
for (let i = 0; i < transforms.length; i++) {
|
||||
code = transforms[i](el, code)
|
||||
for (let i = 0; i < state.transforms.length; i++) {
|
||||
code = state.transforms[i](el, code)
|
||||
}
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
// hoist static sub-trees out
|
||||
function genStatic (el: ASTElement): string {
|
||||
function genStatic (el: ASTElement, state: CodegenState): string {
|
||||
el.staticProcessed = true
|
||||
staticRenderFns.push(`with(this){return ${genElement(el)}}`)
|
||||
return `_m(${staticRenderFns.length - 1}${el.staticInFor ? ',true' : ''})`
|
||||
state.staticRenderFns.push(`with(this){return ${genElement(el, state)}}`)
|
||||
return `_m(${state.staticRenderFns.length - 1}${el.staticInFor ? ',true' : ''})`
|
||||
}
|
||||
|
||||
// v-once
|
||||
function genOnce (el: ASTElement): string {
|
||||
function genOnce (el: ASTElement, state: CodegenState): string {
|
||||
el.onceProcessed = true
|
||||
if (el.if && !el.ifProcessed) {
|
||||
return genIf(el)
|
||||
return genIf(el, state)
|
||||
} else if (el.staticInFor) {
|
||||
let key = ''
|
||||
let parent = el.parent
|
||||
@@ -105,51 +108,76 @@ function genOnce (el: ASTElement): string {
|
||||
parent = parent.parent
|
||||
}
|
||||
if (!key) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
process.env.NODE_ENV !== 'production' && state.warn(
|
||||
`v-once can only be used inside v-for that is keyed. `
|
||||
)
|
||||
return genElement(el)
|
||||
return genElement(el, state)
|
||||
}
|
||||
return `_o(${genElement(el)},${onceCount++}${key ? `,${key}` : ``})`
|
||||
return `_o(${genElement(el, state)},${state.onceId++},${key})`
|
||||
} else {
|
||||
return genStatic(el)
|
||||
return genStatic(el, state)
|
||||
}
|
||||
}
|
||||
|
||||
function genIf (el: any): string {
|
||||
export function genIf (
|
||||
el: any,
|
||||
state: CodegenState,
|
||||
altGen?: Function,
|
||||
altEmpty?: string
|
||||
): string {
|
||||
el.ifProcessed = true // avoid recursion
|
||||
return genIfConditions(el.ifConditions.slice())
|
||||
return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)
|
||||
}
|
||||
|
||||
function genIfConditions (conditions: ASTIfConditions): string {
|
||||
function genIfConditions (
|
||||
conditions: ASTIfConditions,
|
||||
state: CodegenState,
|
||||
altGen?: Function,
|
||||
altEmpty?: string
|
||||
): string {
|
||||
if (!conditions.length) {
|
||||
return '_e()'
|
||||
return altEmpty || '_e()'
|
||||
}
|
||||
|
||||
const condition = conditions.shift()
|
||||
if (condition.exp) {
|
||||
return `(${condition.exp})?${genTernaryExp(condition.block)}:${genIfConditions(conditions)}`
|
||||
return `(${condition.exp})?${
|
||||
genTernaryExp(condition.block)
|
||||
}:${
|
||||
genIfConditions(conditions, state, altGen, altEmpty)
|
||||
}`
|
||||
} else {
|
||||
return `${genTernaryExp(condition.block)}`
|
||||
}
|
||||
|
||||
// v-if with v-once should generate code like (a)?_m(0):_m(1)
|
||||
function genTernaryExp (el) {
|
||||
return el.once ? genOnce(el) : genElement(el)
|
||||
return altGen
|
||||
? altGen(el, state)
|
||||
: el.once
|
||||
? genOnce(el, state)
|
||||
: genElement(el, state)
|
||||
}
|
||||
}
|
||||
|
||||
function genFor (el: any): string {
|
||||
export function genFor (
|
||||
el: any,
|
||||
state: CodegenState,
|
||||
altGen?: Function,
|
||||
altHelper?: string
|
||||
): string {
|
||||
const exp = el.for
|
||||
const alias = el.alias
|
||||
const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
|
||||
const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV !== 'production' &&
|
||||
maybeComponent(el) && el.tag !== 'slot' && el.tag !== 'template' && !el.key
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
state.maybeComponent(el) &&
|
||||
el.tag !== 'slot' &&
|
||||
el.tag !== 'template' &&
|
||||
!el.key
|
||||
) {
|
||||
warn(
|
||||
state.warn(
|
||||
`<${el.tag} v-for="${alias} in ${exp}">: component lists rendered with ` +
|
||||
`v-for should have explicit keys. ` +
|
||||
`See https://vuejs.org/guide/list.html#key for more info.`,
|
||||
@@ -158,18 +186,18 @@ function genFor (el: any): string {
|
||||
}
|
||||
|
||||
el.forProcessed = true // avoid recursion
|
||||
return `_l((${exp}),` +
|
||||
return `${altHelper || '_l'}((${exp}),` +
|
||||
`function(${alias}${iterator1}${iterator2}){` +
|
||||
`return ${genElement(el)}` +
|
||||
`return ${(altGen || genElement)(el, state)}` +
|
||||
'})'
|
||||
}
|
||||
|
||||
function genData (el: ASTElement): string {
|
||||
export function genData (el: ASTElement, state: CodegenState): string {
|
||||
let data = '{'
|
||||
|
||||
// directives first.
|
||||
// directives may mutate the el's other properties before they are generated.
|
||||
const dirs = genDirectives(el)
|
||||
const dirs = genDirectives(el, state)
|
||||
if (dirs) data += dirs + ','
|
||||
|
||||
// key
|
||||
@@ -192,8 +220,8 @@ function genData (el: ASTElement): string {
|
||||
data += `tag:"${el.tag}",`
|
||||
}
|
||||
// module data generation functions
|
||||
for (let i = 0; i < dataGenFns.length; i++) {
|
||||
data += dataGenFns[i](el)
|
||||
for (let i = 0; i < state.dataGenFns.length; i++) {
|
||||
data += state.dataGenFns[i](el)
|
||||
}
|
||||
// attributes
|
||||
if (el.attrs) {
|
||||
@@ -205,10 +233,10 @@ function genData (el: ASTElement): string {
|
||||
}
|
||||
// event handlers
|
||||
if (el.events) {
|
||||
data += `${genHandlers(el.events, false, warn)},`
|
||||
data += `${genHandlers(el.events, false, state.warn)},`
|
||||
}
|
||||
if (el.nativeEvents) {
|
||||
data += `${genHandlers(el.nativeEvents, true, warn)},`
|
||||
data += `${genHandlers(el.nativeEvents, true, state.warn)},`
|
||||
}
|
||||
// slot target
|
||||
if (el.slotTarget) {
|
||||
@@ -216,7 +244,7 @@ function genData (el: ASTElement): string {
|
||||
}
|
||||
// scoped slots
|
||||
if (el.scopedSlots) {
|
||||
data += `${genScopedSlots(el.scopedSlots)},`
|
||||
data += `${genScopedSlots(el.scopedSlots, state)},`
|
||||
}
|
||||
// component v-model
|
||||
if (el.model) {
|
||||
@@ -230,7 +258,7 @@ function genData (el: ASTElement): string {
|
||||
}
|
||||
// inline-template
|
||||
if (el.inlineTemplate) {
|
||||
const inlineTemplate = genInlineTemplate(el)
|
||||
const inlineTemplate = genInlineTemplate(el, state)
|
||||
if (inlineTemplate) {
|
||||
data += `${inlineTemplate},`
|
||||
}
|
||||
@@ -240,10 +268,14 @@ function genData (el: ASTElement): string {
|
||||
if (el.wrapData) {
|
||||
data = el.wrapData(data)
|
||||
}
|
||||
// v-on data wrap
|
||||
if (el.wrapListeners) {
|
||||
data = el.wrapListeners(data)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
function genDirectives (el: ASTElement): string | void {
|
||||
function genDirectives (el: ASTElement, state: CodegenState): string | void {
|
||||
const dirs = el.directives
|
||||
if (!dirs) return
|
||||
let res = 'directives:['
|
||||
@@ -252,11 +284,11 @@ function genDirectives (el: ASTElement): string | void {
|
||||
for (i = 0, l = dirs.length; i < l; i++) {
|
||||
dir = dirs[i]
|
||||
needRuntime = true
|
||||
const gen: DirectiveFunction = platformDirectives[dir.name] || baseDirectives[dir.name]
|
||||
const gen: DirectiveFunction = state.directives[dir.name]
|
||||
if (gen) {
|
||||
// compile-time directive that manipulates AST.
|
||||
// returns true if it also needs a runtime counterpart.
|
||||
needRuntime = !!gen(el, dir, warn)
|
||||
needRuntime = !!gen(el, dir, state.warn)
|
||||
}
|
||||
if (needRuntime) {
|
||||
hasRuntime = true
|
||||
@@ -274,15 +306,15 @@ function genDirectives (el: ASTElement): string | void {
|
||||
}
|
||||
}
|
||||
|
||||
function genInlineTemplate (el: ASTElement): ?string {
|
||||
function genInlineTemplate (el: ASTElement, state: CodegenState): ?string {
|
||||
const ast = el.children[0]
|
||||
if (process.env.NODE_ENV !== 'production' && (
|
||||
el.children.length > 1 || ast.type !== 1
|
||||
)) {
|
||||
warn('Inline-template components must have exactly one child element.')
|
||||
state.warn('Inline-template components must have exactly one child element.')
|
||||
}
|
||||
if (ast.type === 1) {
|
||||
const inlineRenderFns = generate(ast, currentOptions)
|
||||
const inlineRenderFns = generate(ast, state.options)
|
||||
return `inlineTemplate:{render:function(){${
|
||||
inlineRenderFns.render
|
||||
}},staticRenderFns:[${
|
||||
@@ -291,24 +323,37 @@ function genInlineTemplate (el: ASTElement): ?string {
|
||||
}
|
||||
}
|
||||
|
||||
function genScopedSlots (slots: { [key: string]: ASTElement }): string {
|
||||
function genScopedSlots (
|
||||
slots: { [key: string]: ASTElement },
|
||||
state: CodegenState
|
||||
): string {
|
||||
return `scopedSlots:_u([${
|
||||
Object.keys(slots).map(key => genScopedSlot(key, slots[key])).join(',')
|
||||
Object.keys(slots).map(key => {
|
||||
return genScopedSlot(key, slots[key], state)
|
||||
}).join(',')
|
||||
}])`
|
||||
}
|
||||
|
||||
function genScopedSlot (key: string, el: ASTElement) {
|
||||
function genScopedSlot (
|
||||
key: string,
|
||||
el: ASTElement,
|
||||
state: CodegenState
|
||||
): string {
|
||||
if (el.for && !el.forProcessed) {
|
||||
return genForScopedSlot(key, el)
|
||||
return genForScopedSlot(key, el, state)
|
||||
}
|
||||
return `{key:${key},fn:function(${String(el.attrsMap.scope)}){` +
|
||||
`return ${el.tag === 'template'
|
||||
? genChildren(el) || 'void 0'
|
||||
: genElement(el)
|
||||
? genChildren(el, state) || 'void 0'
|
||||
: genElement(el, state)
|
||||
}}}`
|
||||
}
|
||||
|
||||
function genForScopedSlot (key: string, el: any) {
|
||||
function genForScopedSlot (
|
||||
key: string,
|
||||
el: any,
|
||||
state: CodegenState
|
||||
): string {
|
||||
const exp = el.for
|
||||
const alias = el.alias
|
||||
const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
|
||||
@@ -316,11 +361,17 @@ function genForScopedSlot (key: string, el: any) {
|
||||
el.forProcessed = true // avoid recursion
|
||||
return `_l((${exp}),` +
|
||||
`function(${alias}${iterator1}${iterator2}){` +
|
||||
`return ${genScopedSlot(key, el)}` +
|
||||
`return ${genScopedSlot(key, el, state)}` +
|
||||
'})'
|
||||
}
|
||||
|
||||
function genChildren (el: ASTElement, checkSkip?: boolean): string | void {
|
||||
export function genChildren (
|
||||
el: ASTElement,
|
||||
state: CodegenState,
|
||||
checkSkip?: boolean,
|
||||
altGenElement?: Function,
|
||||
altGenNode?: Function
|
||||
): string | void {
|
||||
const children = el.children
|
||||
if (children.length) {
|
||||
const el: any = children[0]
|
||||
@@ -330,10 +381,13 @@ function genChildren (el: ASTElement, checkSkip?: boolean): string | void {
|
||||
el.tag !== 'template' &&
|
||||
el.tag !== 'slot'
|
||||
) {
|
||||
return genElement(el)
|
||||
return (altGenElement || genElement)(el, state)
|
||||
}
|
||||
const normalizationType = checkSkip ? getNormalizationType(children) : 0
|
||||
return `[${children.map(genNode).join(',')}]${
|
||||
const normalizationType = checkSkip
|
||||
? getNormalizationType(children, state.maybeComponent)
|
||||
: 0
|
||||
const gen = altGenNode || genNode
|
||||
return `[${children.map(c => gen(c, state)).join(',')}]${
|
||||
normalizationType ? `,${normalizationType}` : ''
|
||||
}`
|
||||
}
|
||||
@@ -343,7 +397,10 @@ function genChildren (el: ASTElement, checkSkip?: boolean): string | void {
|
||||
// 0: no normalization needed
|
||||
// 1: simple normalization needed (possible 1-level deep nested array)
|
||||
// 2: full normalization needed
|
||||
function getNormalizationType (children: Array<ASTNode>): number {
|
||||
function getNormalizationType (
|
||||
children: Array<ASTNode>,
|
||||
maybeComponent: (el: ASTElement) => boolean
|
||||
): number {
|
||||
let res = 0
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const el: ASTNode = children[i]
|
||||
@@ -367,28 +424,30 @@ function needsNormalization (el: ASTElement): boolean {
|
||||
return el.for !== undefined || el.tag === 'template' || el.tag === 'slot'
|
||||
}
|
||||
|
||||
function maybeComponent (el: ASTElement): boolean {
|
||||
return !isPlatformReservedTag(el.tag)
|
||||
}
|
||||
|
||||
function genNode (node: ASTNode): string {
|
||||
function genNode (node: ASTNode, state: CodegenState): string {
|
||||
if (node.type === 1) {
|
||||
return genElement(node)
|
||||
return genElement(node, state)
|
||||
} if (node.type === 3 && node.isComment) {
|
||||
return genComment(node)
|
||||
} else {
|
||||
return genText(node)
|
||||
}
|
||||
}
|
||||
|
||||
function genText (text: ASTText | ASTExpression): string {
|
||||
export function genText (text: ASTText | ASTExpression): string {
|
||||
return `_v(${text.type === 2
|
||||
? text.expression // no need for () because already wrapped in _s()
|
||||
: transformSpecialNewlines(JSON.stringify(text.text))
|
||||
})`
|
||||
}
|
||||
|
||||
function genSlot (el: ASTElement): string {
|
||||
export function genComment (comment: ASTText): string {
|
||||
return `_e(${JSON.stringify(comment.text)})`
|
||||
}
|
||||
|
||||
function genSlot (el: ASTElement, state: CodegenState): string {
|
||||
const slotName = el.slotName || '"default"'
|
||||
const children = genChildren(el)
|
||||
const children = genChildren(el, state)
|
||||
let res = `_t(${slotName}${children ? `,${children}` : ''}`
|
||||
const attrs = el.attrs && `{${el.attrs.map(a => `${camelize(a.name)}:${a.value}`).join(',')}}`
|
||||
const bind = el.attrsMap['v-bind']
|
||||
@@ -405,9 +464,13 @@ function genSlot (el: ASTElement): string {
|
||||
}
|
||||
|
||||
// componentName is el.component, take it as argument to shun flow's pessimistic refinement
|
||||
function genComponent (componentName: string, el: ASTElement): string {
|
||||
const children = el.inlineTemplate ? null : genChildren(el, true)
|
||||
return `_c(${componentName},${genData(el)}${
|
||||
function genComponent (
|
||||
componentName: string,
|
||||
el: ASTElement,
|
||||
state: CodegenState
|
||||
): string {
|
||||
const children = el.inlineTemplate ? null : genChildren(el, state, true)
|
||||
return `_c(${componentName},${genData(el, state)}${
|
||||
children ? `,${children}` : ''
|
||||
})`
|
||||
}
|
||||
|
||||
+4
-2
@@ -2,8 +2,10 @@
|
||||
|
||||
export default function bind (el: ASTElement, dir: ASTDirective) {
|
||||
el.wrapData = (code: string) => {
|
||||
return `_b(${code},'${el.tag}',${dir.value}${
|
||||
dir.modifiers && dir.modifiers.prop ? ',true' : ''
|
||||
return `_b(${code},'${el.tag}',${dir.value},${
|
||||
dir.modifiers && dir.modifiers.prop ? 'true' : 'false'
|
||||
}${
|
||||
dir.modifiers && dir.modifiers.sync ? ',true' : ''
|
||||
})`
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -1,9 +1,11 @@
|
||||
/* @flow */
|
||||
|
||||
import on from './on'
|
||||
import bind from './bind'
|
||||
import { noop } from 'shared/util'
|
||||
|
||||
export default {
|
||||
on,
|
||||
bind,
|
||||
cloak: noop
|
||||
}
|
||||
|
||||
+1
-4
@@ -41,10 +41,7 @@ export function genAssignmentCode (
|
||||
if (modelRs.idx === null) {
|
||||
return `${value}=${assignment}`
|
||||
} else {
|
||||
return `var $$exp = ${modelRs.exp}, $$idx = ${modelRs.idx};` +
|
||||
`if (!Array.isArray($$exp)){` +
|
||||
`${value}=${assignment}}` +
|
||||
`else{$$exp.splice($$idx, 1, ${assignment})}`
|
||||
return `$set(${modelRs.exp}, ${modelRs.idx}, ${assignment})`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-142
@@ -3,11 +3,12 @@
|
||||
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 { createCompilerCreator } from './create-compiler'
|
||||
|
||||
function baseCompile (
|
||||
// `createCompilerCreator` allows creating compilers that use alternative
|
||||
// parser/optimizer/codegen, e.g the SSR optimizing compiler.
|
||||
// Here we just export a default compiler using the default parts.
|
||||
export const createCompiler = createCompilerCreator(function baseCompile (
|
||||
template: string,
|
||||
options: CompilerOptions
|
||||
): CompiledResult {
|
||||
@@ -19,141 +20,4 @@ function baseCompile (
|
||||
render: code.render,
|
||||
staticRenderFns: code.staticRenderFns
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
): CompiledResult {
|
||||
const finalOptions = Object.create(baseOptions)
|
||||
const errors = []
|
||||
const tips = []
|
||||
finalOptions.warn = (msg, tip) => {
|
||||
(tip ? tips : errors).push(msg)
|
||||
}
|
||||
|
||||
if (options) {
|
||||
// merge custom modules
|
||||
if (options.modules) {
|
||||
finalOptions.modules = (baseOptions.modules || []).concat(options.modules)
|
||||
}
|
||||
// merge custom directives
|
||||
if (options.directives) {
|
||||
finalOptions.directives = extend(
|
||||
Object.create(baseOptions.directives),
|
||||
options.directives
|
||||
)
|
||||
}
|
||||
// copy other options
|
||||
for (const key in options) {
|
||||
if (key !== 'modules' && key !== 'directives') {
|
||||
finalOptions[key] = options[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const compiled = baseCompile(template, finalOptions)
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
errors.push.apply(errors, detectErrors(compiled.ast))
|
||||
}
|
||||
compiled.errors = errors
|
||||
compiled.tips = tips
|
||||
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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
+12
-7
@@ -55,6 +55,15 @@ function markStatic (node: ASTNode) {
|
||||
node.static = false
|
||||
}
|
||||
}
|
||||
if (node.ifConditions) {
|
||||
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
|
||||
const block = node.ifConditions[i].block
|
||||
markStatic(block)
|
||||
if (!block.static) {
|
||||
node.static = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,17 +90,13 @@ function markStaticRoots (node: ASTNode, isInFor: boolean) {
|
||||
}
|
||||
}
|
||||
if (node.ifConditions) {
|
||||
walkThroughConditionsBlocks(node.ifConditions, isInFor)
|
||||
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
|
||||
markStaticRoots(node.ifConditions[i].block, isInFor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function walkThroughConditionsBlocks (conditionBlocks: ASTIfConditions, isInFor: boolean): void {
|
||||
for (let i = 1, len = conditionBlocks.length; i < len; i++) {
|
||||
markStaticRoots(conditionBlocks[i].block, isInFor)
|
||||
}
|
||||
}
|
||||
|
||||
function isStatic (node: ASTNode): boolean {
|
||||
if (node.type === 2) { // expression
|
||||
return false
|
||||
|
||||
+6
-4
@@ -2,8 +2,10 @@
|
||||
|
||||
let decoder
|
||||
|
||||
export function decode (html: string): string {
|
||||
decoder = decoder || document.createElement('div')
|
||||
decoder.innerHTML = html
|
||||
return decoder.textContent
|
||||
export default {
|
||||
decode (html: string): string {
|
||||
decoder = decoder || document.createElement('div')
|
||||
decoder.innerHTML = html
|
||||
return decoder.textContent
|
||||
}
|
||||
}
|
||||
|
||||
+22
-24
@@ -13,29 +13,14 @@ import { makeMap, no } from 'shared/util'
|
||||
import { isNonPhrasingTag } from 'web/compiler/util'
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
const singleAttrIdentifier = /([^\s"'<>/=]+)/
|
||||
const singleAttrAssign = /(?:=)/
|
||||
const singleAttrValues = [
|
||||
// attr value double quotes
|
||||
/"([^"]*)"+/.source,
|
||||
// attr value, single quotes
|
||||
/'([^']*)'+/.source,
|
||||
// attr value, no quotes
|
||||
/([^\s"'=<>`]+)/.source
|
||||
]
|
||||
const attribute = new RegExp(
|
||||
'^\\s*' + singleAttrIdentifier.source +
|
||||
'(?:\\s*(' + singleAttrAssign.source + ')' +
|
||||
'\\s*(?:' + singleAttrValues.join('|') + '))?'
|
||||
)
|
||||
|
||||
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
|
||||
// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName
|
||||
// but for Vue templates we can enforce a simple charset
|
||||
const ncname = '[a-zA-Z_][\\w\\-\\.]*'
|
||||
const qnameCapture = '((?:' + ncname + '\\:)?' + ncname + ')'
|
||||
const startTagOpen = new RegExp('^<' + qnameCapture)
|
||||
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
|
||||
const startTagOpen = new RegExp(`^<${qnameCapture}`)
|
||||
const startTagClose = /^\s*(\/?)>/
|
||||
const endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>')
|
||||
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
|
||||
const doctype = /^<!DOCTYPE [^>]+>/i
|
||||
const comment = /^<!--/
|
||||
const conditionalComment = /^<!\[/
|
||||
@@ -59,6 +44,10 @@ const decodingMap = {
|
||||
const encodedAttr = /&(?:lt|gt|quot|amp);/g
|
||||
const encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10);/g
|
||||
|
||||
// #5992
|
||||
const isIgnoreNewlineTag = makeMap('pre,textarea', true)
|
||||
const shouldIgnoreFirstNewline = (tag, html) => tag && isIgnoreNewlineTag(tag) && html[0] === '\n'
|
||||
|
||||
function decodeAttr (value, shouldDecodeNewlines) {
|
||||
const re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr
|
||||
return value.replace(re, match => decodingMap[match])
|
||||
@@ -82,6 +71,9 @@ export function parseHTML (html, options) {
|
||||
const commentEnd = html.indexOf('-->')
|
||||
|
||||
if (commentEnd >= 0) {
|
||||
if (options.shouldKeepComment) {
|
||||
options.comment(html.substring(4, commentEnd))
|
||||
}
|
||||
advance(commentEnd + 3)
|
||||
continue
|
||||
}
|
||||
@@ -117,6 +109,9 @@ export function parseHTML (html, options) {
|
||||
const startTagMatch = parseStartTag()
|
||||
if (startTagMatch) {
|
||||
handleStartTag(startTagMatch)
|
||||
if (shouldIgnoreFirstNewline(lastTag, html)) {
|
||||
advance(1)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -149,16 +144,19 @@ export function parseHTML (html, options) {
|
||||
options.chars(text)
|
||||
}
|
||||
} else {
|
||||
var stackedTag = lastTag.toLowerCase()
|
||||
var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i'))
|
||||
var endTagLength = 0
|
||||
var rest = html.replace(reStackedTag, function (all, text, endTag) {
|
||||
let endTagLength = 0
|
||||
const stackedTag = lastTag.toLowerCase()
|
||||
const reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i'))
|
||||
const rest = html.replace(reStackedTag, function (all, text, endTag) {
|
||||
endTagLength = endTag.length
|
||||
if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') {
|
||||
text = text
|
||||
.replace(/<!--([\s\S]*?)-->/g, '$1')
|
||||
.replace(/<!\[CDATA\[([\s\S]*?)]]>/g, '$1')
|
||||
}
|
||||
if (shouldIgnoreFirstNewline(stackedTag, text)) {
|
||||
text = text.slice(1)
|
||||
}
|
||||
if (options.chars) {
|
||||
options.chars(text)
|
||||
}
|
||||
@@ -222,7 +220,7 @@ export function parseHTML (html, options) {
|
||||
}
|
||||
}
|
||||
|
||||
const unary = isUnaryTag(tagName) || tagName === 'html' && lastTag === 'head' || !!unarySlash
|
||||
const unary = isUnaryTag(tagName) || !!unarySlash
|
||||
|
||||
const l = match.attrs.length
|
||||
const attrs = new Array(l)
|
||||
|
||||
+21
-6
@@ -1,6 +1,6 @@
|
||||
/* @flow */
|
||||
|
||||
import { decode } from 'he'
|
||||
import he from 'he'
|
||||
import { parseHTML } from './html-parser'
|
||||
import { parseText } from './text-parser'
|
||||
import { parseFilters } from './filter-parser'
|
||||
@@ -28,7 +28,7 @@ const argRE = /:(.*)$/
|
||||
const bindRE = /^:|^v-bind:/
|
||||
const modifierRE = /\.[^.]+/g
|
||||
|
||||
const decodeHTMLCached = cached(decode)
|
||||
const decodeHTMLCached = cached(he.decode)
|
||||
|
||||
// configurable state
|
||||
export let warn
|
||||
@@ -48,12 +48,15 @@ export function parse (
|
||||
options: CompilerOptions
|
||||
): ASTElement | void {
|
||||
warn = options.warn || baseWarn
|
||||
platformGetTagNamespace = options.getTagNamespace || no
|
||||
platformMustUseProp = options.mustUseProp || no
|
||||
|
||||
platformIsPreTag = options.isPreTag || no
|
||||
preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
|
||||
platformMustUseProp = options.mustUseProp || no
|
||||
platformGetTagNamespace = options.getTagNamespace || no
|
||||
|
||||
transforms = pluckModuleFunction(options.modules, 'transformNode')
|
||||
preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
|
||||
postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')
|
||||
|
||||
delimiters = options.delimiters
|
||||
|
||||
const stack = []
|
||||
@@ -87,6 +90,7 @@ export function parse (
|
||||
isUnaryTag: options.isUnaryTag,
|
||||
canBeLeftOpenTag: options.canBeLeftOpenTag,
|
||||
shouldDecodeNewlines: options.shouldDecodeNewlines,
|
||||
shouldKeepComment: options.comments,
|
||||
start (tag, attrs, unary) {
|
||||
// check namespace.
|
||||
// inherit parent ns if there is one
|
||||
@@ -271,6 +275,13 @@ export function parse (
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
comment (text: string) {
|
||||
currentParent.children.push({
|
||||
type: 3,
|
||||
text,
|
||||
isComment: true
|
||||
})
|
||||
}
|
||||
})
|
||||
return root
|
||||
@@ -420,6 +431,8 @@ function processSlot (el) {
|
||||
const slotTarget = getBindingAttr(el, 'slot')
|
||||
if (slotTarget) {
|
||||
el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
|
||||
// preserve slot as an attribute for native shadow DOM compat
|
||||
addAttr(el, 'slot', slotTarget)
|
||||
}
|
||||
if (el.tag === 'template') {
|
||||
el.slotScope = getAndRemoveAttr(el, 'scope')
|
||||
@@ -472,7 +485,9 @@ function processAttrs (el) {
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isProp || platformMustUseProp(el.tag, el.attrsMap.type, name)) {
|
||||
if (isProp || (
|
||||
!el.component && platformMustUseProp(el.tag, el.attrsMap.type, name)
|
||||
)) {
|
||||
addProp(el, name, value)
|
||||
} else {
|
||||
addAttr(el, name, value)
|
||||
|
||||
Reference in New Issue
Block a user