.'
22 | )
23 | }
24 | }
25 | el.staticStyle = JSON.stringify(parseStyleText(staticStyle))
26 | }
27 |
28 | const styleBinding = getBindingAttr(el, 'style', false /* getStatic */)
29 | if (styleBinding) {
30 | el.styleBinding = styleBinding
31 | }
32 | }
33 |
34 | function genData (el: ASTElement): string {
35 | let data = ''
36 | if (el.staticStyle) {
37 | data += `staticStyle:${el.staticStyle},`
38 | }
39 | if (el.styleBinding) {
40 | data += `style:(${el.styleBinding}),`
41 | }
42 | return data
43 | }
44 |
45 | export default {
46 | staticKeys: ['staticStyle'],
47 | transformNode,
48 | genData
49 | }
50 |
--------------------------------------------------------------------------------
/src/core/instance/render-helpers/bind-object-props.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import config from 'core/config'
4 |
5 | import {
6 | warn,
7 | isObject,
8 | toObject,
9 | isReservedAttribute
10 | } from 'core/util/index'
11 |
12 | /**
13 | * Runtime helper for merging v-bind="object" into a VNode's data.
14 | */
15 | export function bindObjectProps (
16 | data: any,
17 | tag: string,
18 | value: any,
19 | asProp: boolean,
20 | isSync?: boolean
21 | ): VNodeData {
22 | if (value) {
23 | if (!isObject(value)) {
24 | process.env.NODE_ENV !== 'production' && warn(
25 | 'v-bind without argument expects an Object or Array value',
26 | this
27 | )
28 | } else {
29 | if (Array.isArray(value)) {
30 | value = toObject(value)
31 | }
32 | let hash
33 | for (const key in value) {
34 | if (
35 | key === 'class' ||
36 | key === 'style' ||
37 | isReservedAttribute(key)
38 | ) {
39 | hash = data
40 | } else {
41 | const type = data.attrs && data.attrs.type
42 | hash = asProp || config.mustUseProp(tag, type, key)
43 | ? data.domProps || (data.domProps = {})
44 | : data.attrs || (data.attrs = {})
45 | }
46 | if (!(key in hash)) {
47 | hash[key] = value[key]
48 |
49 | if (isSync) {
50 | const on = data.on || (data.on = {})
51 | on[`update:${key}`] = function ($event) {
52 | value[key] = $event
53 | }
54 | }
55 | }
56 | }
57 | }
58 | }
59 | return data
60 | }
61 |
--------------------------------------------------------------------------------
/src/core/instance/render-helpers/resolve-slots.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | /**
4 | * Runtime helper for resolving raw children VNodes into a slot object.
5 | */
6 | export function resolveSlots (
7 | children: ?Array
,
8 | context: ?Component
9 | ): { [key: string]: Array } {
10 | const slots = {}
11 | if (!children) {
12 | return slots
13 | }
14 | const defaultSlot = []
15 | for (let i = 0, l = children.length; i < l; i++) {
16 | const child = children[i]
17 | // named slots should only be respected if the vnode was rendered in the
18 | // same context.
19 | if ((child.context === context || child.functionalContext === context) &&
20 | child.data && child.data.slot != null
21 | ) {
22 | const name = child.data.slot
23 | const slot = (slots[name] || (slots[name] = []))
24 | if (child.tag === 'template') {
25 | slot.push.apply(slot, child.children)
26 | } else {
27 | slot.push(child)
28 | }
29 | } else {
30 | defaultSlot.push(child)
31 | }
32 | }
33 | // ignore whitespace
34 | if (!defaultSlot.every(isWhitespace)) {
35 | slots.default = defaultSlot
36 | }
37 | return slots
38 | }
39 |
40 | function isWhitespace (node: VNode): boolean {
41 | return node.isComment || node.text === ' '
42 | }
43 |
44 | export function resolveScopedSlots (
45 | fns: ScopedSlotsData, // see flow/vnode
46 | res?: Object
47 | ): { [key: string]: Function } {
48 | res = res || {}
49 | for (let i = 0; i < fns.length; i++) {
50 | if (Array.isArray(fns[i])) {
51 | resolveScopedSlots(fns[i], res)
52 | } else {
53 | res[fns[i].key] = fns[i].fn
54 | }
55 | }
56 | return res
57 | }
58 |
--------------------------------------------------------------------------------
/src/core/instance/render-helpers/render-static.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { cloneVNode, cloneVNodes } from 'core/vdom/vnode'
4 |
5 | /**
6 | * Runtime helper for rendering static trees.
7 | */
8 | export function renderStatic (
9 | index: number,
10 | isInFor?: boolean
11 | ): VNode | Array {
12 | let tree = this._staticTrees[index]
13 | // if has already-rendered static tree and not inside v-for,
14 | // we can reuse the same tree by doing a shallow clone.
15 | if (tree && !isInFor) {
16 | return Array.isArray(tree)
17 | ? cloneVNodes(tree)
18 | : cloneVNode(tree)
19 | }
20 | // otherwise, render a fresh tree.
21 | tree = this._staticTrees[index] =
22 | this.$options.staticRenderFns[index].call(this._renderProxy)
23 | markStatic(tree, `__static__${index}`, false)
24 | return tree
25 | }
26 |
27 | /**
28 | * Runtime helper for v-once.
29 | * Effectively it means marking the node as static with a unique key.
30 | */
31 | export function markOnce (
32 | tree: VNode | Array,
33 | index: number,
34 | key: string
35 | ) {
36 | markStatic(tree, `__once__${index}${key ? `_${key}` : ``}`, true)
37 | return tree
38 | }
39 |
40 | function markStatic (
41 | tree: VNode | Array,
42 | key: string,
43 | isOnce: boolean
44 | ) {
45 | if (Array.isArray(tree)) {
46 | for (let i = 0; i < tree.length; i++) {
47 | if (tree[i] && typeof tree[i] !== 'string') {
48 | markStaticNode(tree[i], `${key}_${i}`, isOnce)
49 | }
50 | }
51 | } else {
52 | markStaticNode(tree, key, isOnce)
53 | }
54 | }
55 |
56 | function markStaticNode (node, key, isOnce) {
57 | node.isStatic = true
58 | node.key = key
59 | node.isOnce = isOnce
60 | }
61 |
--------------------------------------------------------------------------------
/src/platforms/web/util/attrs.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { makeMap } from 'shared/util'
4 |
5 | // these are reserved for web because they are directly compiled away
6 | // during template compilation
7 | export const isReservedAttr = makeMap('style,class')
8 |
9 | // attributes that should be using props for binding
10 | const acceptValue = makeMap('input,textarea,option,select')
11 | export const mustUseProp = (tag: string, type: ?string, attr: string): boolean => {
12 | return (
13 | (attr === 'value' && acceptValue(tag)) && type !== 'button' ||
14 | (attr === 'selected' && tag === 'option') ||
15 | (attr === 'checked' && tag === 'input') ||
16 | (attr === 'muted' && tag === 'video')
17 | )
18 | }
19 |
20 |
21 | export const isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck')
22 |
23 | export const isBooleanAttr = makeMap(
24 | 'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' +
25 | 'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' +
26 | 'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' +
27 | 'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' +
28 | 'required,reversed,scoped,seamless,selected,sortable,translate,' +
29 | 'truespeed,typemustmatch,visible'
30 | )
31 |
32 | export const xlinkNS = 'http://www.w3.org/1999/xlink'
33 |
34 | export const isXlink = (name: string): boolean => {
35 | return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink'
36 | }
37 |
38 | export const getXlinkProp = (name: string): string => {
39 | return isXlink(name) ? name.slice(6, name.length) : ''
40 | }
41 |
42 | export const isFalsyAttrValue = (val: any): boolean => {
43 | return val == null || val === false
44 | }
45 |
--------------------------------------------------------------------------------
/src/core/global-api/index.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import config from '../config'
4 | import { initUse } from './use'
5 | import { initMixin } from './mixin'
6 | import { initExtend } from './extend'
7 | import { initAssetRegisters } from './assets'
8 | import { set, del } from '../observer/index'
9 | import { ASSET_TYPES } from 'shared/constants'
10 | import builtInComponents from '../components/index'
11 |
12 | import {
13 | warn,
14 | extend,
15 | nextTick,
16 | mergeOptions,
17 | defineReactive
18 | } from '../util/index'
19 |
20 | export function initGlobalAPI (Vue: GlobalAPI) {
21 | // config
22 | const configDef = {}
23 | configDef.get = () => config
24 | if (process.env.NODE_ENV !== 'production') {
25 | configDef.set = () => {
26 | warn(
27 | 'Do not replace the Vue.config object, set individual fields instead.'
28 | )
29 | }
30 | }
31 | Object.defineProperty(Vue, 'config', configDef)
32 |
33 | // exposed util methods.
34 | // NOTE: these are not considered part of the public API - avoid relying on
35 | // them unless you are aware of the risk.
36 | Vue.util = {
37 | warn,
38 | extend,
39 | mergeOptions,
40 | defineReactive
41 | }
42 |
43 | Vue.set = set
44 | Vue.delete = del
45 | Vue.nextTick = nextTick
46 |
47 | Vue.options = Object.create(null)
48 | ASSET_TYPES.forEach(type => {
49 | Vue.options[type + 's'] = Object.create(null)
50 | })
51 |
52 | // this is used to identify the "base" constructor to extend all plain-object
53 | // components with in Weex's multi-instance scenarios.
54 | Vue.options._base = Vue
55 |
56 | extend(Vue.options.components, builtInComponents)
57 |
58 | initUse(Vue)
59 | initMixin(Vue)
60 | initExtend(Vue)
61 | initAssetRegisters(Vue)
62 | }
63 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const resolve = require('path').resolve
3 | const exec = require('child_process').exec
4 | const prettier = require('prettier')
5 | const compiler = require('../packages/wxml-transpiler/build.js')
6 |
7 | const fileList = ['./pages/index/index.wxml']
8 |
9 | const srcFiles = fileList.reverse()
10 | const distDir = resolve(__dirname, './dist')
11 | const vueDist = resolve(distDir, 'test.vue.dist.js')
12 | const vueOriDist = resolve(distDir, 'test.vue.ori.dist.js')
13 | const wccDist = resolve(distDir, 'test.wcc.dist.js')
14 | const wccOriDist = resolve(distDir, 'test.wcc.ori.dist.js')
15 | const diffDist = resolve(distDir, 'vue-wcc.diff')
16 | const formatRule = {
17 | tabWidth: 2,
18 | useTabs: false,
19 | semi: false,
20 | singleQuote: true
21 | }
22 |
23 | var files = srcFiles.map(path => ({
24 | path,
25 | template: fs.readFileSync(resolve(__dirname, path), 'utf-8')
26 | }))
27 |
28 | if (!fs.existsSync(distDir)) {
29 | fs.mkdirSync(distDir)
30 | }
31 |
32 | exec(
33 | `cd ${__dirname} && ${resolve(__dirname, './lib/wcc')} -b ${srcFiles.join(' ')}`,
34 | (err, wccRes) => {
35 | if (err) throw err
36 | fs.writeFileSync(wccOriDist, wccRes, 'utf8')
37 | fs.writeFileSync(wccDist, prettier.format(wccRes, formatRule), 'utf8')
38 | const vueRes = compiler.compile(files)
39 | console.log(vueRes.tags)
40 | fs.writeFileSync(vueOriDist, vueRes.render, 'utf8')
41 | fs.writeFileSync(
42 | vueDist,
43 | prettier.format(vueRes.render, formatRule),
44 | 'utf8'
45 | )
46 | exec(`diff -rp ${vueDist} ${wccDist}`, (err, diffRes) => {
47 | // console.log(res)
48 | fs.writeFileSync(diffDist, diffRes, 'utf8')
49 | })
50 | }
51 | )
52 |
53 | console.log('See Result in test/dist dir.')
54 |
--------------------------------------------------------------------------------
/src/compiler/create-compiler.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { extend } from 'shared/util'
4 | import { detectErrors } from './error-detector'
5 | import { createCompileToFunctionFn } from './to-function'
6 |
7 | export function createCompilerCreator (baseCompile: Function): Function {
8 | return function createCompiler (baseOptions: CompilerOptions) {
9 | function compile (
10 | template: string,
11 | options?: CompilerOptions
12 | ): CompiledResult {
13 | const finalOptions = Object.create(baseOptions)
14 | const errors = []
15 | const tips = []
16 | finalOptions.warn = (msg, tip) => {
17 | (tip ? tips : errors).push(msg)
18 | }
19 |
20 | if (options) {
21 | // merge custom modules
22 | if (options.modules) {
23 | finalOptions.modules =
24 | (baseOptions.modules || []).concat(options.modules)
25 | }
26 | // merge custom directives
27 | if (options.directives) {
28 | finalOptions.directives = extend(
29 | Object.create(baseOptions.directives),
30 | options.directives
31 | )
32 | }
33 | // copy other options
34 | for (const key in options) {
35 | if (key !== 'modules' && key !== 'directives') {
36 | finalOptions[key] = options[key]
37 | }
38 | }
39 | }
40 |
41 | const compiled = baseCompile(template, finalOptions)
42 | if (process.env.NODE_ENV !== 'production') {
43 | errors.push.apply(errors, detectErrors(compiled.ast))
44 | }
45 | compiled.errors = errors
46 | compiled.tips = tips
47 | return compiled
48 | }
49 |
50 | return {
51 | compile,
52 | compileToFunctions: createCompileToFunctionFn(compile)
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/core/vdom/create-functional-component.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import VNode from './vnode'
4 | import { createElement } from './create-element'
5 | import { resolveInject } from '../instance/inject'
6 | import { resolveSlots } from '../instance/render-helpers/resolve-slots'
7 |
8 | import {
9 | isDef,
10 | camelize,
11 | validateProp
12 | } from '../util/index'
13 |
14 | export function createFunctionalComponent (
15 | Ctor: Class,
16 | propsData: ?Object,
17 | data: VNodeData,
18 | context: Component,
19 | children: ?Array
20 | ): VNode | void {
21 | const props = {}
22 | const propOptions = Ctor.options.props
23 | if (isDef(propOptions)) {
24 | for (const key in propOptions) {
25 | props[key] = validateProp(key, propOptions, propsData || {})
26 | }
27 | } else {
28 | if (isDef(data.attrs)) mergeProps(props, data.attrs)
29 | if (isDef(data.props)) mergeProps(props, data.props)
30 | }
31 | // ensure the createElement function in functional components
32 | // gets a unique context - this is necessary for correct named slot check
33 | const _context = Object.create(context)
34 | const h = (a, b, c, d) => createElement(_context, a, b, c, d, true)
35 | const vnode = Ctor.options.render.call(null, h, {
36 | data,
37 | props,
38 | children,
39 | parent: context,
40 | listeners: data.on || {},
41 | injections: resolveInject(Ctor.options.inject, context),
42 | slots: () => resolveSlots(children, context)
43 | })
44 | if (vnode instanceof VNode) {
45 | vnode.functionalContext = context
46 | vnode.functionalOptions = Ctor.options
47 | if (data.slot) {
48 | (vnode.data || (vnode.data = {})).slot = data.slot
49 | }
50 | }
51 | return vnode
52 | }
53 |
54 | function mergeProps (to, from) {
55 | for (const key in from) {
56 | to[camelize(key)] = from[key]
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/core/instance/inject.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { warn } from '../util/index'
4 | import { hasSymbol } from 'core/util/env'
5 | import { defineReactive, observerState } from '../observer/index'
6 |
7 | export function initProvide (vm: Component) {
8 | const provide = vm.$options.provide
9 | if (provide) {
10 | vm._provided = typeof provide === 'function'
11 | ? provide.call(vm)
12 | : provide
13 | }
14 | }
15 |
16 | export function initInjections (vm: Component) {
17 | const result = resolveInject(vm.$options.inject, vm)
18 | if (result) {
19 | observerState.shouldConvert = false
20 | Object.keys(result).forEach(key => {
21 | /* istanbul ignore else */
22 | if (process.env.NODE_ENV !== 'production') {
23 | defineReactive(vm, key, result[key], () => {
24 | warn(
25 | `Avoid mutating an injected value directly since the changes will be ` +
26 | `overwritten whenever the provided component re-renders. ` +
27 | `injection being mutated: "${key}"`,
28 | vm
29 | )
30 | })
31 | } else {
32 | defineReactive(vm, key, result[key])
33 | }
34 | })
35 | observerState.shouldConvert = true
36 | }
37 | }
38 |
39 | export function resolveInject (inject: any, vm: Component): ?Object {
40 | if (inject) {
41 | // inject is :any because flow is not smart enough to figure out cached
42 | const result = Object.create(null)
43 | const keys = hasSymbol
44 | ? Reflect.ownKeys(inject)
45 | : Object.keys(inject)
46 |
47 | for (let i = 0; i < keys.length; i++) {
48 | const key = keys[i]
49 | const provideKey = inject[key]
50 | let source = vm
51 | while (source) {
52 | if (source._provided && provideKey in source._provided) {
53 | result[key] = source._provided[provideKey]
54 | break
55 | }
56 | source = source.$parent
57 | }
58 | if (process.env.NODE_ENV !== 'production' && !source) {
59 | warn(`Injection "${key}" not found`, vm)
60 | }
61 | }
62 | return result
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/core/vdom/helpers/update-listeners.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { warn } from 'core/util/index'
4 | import { cached, isUndef } from 'shared/util'
5 |
6 | const normalizeEvent = cached((name: string): {
7 | name: string,
8 | once: boolean,
9 | capture: boolean,
10 | passive: boolean
11 | } => {
12 | const passive = name.charAt(0) === '&'
13 | name = passive ? name.slice(1) : name
14 | const once = name.charAt(0) === '~' // Prefixed last, checked first
15 | name = once ? name.slice(1) : name
16 | const capture = name.charAt(0) === '!'
17 | name = capture ? name.slice(1) : name
18 | return {
19 | name,
20 | once,
21 | capture,
22 | passive
23 | }
24 | })
25 |
26 | export function createFnInvoker (fns: Function | Array): Function {
27 | function invoker () {
28 | const fns = invoker.fns
29 | if (Array.isArray(fns)) {
30 | const cloned = fns.slice()
31 | for (let i = 0; i < cloned.length; i++) {
32 | cloned[i].apply(null, arguments)
33 | }
34 | } else {
35 | // return handler return value for single handlers
36 | return fns.apply(null, arguments)
37 | }
38 | }
39 | invoker.fns = fns
40 | return invoker
41 | }
42 |
43 | export function updateListeners (
44 | on: Object,
45 | oldOn: Object,
46 | add: Function,
47 | remove: Function,
48 | vm: Component
49 | ) {
50 | let name, cur, old, event
51 | for (name in on) {
52 | cur = on[name]
53 | old = oldOn[name]
54 | event = normalizeEvent(name)
55 | if (isUndef(cur)) {
56 | process.env.NODE_ENV !== 'production' && warn(
57 | `Invalid handler for event "${event.name}": got ` + String(cur),
58 | vm
59 | )
60 | } else if (isUndef(old)) {
61 | if (isUndef(cur.fns)) {
62 | cur = on[name] = createFnInvoker(cur)
63 | }
64 | add(event.name, cur, event.once, event.capture, event.passive)
65 | } else if (cur !== old) {
66 | old.fns = cur
67 | on[name] = old
68 | }
69 | }
70 | for (name in oldOn) {
71 | if (isUndef(on[name])) {
72 | event = normalizeEvent(name)
73 | remove(event.name, oldOn[name], event.capture)
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/core/vdom/helpers/extract-props.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import {
4 | tip,
5 | hasOwn,
6 | isDef,
7 | isUndef,
8 | hyphenate,
9 | formatComponentName
10 | } from 'core/util/index'
11 |
12 | export function extractPropsFromVNodeData (
13 | data: VNodeData,
14 | Ctor: Class,
15 | tag?: string
16 | ): ?Object {
17 | // we are only extracting raw values here.
18 | // validation and default values are handled in the child
19 | // component itself.
20 | const propOptions = Ctor.options.props
21 | if (isUndef(propOptions)) {
22 | return
23 | }
24 | const res = {}
25 | const { attrs, props } = data
26 | if (isDef(attrs) || isDef(props)) {
27 | for (const key in propOptions) {
28 | const altKey = hyphenate(key)
29 | if (process.env.NODE_ENV !== 'production') {
30 | const keyInLowerCase = key.toLowerCase()
31 | if (
32 | key !== keyInLowerCase &&
33 | attrs && hasOwn(attrs, keyInLowerCase)
34 | ) {
35 | tip(
36 | `Prop "${keyInLowerCase}" is passed to component ` +
37 | `${formatComponentName(tag || Ctor)}, but the declared prop name is` +
38 | ` "${key}". ` +
39 | `Note that HTML attributes are case-insensitive and camelCased ` +
40 | `props need to use their kebab-case equivalents when using in-DOM ` +
41 | `templates. You should probably use "${altKey}" instead of "${key}".`
42 | )
43 | }
44 | }
45 | checkProp(res, props, key, altKey, true) ||
46 | checkProp(res, attrs, key, altKey, false)
47 | }
48 | }
49 | return res
50 | }
51 |
52 | function checkProp (
53 | res: Object,
54 | hash: ?Object,
55 | key: string,
56 | altKey: string,
57 | preserve: boolean
58 | ): boolean {
59 | if (isDef(hash)) {
60 | if (hasOwn(hash, key)) {
61 | res[key] = hash[key]
62 | if (!preserve) {
63 | delete hash[key]
64 | }
65 | return true
66 | } else if (hasOwn(hash, altKey)) {
67 | res[key] = hash[altKey]
68 | if (!preserve) {
69 | delete hash[altKey]
70 | }
71 | return true
72 | }
73 | }
74 | return false
75 | }
76 |
--------------------------------------------------------------------------------
/src/platforms/web/util/style.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { cached, extend, toObject } from 'shared/util'
4 |
5 | export const parseStyleText = cached(function (cssText) {
6 | const res = {}
7 | const listDelimiter = /;(?![^(]*\))/g
8 | const propertyDelimiter = /:(.+)/
9 | cssText.split(listDelimiter).forEach(function (item) {
10 | if (item) {
11 | var tmp = item.split(propertyDelimiter)
12 | tmp.length > 1 && (res[tmp[0].trim()] = tmp[1].trim())
13 | }
14 | })
15 | return res
16 | })
17 |
18 | // merge static and dynamic style data on the same vnode
19 | function normalizeStyleData (data: VNodeData): ?Object {
20 | const style = normalizeStyleBinding(data.style)
21 | // static style is pre-processed into an object during compilation
22 | // and is always a fresh object, so it's safe to merge into it
23 | return data.staticStyle
24 | ? extend(data.staticStyle, style)
25 | : style
26 | }
27 |
28 | // normalize possible array / string values into Object
29 | export function normalizeStyleBinding (bindingStyle: any): ?Object {
30 | if (Array.isArray(bindingStyle)) {
31 | return toObject(bindingStyle)
32 | }
33 | if (typeof bindingStyle === 'string') {
34 | return parseStyleText(bindingStyle)
35 | }
36 | return bindingStyle
37 | }
38 |
39 | /**
40 | * parent component style should be after child's
41 | * so that parent component's style could override it
42 | */
43 | export function getStyle (vnode: VNode, checkChild: boolean): Object {
44 | const res = {}
45 | let styleData
46 |
47 | if (checkChild) {
48 | let childNode = vnode
49 | while (childNode.componentInstance) {
50 | childNode = childNode.componentInstance._vnode
51 | if (childNode.data && (styleData = normalizeStyleData(childNode.data))) {
52 | extend(res, styleData)
53 | }
54 | }
55 | }
56 |
57 | if ((styleData = normalizeStyleData(vnode.data))) {
58 | extend(res, styleData)
59 | }
60 |
61 | let parentNode = vnode
62 | while ((parentNode = parentNode.parent)) {
63 | if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) {
64 | extend(res, styleData)
65 | }
66 | }
67 | return res
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/src/platforms/web/util/class.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { isDef, isObject } from 'shared/util'
4 |
5 | export function genClassForVnode (vnode: VNode): string {
6 | let data = vnode.data
7 | let parentNode = vnode
8 | let childNode = vnode
9 | while (isDef(childNode.componentInstance)) {
10 | childNode = childNode.componentInstance._vnode
11 | if (childNode.data) {
12 | data = mergeClassData(childNode.data, data)
13 | }
14 | }
15 | while (isDef(parentNode = parentNode.parent)) {
16 | if (parentNode.data) {
17 | data = mergeClassData(data, parentNode.data)
18 | }
19 | }
20 | return renderClass(data.staticClass, data.class)
21 | }
22 |
23 | function mergeClassData (child: VNodeData, parent: VNodeData): {
24 | staticClass: string,
25 | class: any
26 | } {
27 | return {
28 | staticClass: concat(child.staticClass, parent.staticClass),
29 | class: isDef(child.class)
30 | ? [child.class, parent.class]
31 | : parent.class
32 | }
33 | }
34 |
35 | export function renderClass (
36 | staticClass: ?string,
37 | dynamicClass: any
38 | ): string {
39 | if (isDef(staticClass) || isDef(dynamicClass)) {
40 | return concat(staticClass, stringifyClass(dynamicClass))
41 | }
42 | /* istanbul ignore next */
43 | return ''
44 | }
45 |
46 | export function concat (a: ?string, b: ?string): string {
47 | return a ? b ? (a + ' ' + b) : a : (b || '')
48 | }
49 |
50 | export function stringifyClass (value: any): string {
51 | if (Array.isArray(value)) {
52 | return stringifyArray(value)
53 | }
54 | if (isObject(value)) {
55 | return stringifyObject(value)
56 | }
57 | if (typeof value === 'string') {
58 | return value
59 | }
60 | /* istanbul ignore next */
61 | return ''
62 | }
63 |
64 | function stringifyArray (value: Array): string {
65 | let res = ''
66 | let stringified
67 | for (let i = 0, l = value.length; i < l; i++) {
68 | if (isDef(stringified = stringifyClass(value[i])) && stringified !== '') {
69 | if (res) res += ' '
70 | res += stringified
71 | }
72 | }
73 | return res
74 | }
75 |
76 | function stringifyObject (value: Object): string {
77 | let res = ''
78 | for (const key in value) {
79 | if (value[key]) {
80 | if (res) res += ' '
81 | res += key
82 | }
83 | }
84 | return res
85 | }
86 |
--------------------------------------------------------------------------------
/src/core/instance/proxy.js:
--------------------------------------------------------------------------------
1 | /* not type checking this file because flow doesn't play well with Proxy */
2 |
3 | import config from 'core/config'
4 | import { warn, makeMap } from '../util/index'
5 |
6 | let initProxy
7 |
8 | if (process.env.NODE_ENV !== 'production') {
9 | const allowedGlobals = makeMap(
10 | 'Infinity,undefined,NaN,isFinite,isNaN,' +
11 | 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
12 | 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +
13 | 'require' // for Webpack/Browserify
14 | )
15 |
16 | const warnNonPresent = (target, key) => {
17 | warn(
18 | `Property or method "${key}" is not defined on the instance but ` +
19 | `referenced during render. Make sure to declare reactive data ` +
20 | `properties in the data option.`,
21 | target
22 | )
23 | }
24 |
25 | const hasProxy =
26 | typeof Proxy !== 'undefined' &&
27 | Proxy.toString().match(/native code/)
28 |
29 | if (hasProxy) {
30 | const isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta')
31 | config.keyCodes = new Proxy(config.keyCodes, {
32 | set (target, key, value) {
33 | if (isBuiltInModifier(key)) {
34 | warn(`Avoid overwriting built-in modifier in config.keyCodes: .${key}`)
35 | return false
36 | } else {
37 | target[key] = value
38 | return true
39 | }
40 | }
41 | })
42 | }
43 |
44 | const hasHandler = {
45 | has (target, key) {
46 | const has = key in target
47 | const isAllowed = allowedGlobals(key) || key.charAt(0) === '_'
48 | if (!has && !isAllowed) {
49 | warnNonPresent(target, key)
50 | }
51 | return has || !isAllowed
52 | }
53 | }
54 |
55 | const getHandler = {
56 | get (target, key) {
57 | if (typeof key === 'string' && !(key in target)) {
58 | warnNonPresent(target, key)
59 | }
60 | return target[key]
61 | }
62 | }
63 |
64 | initProxy = function initProxy (vm) {
65 | if (hasProxy) {
66 | // determine which proxy handler to use
67 | const options = vm.$options
68 | const handlers = options.render && options.render._withStripped
69 | ? getHandler
70 | : hasHandler
71 | vm._renderProxy = new Proxy(vm, handlers)
72 | } else {
73 | vm._renderProxy = vm
74 | }
75 | }
76 | }
77 |
78 | export { initProxy }
79 |
--------------------------------------------------------------------------------
/flow/options.js:
--------------------------------------------------------------------------------
1 | declare type InternalComponentOptions = {
2 | _isComponent: true;
3 | parent: Component;
4 | propsData: ?Object;
5 | _parentVnode: VNode;
6 | _parentListeners: ?Object;
7 | _renderChildren: ?Array;
8 | _componentTag: ?string;
9 | _parentElm: ?Node;
10 | _refElm: ?Node;
11 | render?: Function;
12 | staticRenderFns?: Array
13 | };
14 |
15 | declare type ComponentOptions = {
16 | // data
17 | data: Object | Function | void;
18 | props?: { [key: string]: PropOptions };
19 | propsData?: ?Object;
20 | computed?: {
21 | [key: string]: Function | {
22 | get?: Function;
23 | set?: Function;
24 | cache?: boolean
25 | }
26 | };
27 | methods?: { [key: string]: Function };
28 | watch?: { [key: string]: Function | string };
29 |
30 | // DOM
31 | el?: string | Element;
32 | template?: string;
33 | render: (h: () => VNode) => VNode;
34 | renderError?: (h: () => VNode, err: Error) => VNode;
35 | staticRenderFns?: Array<() => VNode>;
36 |
37 | // lifecycle
38 | beforeCreate?: Function;
39 | created?: Function;
40 | beforeMount?: Function;
41 | mounted?: Function;
42 | beforeUpdate?: Function;
43 | updated?: Function;
44 | activated?: Function;
45 | deactivated?: Function;
46 | beforeDestroy?: Function;
47 | destroyed?: Function;
48 |
49 | // assets
50 | directives?: { [key: string]: Object };
51 | components?: { [key: string]: Class };
52 | transitions?: { [key: string]: Object };
53 | filters?: { [key: string]: Function };
54 |
55 | // context
56 | provide?: { [key: string | Symbol]: any } | () => { [key: string | Symbol]: any };
57 | inject?: { [key: string]: string | Symbol } | Array;
58 |
59 | // component v-model customization
60 | model?: {
61 | prop?: string;
62 | event?: string;
63 | };
64 |
65 | // misc
66 | parent?: Component;
67 | mixins?: Array