├── .babelrc ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── ---bug-report.md │ ├── ---issue-with-the-documentation-or-website.md │ ├── ---issue-with-vue-native-router.md │ └── ---questions-or-help-with-usage.md ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── COMPONENT.md ├── LICENSE ├── README.md ├── __tests__ └── unit │ └── features │ └── instance │ └── init.spec.js ├── converting-react-native-project.md ├── flow ├── compiler.js ├── component.js ├── global-api.js ├── modules.js ├── options.js ├── ssr.js └── vnode.js ├── jest.config.json ├── jsconfig.json ├── package-lock.json ├── package.json ├── packages ├── vue-native-core │ ├── README.md │ ├── index.js │ └── package.json ├── vue-native-helper │ ├── README.md │ ├── index.js │ └── package.json ├── vue-native-scripts │ ├── .npmignore │ ├── README.md │ ├── bin │ │ └── vue-native-script.js │ ├── index.js │ └── package.json └── vue-native-template-compiler │ ├── README.md │ ├── index.js │ └── package.json ├── scripts ├── .eslintrc ├── alias.js ├── build.js ├── ci.sh ├── config.js ├── git-hooks │ └── pre-commit └── release.sh ├── src ├── compiler │ ├── codegen │ │ ├── events.js │ │ └── index.js │ ├── directives │ │ ├── bind.js │ │ ├── index.js │ │ └── model.js │ ├── error-detector.js │ ├── helpers.js │ ├── index.js │ ├── optimizer.js │ └── parser │ │ ├── entity-decoder.js │ │ ├── filter-parser.js │ │ ├── html-parser.js │ │ ├── index.js │ │ └── text-parser.js ├── core │ ├── components │ │ ├── index.js │ │ └── keep-alive.js │ ├── config.js │ ├── global-api │ │ ├── assets.js │ │ ├── extend.js │ │ ├── index.js │ │ ├── mixin.js │ │ └── use.js │ ├── index.js │ ├── instance │ │ ├── events.js │ │ ├── index.js │ │ ├── init.js │ │ ├── inject.js │ │ ├── lifecycle.js │ │ ├── proxy.js │ │ ├── render-helpers │ │ │ ├── bind-object-props.js │ │ │ ├── check-keycodes.js │ │ │ ├── render-list.js │ │ │ ├── render-slot.js │ │ │ ├── render-static.js │ │ │ ├── resolve-filter.js │ │ │ └── resolve-slots.js │ │ ├── render.js │ │ └── state.js │ ├── observer │ │ ├── array.js │ │ ├── dep.js │ │ ├── index.js │ │ ├── scheduler.js │ │ └── watcher.js │ ├── util │ │ ├── debug.js │ │ ├── env.js │ │ ├── error.js │ │ ├── index.js │ │ ├── lang.js │ │ ├── options.js │ │ ├── perf.js │ │ └── props.js │ └── vdom │ │ ├── create-component.js │ │ ├── create-element.js │ │ ├── create-functional-component.js │ │ ├── helpers │ │ ├── extract-props.js │ │ ├── get-first-component-child.js │ │ ├── index.js │ │ ├── merge-hook.js │ │ ├── normalize-children.js │ │ ├── resolve-async-component.js │ │ └── update-listeners.js │ │ ├── modules │ │ ├── directives.js │ │ ├── index.js │ │ └── ref.js │ │ ├── patch.js │ │ └── vnode.js ├── platforms │ ├── vue-native │ │ ├── compiler.js │ │ ├── compiler │ │ │ ├── codegen │ │ │ │ ├── BaseGenerator.js │ │ │ │ ├── NativeRenderGenerator.js │ │ │ │ ├── RenderGenerator.js │ │ │ │ ├── WebRenderGenerator.js │ │ │ │ └── index.js │ │ │ ├── config.js │ │ │ ├── constants.js │ │ │ ├── directives │ │ │ │ ├── html.js │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ └── text.js │ │ │ ├── helpers.js │ │ │ ├── index.js │ │ │ ├── modules │ │ │ │ ├── events.js │ │ │ │ └── style.js │ │ │ ├── native.js │ │ │ ├── parser │ │ │ │ ├── filter-parser.js │ │ │ │ └── text-parser.js │ │ │ ├── property │ │ │ │ ├── ARIADOMPropertyConfig.js │ │ │ │ ├── EventConstant.js │ │ │ │ ├── HTMLDOMPropertyConfig.js │ │ │ │ ├── ReactProps.js │ │ │ │ ├── SVGDOMPropertyConfig.js │ │ │ │ └── index.js │ │ │ ├── util │ │ │ │ ├── attrs.js │ │ │ │ ├── element.js │ │ │ │ └── index.js │ │ │ └── web.js │ │ ├── index.js │ │ ├── observer.js │ │ ├── runtime │ │ │ ├── components │ │ │ │ ├── buildComponent.js │ │ │ │ ├── buildDirective.js │ │ │ │ ├── buildInputComponent.js │ │ │ │ ├── buildMixin.js │ │ │ │ ├── buildNativeComponent.js │ │ │ │ ├── buildWebEmptyComponent.js │ │ │ │ ├── buildWebInputComponent.js │ │ │ │ ├── buildWebTransition.js │ │ │ │ ├── index.js │ │ │ │ └── util.js │ │ │ ├── directives │ │ │ │ ├── index.js │ │ │ │ └── model.js │ │ │ ├── helpers.js │ │ │ ├── index.js │ │ │ ├── lifeCycle.js │ │ │ ├── render-helpers │ │ │ │ ├── bindNativeClass.js │ │ │ │ ├── bindNativeStyle.js │ │ │ │ ├── bindWebClass.js │ │ │ │ ├── bindWebStyle.js │ │ │ │ ├── checkKeyCodes.js │ │ │ │ ├── directive.js │ │ │ │ ├── dynamicComponent.js │ │ │ │ ├── event.js │ │ │ │ ├── handleProps.js │ │ │ │ ├── index.js │ │ │ │ ├── mergeCssModule.js │ │ │ │ ├── mergeNativeStyleAndNativeClass.js │ │ │ │ ├── mergeProps.js │ │ │ │ ├── renderList.js │ │ │ │ ├── renderSlot.js │ │ │ │ ├── resolveFilter.js │ │ │ │ ├── template.js │ │ │ │ ├── transitionGroupWeb.js │ │ │ │ └── transitionWeb.js │ │ │ └── render.js │ │ └── scripts │ │ │ ├── compiler.js │ │ │ ├── index.js │ │ │ ├── transformerPlugin.js │ │ │ └── util │ │ │ ├── addvm.js │ │ │ ├── constants.js │ │ │ ├── parseCss.js │ │ │ └── parseTransform.js │ └── web │ │ ├── compiler.js │ │ ├── compiler │ │ ├── directives │ │ │ ├── html.js │ │ │ ├── index.js │ │ │ ├── model.js │ │ │ └── text.js │ │ ├── index.js │ │ ├── modules │ │ │ ├── class.js │ │ │ ├── index.js │ │ │ └── style.js │ │ └── util.js │ │ ├── runtime-with-compiler.js │ │ ├── runtime.js │ │ ├── runtime │ │ ├── class-util.js │ │ ├── components │ │ │ ├── index.js │ │ │ ├── transition-group.js │ │ │ └── transition.js │ │ ├── directives │ │ │ ├── index.js │ │ │ ├── model.js │ │ │ └── show.js │ │ ├── index.js │ │ ├── modules │ │ │ ├── attrs.js │ │ │ ├── class.js │ │ │ ├── dom-props.js │ │ │ ├── events.js │ │ │ ├── index.js │ │ │ ├── style.js │ │ │ └── transition.js │ │ ├── node-ops.js │ │ ├── patch.js │ │ └── transition-util.js │ │ └── util │ │ ├── attrs.js │ │ ├── class.js │ │ ├── compat.js │ │ ├── element.js │ │ ├── index.js │ │ └── style.js ├── sfc │ └── parser.js └── shared │ ├── constants.js │ └── util.js └── types ├── index.d.ts ├── options.d.ts ├── plugin.d.ts ├── test ├── augmentation-test.ts ├── options-test.ts ├── plugin-test.ts ├── tsconfig.json └── vue-test.ts ├── typings.json ├── vnode.d.ts └── vue.d.ts /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "presets": [ 5 | ["@babel/preset-env", { 6 | "targets": {"node": "current"}, 7 | "modules": "commonjs" 8 | }], 9 | "@babel/preset-flow" 10 | ] 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | flow 2 | dist 3 | packages 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parserOptions": { 4 | "parser": "babel-eslint", 5 | "ecmaVersion": 2018, 6 | "sourceType": "module" 7 | }, 8 | "env": { 9 | "es6": true, 10 | "node": true, 11 | "browser": true 12 | }, 13 | "plugins": [ 14 | "flowtype", 15 | "prettier" 16 | ], 17 | "extends": [ 18 | "eslint:recommended", 19 | "plugin:flowtype/recommended", 20 | "plugin:prettier/recommended" 21 | ], 22 | "rules": { 23 | "prettier/prettier": ["error"], 24 | "no-console": 0, 25 | "no-useless-escape": 0, 26 | "no-empty": 0, 27 | "semi": ["error", "never"], 28 | "comma-dangle": ["error", "always-multiline"] 29 | }, 30 | "globals": { 31 | "__WEEX__": true 32 | } 33 | } -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/__tests__/.* 3 | .*/node_modules/.* 4 | .*/examples/.* 5 | .*/packages/.* 6 | .*/scripts/.* 7 | 8 | [include] 9 | 10 | [libs] 11 | flow 12 | 13 | [options] 14 | unsafe.enable_getters_and_setters=true 15 | module.name_mapper='^compiler/\(.*\)#39; -> '<PROJECT_ROOT>/src/compiler/\1' 16 | module.name_mapper='^core/\(.*\)#39; -> '<PROJECT_ROOT>/src/core/\1' 17 | module.name_mapper='^shared/\(.*\)#39; -> '<PROJECT_ROOT>/src/shared/\1' 18 | module.name_mapper='^web/\(.*\)#39; -> '<PROJECT_ROOT>/src/platforms/web/\1' 19 | module.name_mapper='^weex/\(.*\)#39; -> '<PROJECT_ROOT>/src/platforms/weex/\1' 20 | module.name_mapper='^vue-native/\(.*\)#39; -> '<PROJECT_ROOT>/src/platforms/vue-native/\1' 21 | module.name_mapper='^server/\(.*\)#39; -> '<PROJECT_ROOT>/src/server/\1' 22 | module.name_mapper='^entries/\(.*\)#39; -> '<PROJECT_ROOT>/src/entries/\1' 23 | module.name_mapper='^sfc/\(.*\)#39; -> '<PROJECT_ROOT>/src/sfc/\1' 24 | suppress_comment= \\(.\\|\n\\)*\\$flow-disable-line 25 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: vue-native-core 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | custom: # Replace with a single custom sponsorship URL 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41E Bug report" 3 | about: Spotted a bug in Vue Native? 4 | title: '' 5 | labels: '' 6 | 7 | --- 8 | 9 | **Description of the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 1. Go to '...' 15 | 2. Click on '....' 16 | 3. Scroll down to '....' 17 | 4. See error 18 | 19 | **What I expected** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Did I use `vue-native-cli` to create the project?** 26 | Yes/No 27 | 28 | **Am I using Expo?** 29 | Yes/No 30 | - If you created your project with `vue-native init <projectName>` then yes 31 | - If you used `vue-native init <projectName> --no-expo`, then no 32 | 33 | **Development platform (please complete the following information):** 34 | - OS: [e.g. iOS] 35 | - Shell/terminal: [e.g. ZSH, PowerShell] 36 | 37 | **The device on which I run my Vue Native app** 38 | - Device: [e.g. iPhone6] 39 | - OS: [e.g. iOS8.1] 40 | 41 | **Additional context** 42 | Add any other context about the problem here. 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---issue-with-the-documentation-or-website.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4D6 Issue with the documentation or website" 3 | about: Spotted something incorrect or outdated in the docs? Website not rendering correctly? 4 | title: '' 5 | labels: '' 6 | 7 | --- 8 | 9 | This repository only hosts the core packages of Vue Native. 10 | 11 | If you have an issue with the docs or website, please create an issue in the [Vue Native Website repository](https://github.com/GeekyAnts/vue-native-website/issues). 12 | 13 | Issues opened here that are related to the documentation/website will be closed. 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---issue-with-vue-native-router.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F9ED Issue with Vue Native Router" 3 | about: Having trouble with Vue Native Router? 4 | title: '' 5 | labels: '' 6 | 7 | --- 8 | 9 | This repository only hosts the core packages of Vue Native. 10 | 11 | If you have an issue with Vue Native Router, please create an issue in the [Vue Native Router repository](https://github.com/GeekyAnts/vue-native-router/issues). 12 | 13 | Issues opened here that are related to Vue Native Router will be closed. 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---questions-or-help-with-usage.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F9D0 Questions or help with usage" 3 | about: Need help with code or Vue Native usage? 4 | title: 'Question: ' 5 | labels: '' 6 | 7 | --- 8 | 9 | The best place to find answers is the [documentation](https://www.vue-native.io/docs/installation.html) 📖 10 | 11 | If you still haven't found what you are looking for or need more help, feel free to reach out on [Vue Native Slack](http://slack.vue-native.io/) 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | .idea 4 | 5 | node_modules 6 | 7 | *.log 8 | 9 | dist/*.gz 10 | dist/*.map 11 | dist/vue.common.min.js 12 | 13 | coverage 14 | 15 | packages/**/build.js 16 | packages/**/package-lock.json 17 | packages/**/.watchmanconfig 18 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel", 3 | "semi": false, 4 | "trailingComma": "all", 5 | "singleQuote": true 6 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## [ 0.0.1 ] - 2018-08-01 5 | ### :zap: Improvements 6 | - Support for v-model in switch. 7 | - `render-prop` and `render-prop-fn` as API to provide a way to pass JSX as props (inline jsx props) or callback which returns JSX thus avoiding the need to use JSX for components such as Flat List. 8 | - Named Slots support and Basic scoped slots support. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-present, Yuxi (Evan) You 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /__tests__/unit/features/instance/init.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue-native/index' 2 | 3 | describe('Initialization', () => { 4 | function noop () {} 5 | 6 | let asserted 7 | 8 | function createCompareFn (spy) { 9 | const hasWarned = msg => { 10 | var count = spy.calls.count() 11 | var args 12 | while (count--) { 13 | args = spy.calls.argsFor(count) 14 | if (args.some(containsMsg)) { 15 | return true 16 | } 17 | } 18 | 19 | function containsMsg (arg) { 20 | return arg.toString().indexOf(msg) > -1 21 | } 22 | } 23 | 24 | return { 25 | compare: msg => { 26 | asserted = asserted.concat(msg) 27 | var warned = Array.isArray(msg) 28 | ? msg.some(hasWarned) 29 | : hasWarned(msg) 30 | return { 31 | pass: warned, 32 | message: warned 33 | ? 'Expected message "' + msg + '" not to have been warned' 34 | : 'Expected message "' + msg + '" to have been warned' 35 | } 36 | } 37 | } 38 | } 39 | 40 | // define custom matcher for warnings 41 | beforeEach(() => { 42 | asserted = [] 43 | spyOn(console, 'warn') 44 | spyOn(console, 'error') 45 | jasmine.addMatchers({ 46 | toHaveBeenWarned: () => createCompareFn(console.error), 47 | toHaveBeenTipped: () => createCompareFn(console.warn) 48 | }) 49 | }) 50 | 51 | afterEach(done => { 52 | const warned = msg => asserted.some(assertedMsg => msg.toString().indexOf(assertedMsg) > -1) 53 | let count = console.error.calls.count() 54 | let args 55 | while (count--) { 56 | args = console.error.calls.argsFor(count) 57 | if (!warned(args[0])) { 58 | done.fail(`Unexpected console.error message: ${args[0]}`) 59 | return 60 | } 61 | } 62 | done() 63 | }) 64 | 65 | it('without new', () => { 66 | try { Vue() } catch (e) {} 67 | expect('Vue is a constructor and should be called with the `new` keyword').toHaveBeenWarned() 68 | }) 69 | 70 | it('with new', () => { 71 | expect(new Vue() instanceof Vue).toBe(true) 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /flow/global-api.js: -------------------------------------------------------------------------------- 1 | declare interface GlobalAPI { 2 | cid: number; 3 | options: Object; 4 | config: Config; 5 | util: Object; 6 | 7 | extend: (options: Object) => Function; 8 | set: <T>(target: Object | Array<T>, key: string | number, value: T) => T; 9 | delete: <T>(target: Object| Array<T>, key: string | number) => void; 10 | nextTick: (fn: Function, context?: Object) => void; 11 | use: (plugin: Function | Object) => void; 12 | mixin: (mixin: Object) => void; 13 | compile: (template: string) => { render: Function, staticRenderFns: Array<Function> }; 14 | 15 | directive: (id: string, def?: Function | Object) => Function | Object | void; 16 | component: (id: string, def?: Class<Component> | Object) => Class<Component>; 17 | filter: (id: string, def?: Function) => Function | void; 18 | 19 | // allow dynamic method registration 20 | [key: string]: any 21 | } 22 | -------------------------------------------------------------------------------- /flow/modules.js: -------------------------------------------------------------------------------- 1 | declare module 'he' { 2 | declare function escape(html: string): string; 3 | declare function decode(html: string): string; 4 | } 5 | 6 | declare module 'source-map' { 7 | declare class SourceMapGenerator { 8 | setSourceContent(filename: string, content: string): void; 9 | addMapping(mapping: Object): void; 10 | toString(): string; 11 | } 12 | declare class SourceMapConsumer { 13 | originalPositionFor(position: { line: number; column: number; }): { 14 | source: ?string; 15 | line: ?number; 16 | column: ?number; 17 | }; 18 | } 19 | } 20 | 21 | declare module 'lru-cache' { 22 | declare var exports: { 23 | (): any 24 | } 25 | } 26 | 27 | declare module 'de-indent' { 28 | declare var exports: { 29 | (input: string): string 30 | } 31 | } 32 | 33 | declare module 'serialize-javascript' { 34 | declare var exports: { 35 | (input: string, options: { isJSON: boolean }): string 36 | } 37 | } 38 | 39 | declare module 'lodash.template' { 40 | declare var exports: { 41 | (input: string, options: { interpolate: RegExp, escape: RegExp }): Function 42 | } 43 | } 44 | 45 | declare module 'change-case' { 46 | declare var exports: { 47 | (): any 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /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<VNode>; 8 | _componentTag: ?string; 9 | _parentElm: ?Node; 10 | _refElm: ?Node; 11 | render?: Function; 12 | staticRenderFns?: Array<Function> 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<Component> }; 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<string>; 58 | 59 | // component v-model customization 60 | model?: { 61 | prop?: string; 62 | event?: string; 63 | }; 64 | 65 | // misc 66 | parent?: Component; 67 | mixins?: Array<Object>; 68 | name?: string; 69 | extends?: Class<Component> | Object; 70 | delimiters?: [string, string]; 71 | 72 | // private 73 | _isComponent?: true; 74 | _propKeys?: Array<string>; 75 | _parentVnode?: VNode; 76 | _parentListeners?: ?Object; 77 | _renderChildren?: ?Array<VNode>; 78 | _componentTag: ?string; 79 | _scopeId: ?string; 80 | _base: Class<Component>; 81 | _parentElm: ?Node; 82 | _refElm: ?Node; 83 | } 84 | 85 | declare type PropOptions = { 86 | type: Function | Array<Function> | null; 87 | default: any; 88 | required: ?boolean; 89 | validator: ?Function; 90 | } 91 | -------------------------------------------------------------------------------- /flow/ssr.js: -------------------------------------------------------------------------------- 1 | declare type ComponentWithCacheContext = { 2 | type: 'ComponentWithCache'; 3 | bufferIndex: number; 4 | buffer: Array<string>; 5 | key: string; 6 | } 7 | 8 | declare type ElementContext = { 9 | type: 'Element'; 10 | children: Array<VNode>; 11 | rendered: number; 12 | endTag: string; 13 | total: number; 14 | } 15 | 16 | declare type ComponentContext = { 17 | type: 'Component'; 18 | prevActive: Component; 19 | } 20 | 21 | declare type RenderState = ComponentContext | ComponentWithCacheContext | ElementContext 22 | -------------------------------------------------------------------------------- /flow/vnode.js: -------------------------------------------------------------------------------- 1 | declare type VNodeChildren = Array<?VNode | string | VNodeChildren> | string 2 | 3 | declare type VNodeComponentOptions = { 4 | Ctor: Class<Component>; 5 | propsData: ?Object; 6 | listeners: ?Object; 7 | children: ?Array<VNode>; 8 | tag?: string; 9 | } 10 | 11 | declare type MountedComponentVNode = { 12 | context: Component; 13 | componentOptions: VNodeComponentOptions; 14 | componentInstance: Component; 15 | parent: VNode; 16 | data: VNodeData; 17 | } 18 | 19 | // interface for vnodes in update modules 20 | declare type VNodeWithData = { 21 | tag: string; 22 | data: VNodeData; 23 | children: ?Array<VNode>; 24 | text: void; 25 | elm: any; 26 | ns: string | void; 27 | context: Component; 28 | key: string | number | void; 29 | parent?: VNodeWithData; 30 | componentInstance?: Component; 31 | isRootInsert: boolean; 32 | } 33 | 34 | declare interface VNodeData { 35 | key?: string | number; 36 | slot?: string; 37 | ref?: string; 38 | pre?: boolean; 39 | tag?: string; 40 | staticClass?: string; 41 | class?: any; 42 | staticStyle?: { [key: string]: any }; 43 | style?: Array<Object> | Object; 44 | normalizedStyle?: Object; 45 | props?: { [key: string]: any }; 46 | attrs?: { [key: string]: string }; 47 | domProps?: { [key: string]: any }; 48 | hook?: { [key: string]: Function }; 49 | on?: ?{ [key: string]: Function | Array<Function> }; 50 | nativeOn?: { [key: string]: Function | Array<Function> }; 51 | transition?: Object; 52 | show?: boolean; // marker for v-show 53 | inlineTemplate?: { 54 | render: Function; 55 | staticRenderFns: Array<Function>; 56 | }; 57 | directives?: Array<VNodeDirective>; 58 | keepAlive?: boolean; 59 | scopedSlots?: { [key: string]: Function }; 60 | model?: { 61 | value: any; 62 | callback: Function; 63 | }; 64 | } 65 | 66 | declare type VNodeDirective = { 67 | name: string; 68 | rawName: string; 69 | value?: any; 70 | oldValue?: any; 71 | arg?: string; 72 | modifiers?: ASTModifiers; 73 | def?: Object; 74 | } 75 | -------------------------------------------------------------------------------- /jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": true, 3 | "testMatch": [ 4 | "<rootDir>/**/__tests__/**/*.(test|spec).js" 5 | ], 6 | "moduleDirectories": [ 7 | "node_modules" 8 | ], 9 | "moduleNameMapper": { 10 | "^vue-native(.*)quot;: "<rootDir>/src/platforms/vue-native$1", 11 | "^vue(.*)quot;: "<rootDir>/src/platforms/web/runtime-with-compiler$1", 12 | "^compiler(.*)quot;: "<rootDir>/src/compiler$1", 13 | "^core(.*)quot;: "<rootDir>/src/core$1", 14 | "^shared(.*)quot;: "<rootDir>/src/shared$1", 15 | "^web(.*)quot;: "<rootDir>/src/platforms/web$1", 16 | "^sfc(.*)quot;: "<rootDir>/src/sfc$1" 17 | }, 18 | "collectCoverage": true, 19 | "collectCoverageFrom": [ 20 | "<rootDir>/src/platforms/vue-native/**/*.js", 21 | "!**/node_modules/**" 22 | ], 23 | "coverageDirectory": "<rootDir>/coverage", 24 | "testURL": "http://localhost" 25 | } -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "baseUrl": "src", 5 | "paths": { 6 | "vue/*": ["platforms/web/runtime-with-compiler/*"], 7 | "compiler/*": ["compiler/*"], 8 | "core/*": ["core/*"], 9 | "shared/*": ["shared/*"], 10 | "web/*": ["platforms/web/*"], 11 | "vue-native/*": ["platforms/vue-native/*"], 12 | "sfc/*": ["sfc/*"] 13 | } 14 | }, 15 | "exclude": ["node_modules", "packages"] 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-native-core", 3 | "version": "0.3.1", 4 | "description": "Create mobile apps using vuejs", 5 | "main": "packages/vue-native-core/index.js", 6 | "typings": "types/index.d.ts", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/GeekyAnts/vue-native-core.git" 10 | }, 11 | "keywords": [ 12 | "vue", 13 | "native", 14 | "mobile" 15 | ], 16 | "engines": { 17 | "node": "^4.5 || >=5.10" 18 | }, 19 | "engineStrict": true, 20 | "author": "Geekyants (https://geekyants.io/)", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/GeekyAnts/vue-native-core/issues" 24 | }, 25 | "files": [ 26 | "src", 27 | "packages", 28 | "types/*.d.ts" 29 | ], 30 | "scripts": { 31 | "lint": "eslint src scripts", 32 | "lint:fix": "eslint --fix src scripts", 33 | "flow": "flow check", 34 | "test:unit": "jest --config ./jest.config.json", 35 | "test": "npm run lint && npm run flow && npm run test:unit", 36 | "build": "node scripts/build.js", 37 | "release": "bash scripts/release.sh" 38 | }, 39 | "husky": { 40 | "hooks": { 41 | "pre-commit": "npm run lint:fix" 42 | } 43 | }, 44 | "homepage": "https://github.com/GeekyAnts/vue-native-core", 45 | "devDependencies": { 46 | "@babel/core": "^7.5.5", 47 | "@babel/preset-env": "^7.5.5", 48 | "@babel/preset-flow": "^7.0.0", 49 | "babel-core": "^6.9.0", 50 | "babel-eslint": "^10.0.3", 51 | "babel-helper-vue-jsx-merge-props": "^2.0.2", 52 | "babel-jest": "^26.0.1", 53 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 54 | "babel-plugin-syntax-jsx": "^6.18.0", 55 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 56 | "babel-plugin-transform-vue-jsx": "^3.2.0", 57 | "babel-preset-flow-vue": "^1.0.0", 58 | "babel-preset-react": "^6.24.1", 59 | "buble": "^0.19.8", 60 | "change-case": "^3.0.1", 61 | "cross-spawn": "^5.0.1", 62 | "de-indent": "^1.0.2", 63 | "eslint": "^6.5.1", 64 | "eslint-config-prettier": "^6.5.0", 65 | "eslint-plugin-flowtype": "^4.3.0", 66 | "eslint-plugin-prettier": "^3.1.1", 67 | "flow-bin": "^0.39.0", 68 | "hash-sum": "^1.0.2", 69 | "he": "^1.1.0", 70 | "husky": "^3.0.8", 71 | "jasmine": "^2.5.2", 72 | "jasmine-core": "^2.5.2", 73 | "jest": "^26.0.1", 74 | "js-beautify": "^1.6.14", 75 | "lodash": "^4.17.20", 76 | "lodash.template": "^4.4.0", 77 | "lru-cache": "^4.0.2", 78 | "prettier": "^1.18.2", 79 | "react": "16.8.6", 80 | "resolve": "^1.2.0", 81 | "rollup": "^1.17.0", 82 | "rollup-plugin-alias": "^1.5.2", 83 | "rollup-plugin-buble": "^0.19.8", 84 | "rollup-plugin-flow-no-whitespace": "^1.0.0", 85 | "rollup-plugin-prettier": "^0.6.0", 86 | "rollup-plugin-replace": "^2.2.0", 87 | "serialize-javascript": "^3.1.0", 88 | "typescript": "^2.1.6", 89 | "uglify-js": "^3.6.0" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /packages/vue-native-core/README.md: -------------------------------------------------------------------------------- 1 | # vue-native-core 2 | 3 | > This package is auto-generated. For pull requests please work with [src/platforms/vue-native/index.js](https://github.com/GeekyAnts/vue-native-core/tree/develop/src/platforms/vue-native). 4 | 5 | Find the `vue-native-core` repository [here](https://github.com/GeekyAnts/vue-native-core). 6 | 7 | For the official documentation, visit [this](https://vue-native.io/docs/installation.html) website. 8 | -------------------------------------------------------------------------------- /packages/vue-native-core/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build') -------------------------------------------------------------------------------- /packages/vue-native-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-native-core", 3 | "version": "0.3.1", 4 | "description": "Library with core functionalities to create Vue Native components", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/GeekyAnts/vue-native-core" 9 | }, 10 | "keywords": [ 11 | "react", 12 | "vue" 13 | ], 14 | "author": "Geekyants (https://geekyants.io/)", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/GeekyAnts/vue-native-core/issues" 18 | }, 19 | "homepage": "https://github.com/GeekyAnts/vue-native-core/tree/master/packages/vue-native-core", 20 | "peerDependencies": { 21 | "react": "^16.3.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/vue-native-helper/README.md: -------------------------------------------------------------------------------- 1 | # vue-native-helper 2 | 3 | > This package is auto-generated. For pull requests please work with [src/platforms/vue-native/runtime/helpers.js](https://github.com/GeekyAnts/vue-native-core/tree/develop/src/platforms/vue-native/runtime). 4 | 5 | Find the `vue-native-core` repository [here](https://github.com/GeekyAnts/vue-native-core). 6 | 7 | For the official documentation, visit [this](https://vue-native.io/docs/installation.html) website. 8 | -------------------------------------------------------------------------------- /packages/vue-native-helper/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build') -------------------------------------------------------------------------------- /packages/vue-native-helper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-native-helper", 3 | "version": "0.3.1", 4 | "description": "Helper library with utilities for vue-native-core", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/GeekyAnts/vue-native-core" 9 | }, 10 | "keywords": [ 11 | "react", 12 | "vue", 13 | "helper" 14 | ], 15 | "author": "Geekyants (https://geekyants.io/)", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/GeekyAnts/vue-native-core/issues" 19 | }, 20 | "homepage": "https://github.com/GeekyAnts/vue-native-core#readme", 21 | "peerDependencies": { 22 | "react": "^16.3.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/vue-native-scripts/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | test/output 4 | docs/_book 5 | .DS_Store 6 | .idea 7 | *.iml 8 | -------------------------------------------------------------------------------- /packages/vue-native-scripts/bin/vue-native-script.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const spawn = require('cross-spawn'); 4 | const script = process.argv[2]; 5 | const args = process.argv.slice(3); 6 | const validCommands = ['compiler']; 7 | 8 | if(validCommands.indexOf(script) !== -1) { 9 | const result = spawn.sync( 10 | 'node', 11 | ['--no-deprecation', require.resolve('../scripts/' + script + '.js')].concat(args), 12 | { stdio: 'inherit' } 13 | ); 14 | process.exit(result.status); 15 | } else { 16 | console.log( 17 | `Invalid command '${script}'. Please check if you need to update react-native-scripts.` 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/vue-native-scripts/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build.js') -------------------------------------------------------------------------------- /packages/vue-native-scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-native-scripts", 3 | "version": "0.3.1", 4 | "description": "Compile Vue Native components to React Native", 5 | "main": "index.js", 6 | "scripts": {}, 7 | "bin": { 8 | "vue-native-scripts": "./bin/vue-native-script.js" 9 | }, 10 | "author": "Geekyants (https://geekyants.io/)", 11 | "license": "MIT", 12 | "dependencies": { 13 | "babel-core": "^6.25.0", 14 | "babel-traverse": "^6.26.0", 15 | "cross-spawn": "^5.1.0", 16 | "css-parse": "^2.0.0", 17 | "hash-sum": "^1.0.2", 18 | "js-beautify": "^1.6.14", 19 | "line-number": "^0.1.0", 20 | "parse5": "^5.0.0", 21 | "path": "^0.12.7", 22 | "semver": "^6.2.0", 23 | "source-map": "~0.6.0", 24 | "vue-native-template-compiler": "^0.3.0" 25 | }, 26 | "peerDependencies": { 27 | "react-native": "^0.59.0", 28 | "metro": "^0.51.0", 29 | "metro-bundler": "*", 30 | "metro-react-native-babel-transformer": "^0.51.0" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/GeekyAnts/vue-native-core/issues" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/GeekyAnts/vue-native-core/tree/master/packages/vue-native-scripts" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/vue-native-template-compiler/README.md: -------------------------------------------------------------------------------- 1 | # vue-native-template-compiler 2 | 3 | > This package is auto-generated. For pull requests please work with [src/platforms/vue-native/compiler/index.js](https://github.com/GeekyAnts/vue-native-core/tree/develop/src/platforms/vue-native). 4 | 5 | Find the `vue-native-core` repository [here](https://github.com/GeekyAnts/vue-native-core). 6 | 7 | For the official documentation, visit [this](https://vue-native.io/docs/installation.html) website. 8 | -------------------------------------------------------------------------------- /packages/vue-native-template-compiler/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build') -------------------------------------------------------------------------------- /packages/vue-native-template-compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-native-template-compiler", 3 | "version": "0.3.1", 4 | "description": "Vue Native template compiler, dependency of vue-native-scripts. It can also be used as a stand-alone compiler", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/GeekyAnts/vue-native-core" 9 | }, 10 | "keywords": [ 11 | "react", 12 | "vue", 13 | "template", 14 | "compiler" 15 | ], 16 | "author": "Geekyants (https://geekyants.io/)", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/GeekyAnts/vue-native-core/issues" 20 | }, 21 | "homepage": "https://github.com/GeekyAnts/vue-native-core/tree/master/packages/vue-native-template-compiler", 22 | "dependencies": { 23 | "he": "^1.1.0", 24 | "de-indent": "^1.0.2", 25 | "change-case": "^3.0.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /scripts/.eslintrc: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "env": { 4 | "es6": true, 5 | }, 6 | "rules": { 7 | "camelcase": 0, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /scripts/alias.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | vue: path.resolve(__dirname, '../src/platforms/web/runtime-with-compiler'), 5 | compiler: path.resolve(__dirname, '../src/compiler'), 6 | core: path.resolve(__dirname, '../src/core'), 7 | shared: path.resolve(__dirname, '../src/shared'), 8 | web: path.resolve(__dirname, '../src/platforms/web'), 9 | 'vue-native': path.resolve(__dirname, '../src/platforms/vue-native'), 10 | sfc: path.resolve(__dirname, '../src/sfc'), 11 | } 12 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const zlib = require('zlib') 4 | const rollup = require('rollup') 5 | const uglify = require('uglify-js') 6 | 7 | if (!fs.existsSync('dist')) { 8 | fs.mkdirSync('dist') 9 | } 10 | 11 | let builds = require('./config').getAllBuilds() 12 | 13 | // filter builds via command line arg 14 | if (process.argv[2]) { 15 | const filters = process.argv[2].split(',') 16 | builds = builds.filter(b => { 17 | return filters.some(f => b.dest.indexOf(f) > -1) 18 | }) 19 | } 20 | 21 | build(builds) 22 | 23 | function build(builds) { 24 | let built = 0 25 | const total = builds.length 26 | const next = () => { 27 | buildEntry(builds[built]) 28 | .then(() => { 29 | built++ 30 | if (built < total) { 31 | next() 32 | } 33 | }) 34 | .catch(logError) 35 | } 36 | 37 | next() 38 | } 39 | 40 | function buildEntry(config) { 41 | const isProd = /min\.js$/.test(config.dest) 42 | const output = config.output 43 | const { file, banner } = output 44 | return rollup 45 | .rollup(config) 46 | .then(bundle => bundle.generate(output)) 47 | .then(({ output: [{ code }] }) => { 48 | if (isProd) { 49 | const minified = 50 | (banner ? banner + '\n' : '') + 51 | uglify.minify(code, { 52 | fromString: true, 53 | output: { 54 | screw_ie8: true, 55 | ascii_only: true, 56 | }, 57 | compress: { 58 | pure_funcs: ['makeMap'], 59 | }, 60 | }).code 61 | return write(file, minified, true) 62 | } else { 63 | return write(file, code) 64 | } 65 | }) 66 | } 67 | 68 | function write(dest, code, zip) { 69 | return new Promise((resolve, reject) => { 70 | function report(extra) { 71 | console.log( 72 | blue(path.relative(process.cwd(), dest)) + 73 | ' ' + 74 | getSize(code) + 75 | (extra || ''), 76 | ) 77 | resolve() 78 | } 79 | 80 | fs.writeFile(dest, code, err => { 81 | if (err) return reject(err) 82 | if (zip) { 83 | zlib.gzip(code, (err, zipped) => { 84 | if (err) return reject(err) 85 | report(' (gzipped: ' + getSize(zipped) + ')') 86 | }) 87 | } else { 88 | report() 89 | } 90 | }) 91 | }) 92 | } 93 | 94 | function getSize(code) { 95 | return (code.length / 1024).toFixed(2) + 'kb' 96 | } 97 | 98 | function logError(e) { 99 | console.log(e) 100 | } 101 | 102 | function blue(str) { 103 | return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m' 104 | } 105 | -------------------------------------------------------------------------------- /scripts/ci.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | npm test 3 | 4 | # report coverage stats for non-PRs 5 | if [[ -z $CI_PULL_REQUEST ]]; then 6 | cat ./coverage/lcov.info | ./node_modules/.bin/codecov 7 | fi 8 | -------------------------------------------------------------------------------- /scripts/config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const buble = require('rollup-plugin-buble') 3 | const alias = require('rollup-plugin-alias') 4 | const replace = require('rollup-plugin-replace') 5 | const flow = require('rollup-plugin-flow-no-whitespace') 6 | const prettier = require('rollup-plugin-prettier') 7 | 8 | const version = '2.2.6' 9 | 10 | // const banner = 11 | // "/*!\n" + 12 | // " * Vue Native v" + 13 | // version + 14 | // "\n" + 15 | // " * (c) 2018-" + 16 | // new Date().getFullYear() + 17 | // " GeekyAnts Software Pvt. Ltd.\n" + 18 | // " * Released under the MIT License.\n" + 19 | // " */" 20 | 21 | const aliases = require('./alias') 22 | const resolve = p => { 23 | const base = p.split('/')[0] 24 | if (aliases[base]) { 25 | return path.resolve(aliases[base], p.slice(base.length + 1)) 26 | } else { 27 | return path.resolve(__dirname, '../', p) 28 | } 29 | } 30 | 31 | const builds = { 32 | 'vue-native-core': { 33 | entry: resolve('vue-native/index.js'), 34 | dest: resolve('packages/vue-native-core/build.js'), 35 | format: 'cjs', 36 | external: ['react'], 37 | }, 38 | 'vue-native-helper': { 39 | entry: resolve('vue-native/runtime/helpers.js'), 40 | dest: resolve('packages/vue-native-helper/build.js'), 41 | format: 'cjs', 42 | }, 43 | 'vue-native-scripts': { 44 | entry: resolve('vue-native/scripts/index.js'), 45 | dest: resolve('packages/vue-native-scripts/build.js'), 46 | format: 'cjs', 47 | external: [] 48 | .concat( 49 | Object.keys( 50 | require('../packages/vue-native-scripts/package.json').dependencies, 51 | ), 52 | ) 53 | .concat( 54 | Object.keys( 55 | require('../packages/vue-native-scripts/package.json') 56 | .peerDependencies, 57 | ), 58 | ), 59 | }, 60 | 'vue-native-template-compiler': { 61 | entry: resolve('vue-native/compiler.js'), 62 | dest: resolve('packages/vue-native-template-compiler/build.js'), 63 | format: 'cjs', 64 | external: ['change-case', 'he', 'de-indent', 'lodash'], 65 | }, 66 | } 67 | 68 | function genConfig(opts) { 69 | const config = { 70 | input: opts.entry, 71 | output: { 72 | file: opts.dest, 73 | format: opts.format, 74 | banner: opts.banner, 75 | name: 'Vue', 76 | }, 77 | external: opts.external, 78 | plugins: [ 79 | replace({ 80 | __VERSION__: version, 81 | }), 82 | flow(), 83 | buble(), 84 | alias(Object.assign({}, aliases, opts.alias)), 85 | prettier(), 86 | ].concat(opts.plugins || []), 87 | onwarn: (msg, warn) => { 88 | if (!/Circular/.test(msg)) { 89 | warn(msg) 90 | } 91 | }, 92 | } 93 | 94 | if (opts.env) { 95 | config.plugins.push( 96 | replace({ 97 | 'process.env.NODE_ENV': JSON.stringify(opts.env), 98 | }), 99 | ) 100 | } 101 | 102 | return config 103 | } 104 | 105 | if (process.env.TARGET) { 106 | module.exports = genConfig(builds[process.env.TARGET]) 107 | } else { 108 | exports.getBuild = name => genConfig(builds[name]) 109 | exports.getAllBuilds = () => 110 | Object.keys(builds).map(name => genConfig(builds[name])) 111 | } 112 | -------------------------------------------------------------------------------- /scripts/git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | files_to_lint=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js#39;) 4 | 5 | if [ -n "$files_to_lint" ]; then 6 | NODE_ENV=production eslint --quiet $files_to_lint 7 | fi 8 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | CURRENT_BRANCH=$(git branch --show-current) 4 | if [ $CURRENT_BRANCH != "master" ]; then 5 | echo "This script can only be run in the master branch. Exiting..." 6 | exit 1 7 | fi 8 | 9 | # get the version number from the user 10 | read -e -p "Enter the new Vue Native version: " VERSION 11 | if [[ -z $VERSION ]]; then 12 | echo "No version entered. Exiting..." 13 | exit 0 14 | fi 15 | 16 | # Decide the NPM tag. 17 | # Tag should be set to 'next' for pre-release versions 18 | # and will be 'latest' by default 19 | read -p "Is this a pre-release version? (y/n) " -n 1 -r 20 | echo 21 | if [[ $REPLY =~ ^[Yy]$ ]]; then 22 | echo "Using npm tag 'next' for this release." 23 | TAG='next' 24 | else 25 | echo "Using npm tag 'latest' for this release." 26 | TAG='latest' 27 | fi 28 | 29 | read -p "Releasing $VERSION with npm tag $TAG - are you sure? (y/n) " -n 1 -r 30 | echo 31 | if [[ $REPLY =~ ^[Yy]$ ]]; then 32 | echo "Releasing $VERSION (with npm tag $TAG)..." 33 | 34 | # bump package versions 35 | # packages: 36 | # - vue-native-core 37 | # - vue-native-helper 38 | # - vue-native-scripts 39 | # - vue-native-template-compiler 40 | 41 | cd packages/vue-native-core 42 | npm version $VERSION 43 | cd - 44 | 45 | cd packages/vue-native-helper 46 | npm version $VERSION 47 | cd - 48 | 49 | cd packages/vue-native-scripts 50 | npm version $VERSION 51 | cd - 52 | 53 | cd packages/vue-native-template-compiler 54 | npm version $VERSION 55 | cd - 56 | 57 | # build 58 | # the build needs to be generated after the version bump 59 | # because the Vue version comes from packages/vue-native-core/package.json 60 | # refer to scripts/config.js 61 | VERSION=$VERSION npm run build 62 | 63 | # commit 64 | git add -A 65 | git commit -m "[build] $VERSION" 66 | 67 | # publish packages 68 | # vue-native-core has already been published by np 69 | # packages: 70 | # - vue-native-core 71 | # - vue-native-helper 72 | # - vue-native-scripts 73 | # - vue-native-template-compiler 74 | 75 | cd packages/vue-native-core 76 | npm publish --tag $TAG 77 | cd - 78 | 79 | cd packages/vue-native-helper 80 | npm publish --tag $TAG 81 | cd - 82 | 83 | cd packages/vue-native-scripts 84 | npm publish --tag $TAG 85 | cd - 86 | 87 | cd packages/vue-native-template-compiler 88 | npm publish --tag $TAG 89 | cd - 90 | 91 | # Update version in main package.json and commit 92 | npm version $VERSION 93 | 94 | # Push the tags and version update 95 | git push origin v$VERSION 96 | git push origin master 97 | 98 | echo "\nPublished v$VERSION!" 99 | fi 100 | -------------------------------------------------------------------------------- /src/compiler/directives/bind.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export default function bind(el: ASTElement, dir: ASTDirective) { 4 | el.wrapData = (code: string) => { 5 | return `_b(${code},'${el.tag}',${dir.value}${ 6 | dir.modifiers && dir.modifiers.prop ? ',true' : '' 7 | })` 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/compiler/directives/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import bind from './bind' 4 | import { noop } from 'shared/util' 5 | 6 | export default { 7 | bind, 8 | cloak: noop, 9 | } 10 | -------------------------------------------------------------------------------- /src/compiler/parser/entity-decoder.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | let decoder 4 | 5 | export function decode(html: string): string { 6 | decoder = decoder || document.createElement('div') 7 | decoder.innerHTML = html 8 | return decoder.textContent 9 | } 10 | -------------------------------------------------------------------------------- /src/compiler/parser/filter-parser.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | const validDivisionCharRE = /[\w).+\-_$\]]/ 4 | 5 | export function parseFilters(exp: string): string { 6 | let inSingle = false 7 | let inDouble = false 8 | let inTemplateString = false 9 | let inRegex = false 10 | let curly = 0 11 | let square = 0 12 | let paren = 0 13 | let lastFilterIndex = 0 14 | let c, prev, i, expression, filters 15 | 16 | for (i = 0; i < exp.length; i++) { 17 | prev = c 18 | c = exp.charCodeAt(i) 19 | if (inSingle) { 20 | if (c === 0x27 && prev !== 0x5c) inSingle = false 21 | } else if (inDouble) { 22 | if (c === 0x22 && prev !== 0x5c) inDouble = false 23 | } else if (inTemplateString) { 24 | if (c === 0x60 && prev !== 0x5c) inTemplateString = false 25 | } else if (inRegex) { 26 | if (c === 0x2f && prev !== 0x5c) inRegex = false 27 | } else if ( 28 | c === 0x7c && // pipe 29 | exp.charCodeAt(i + 1) !== 0x7c && 30 | exp.charCodeAt(i - 1) !== 0x7c && 31 | !curly && 32 | !square && 33 | !paren 34 | ) { 35 | if (expression === undefined) { 36 | // first filter, end of expression 37 | lastFilterIndex = i + 1 38 | expression = exp.slice(0, i).trim() 39 | } else { 40 | pushFilter() 41 | } 42 | } else { 43 | switch (c) { 44 | case 0x22: 45 | inDouble = true 46 | break // " 47 | case 0x27: 48 | inSingle = true 49 | break // ' 50 | case 0x60: 51 | inTemplateString = true 52 | break // ` 53 | case 0x28: 54 | paren++ 55 | break // ( 56 | case 0x29: 57 | paren-- 58 | break // ) 59 | case 0x5b: 60 | square++ 61 | break // [ 62 | case 0x5d: 63 | square-- 64 | break // ] 65 | case 0x7b: 66 | curly++ 67 | break // { 68 | case 0x7d: 69 | curly-- 70 | break // } 71 | } 72 | if (c === 0x2f) { 73 | // / 74 | let j = i - 1 75 | let p 76 | // find first non-whitespace prev char 77 | for (; j >= 0; j--) { 78 | p = exp.charAt(j) 79 | if (p !== ' ') break 80 | } 81 | if (!p || !validDivisionCharRE.test(p)) { 82 | inRegex = true 83 | } 84 | } 85 | } 86 | } 87 | 88 | if (expression === undefined) { 89 | expression = exp.slice(0, i).trim() 90 | } else if (lastFilterIndex !== 0) { 91 | pushFilter() 92 | } 93 | 94 | function pushFilter() { 95 | ;(filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim()) 96 | lastFilterIndex = i + 1 97 | } 98 | 99 | if (filters) { 100 | for (i = 0; i < filters.length; i++) { 101 | expression = wrapFilter(expression, filters[i]) 102 | } 103 | } 104 | 105 | return expression 106 | } 107 | 108 | function wrapFilter(exp: string, filter: string): string { 109 | const i = filter.indexOf('(') 110 | if (i < 0) { 111 | // _f: resolveFilter 112 | return `_f("${filter}")(${exp})` 113 | } else { 114 | const name = filter.slice(0, i) 115 | const args = filter.slice(i + 1) 116 | return `_f("${name}")(${exp},${args}` 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/compiler/parser/text-parser.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { cached } from 'shared/util' 4 | import { parseFilters } from './filter-parser' 5 | 6 | const defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g 7 | const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g 8 | 9 | const buildRegex = cached(delimiters => { 10 | const open = delimiters[0].replace(regexEscapeRE, '\\amp;') 11 | const close = delimiters[1].replace(regexEscapeRE, '\\amp;') 12 | return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') 13 | }) 14 | 15 | export function parseText( 16 | text: string, 17 | delimiters?: [string, string], 18 | ): string | void { 19 | const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE 20 | if (!tagRE.test(text)) { 21 | return 22 | } 23 | const tokens = [] 24 | let lastIndex = (tagRE.lastIndex = 0) 25 | let match, index 26 | while ((match = tagRE.exec(text))) { 27 | index = match.index 28 | // push text token 29 | if (index > lastIndex) { 30 | tokens.push(JSON.stringify(text.slice(lastIndex, index))) 31 | } 32 | // tag token 33 | const exp = parseFilters(match[1].trim()) 34 | tokens.push(`_s(${exp})`) 35 | lastIndex = index + match[0].length 36 | } 37 | if (lastIndex < text.length) { 38 | tokens.push(JSON.stringify(text.slice(lastIndex))) 39 | } 40 | return tokens.join('+') 41 | } 42 | -------------------------------------------------------------------------------- /src/core/components/index.js: -------------------------------------------------------------------------------- 1 | import KeepAlive from './keep-alive' 2 | 3 | export default { 4 | KeepAlive, 5 | } 6 | -------------------------------------------------------------------------------- /src/core/components/keep-alive.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getFirstComponentChild } from 'core/vdom/helpers/index' 4 | 5 | type VNodeCache = { [key: string]: ?VNode } 6 | 7 | const patternTypes: Array<Function> = [String, RegExp] 8 | 9 | function getComponentName(opts: ?VNodeComponentOptions): ?string { 10 | return opts && (opts.Ctor.options.name || opts.tag) 11 | } 12 | 13 | function matches(pattern: string | RegExp, name: string): boolean { 14 | if (typeof pattern === 'string') { 15 | return pattern.split(',').indexOf(name) > -1 16 | } else if (pattern instanceof RegExp) { 17 | return pattern.test(name) 18 | } 19 | /* istanbul ignore next */ 20 | return false 21 | } 22 | 23 | function pruneCache(cache: VNodeCache, current: VNode, filter: Function) { 24 | for (const key in cache) { 25 | const cachedNode: ?VNode = cache[key] 26 | if (cachedNode) { 27 | const name: ?string = getComponentName(cachedNode.componentOptions) 28 | if (name && !filter(name)) { 29 | if (cachedNode !== current) { 30 | pruneCacheEntry(cachedNode) 31 | } 32 | cache[key] = null 33 | } 34 | } 35 | } 36 | } 37 | 38 | function pruneCacheEntry(vnode: ?VNode) { 39 | if (vnode) { 40 | vnode.componentInstance.$destroy() 41 | } 42 | } 43 | 44 | export default { 45 | name: 'keep-alive', 46 | abstract: true, 47 | 48 | props: { 49 | include: patternTypes, 50 | exclude: patternTypes, 51 | }, 52 | 53 | created() { 54 | this.cache = Object.create(null) 55 | }, 56 | 57 | destroyed() { 58 | for (const key in this.cache) { 59 | pruneCacheEntry(this.cache[key]) 60 | } 61 | }, 62 | 63 | watch: { 64 | include(val: string | RegExp) { 65 | pruneCache(this.cache, this._vnode, name => matches(val, name)) 66 | }, 67 | exclude(val: string | RegExp) { 68 | pruneCache(this.cache, this._vnode, name => !matches(val, name)) 69 | }, 70 | }, 71 | 72 | render() { 73 | const vnode: VNode = getFirstComponentChild(this.$slots.default) 74 | const componentOptions: ?VNodeComponentOptions = 75 | vnode && vnode.componentOptions 76 | if (componentOptions) { 77 | // check pattern 78 | const name: ?string = getComponentName(componentOptions) 79 | if ( 80 | name && 81 | ((this.include && !matches(this.include, name)) || 82 | (this.exclude && matches(this.exclude, name))) 83 | ) { 84 | return vnode 85 | } 86 | const key: ?string = 87 | vnode.key == null 88 | ? // same constructor may get registered as different local components 89 | // so cid alone is not enough (#3269) 90 | componentOptions.Ctor.cid + 91 | (componentOptions.tag ? `::${componentOptions.tag}` : '') 92 | : vnode.key 93 | if (this.cache[key]) { 94 | vnode.componentInstance = this.cache[key].componentInstance 95 | } else { 96 | this.cache[key] = vnode 97 | } 98 | vnode.data.keepAlive = true 99 | } 100 | return vnode 101 | }, 102 | } 103 | -------------------------------------------------------------------------------- /src/core/config.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { no, noop, identity } from 'shared/util' 4 | 5 | import { LIFECYCLE_HOOKS } from 'shared/constants' 6 | 7 | export type Config = { 8 | // user 9 | optionMergeStrategies: { [key: string]: Function }, 10 | silent: boolean, 11 | productionTip: boolean, 12 | performance: boolean, 13 | devtools: boolean, 14 | errorHandler: ?(err: Error, vm: Component, info: string) => void, 15 | ignoredElements: Array<string>, 16 | keyCodes: { [key: string]: number | Array<number> }, 17 | 18 | // platform 19 | isReservedTag: (x?: string) => boolean, 20 | isReservedAttr: (x?: string) => boolean, 21 | parsePlatformTagName: (x: string) => string, 22 | isUnknownElement: (x?: string) => boolean, 23 | getTagNamespace: (x?: string) => string | void, 24 | mustUseProp: (tag: string, type: ?string, name: string) => boolean, 25 | 26 | // legacy 27 | _lifecycleHooks: Array<string>, 28 | } 29 | 30 | export default ({ 31 | /** 32 | * Option merge strategies (used in core/util/options) 33 | */ 34 | optionMergeStrategies: Object.create(null), 35 | 36 | /** 37 | * Whether to suppress warnings. 38 | */ 39 | silent: false, 40 | 41 | /** 42 | * Show production mode tip message on boot? 43 | */ 44 | productionTip: process.env.NODE_ENV !== 'production', 45 | 46 | /** 47 | * Whether to enable devtools 48 | */ 49 | devtools: process.env.NODE_ENV !== 'production', 50 | 51 | /** 52 | * Whether to record perf 53 | */ 54 | performance: false, 55 | 56 | /** 57 | * Error handler for watcher errors 58 | */ 59 | errorHandler: null, 60 | 61 | /** 62 | * Ignore certain custom elements 63 | */ 64 | ignoredElements: [], 65 | 66 | /** 67 | * Custom user key aliases for v-on 68 | */ 69 | keyCodes: Object.create(null), 70 | 71 | /** 72 | * Check if a tag is reserved so that it cannot be registered as a 73 | * component. This is platform-dependent and may be overwritten. 74 | */ 75 | isReservedTag: no, 76 | 77 | /** 78 | * Check if an attribute is reserved so that it cannot be used as a component 79 | * prop. This is platform-dependent and may be overwritten. 80 | */ 81 | isReservedAttr: no, 82 | 83 | /** 84 | * Check if a tag is an unknown element. 85 | * Platform-dependent. 86 | */ 87 | isUnknownElement: no, 88 | 89 | /** 90 | * Get the namespace of an element 91 | */ 92 | getTagNamespace: noop, 93 | 94 | /** 95 | * Parse the real tag name for the specific platform. 96 | */ 97 | parsePlatformTagName: identity, 98 | 99 | /** 100 | * Check if an attribute must be bound using property, e.g. value 101 | * Platform-dependent. 102 | */ 103 | mustUseProp: no, 104 | 105 | /** 106 | * Exposed for legacy reasons 107 | */ 108 | _lifecycleHooks: LIFECYCLE_HOOKS, 109 | }: Config) 110 | -------------------------------------------------------------------------------- /src/core/global-api/assets.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from '../config' 4 | import { ASSET_TYPES } from 'shared/constants' 5 | import { 6 | warn, 7 | // isPlainObject, 8 | camelize, 9 | capitalize, 10 | } from '../util/index' 11 | 12 | export function initAssetRegisters(Vue: GlobalAPI) { 13 | /** 14 | * Create asset registration methods. 15 | */ 16 | ASSET_TYPES.forEach(type => { 17 | Vue[type] = function( 18 | id: string, 19 | definition: Function | Object, 20 | ): Function | Object | void { 21 | if (!definition) { 22 | return this.options[type + 's'][id] 23 | } else { 24 | /* istanbul ignore if */ 25 | if (process.env.NODE_ENV !== 'production') { 26 | if (type === 'component' && config.isReservedTag(id)) { 27 | warn( 28 | 'Do not use built-in or reserved HTML elements as component ' + 29 | 'id: ' + 30 | id, 31 | ) 32 | } 33 | } 34 | // if (type === 'component' && isPlainObject(definition)) { 35 | // definition.name = definition.name || id 36 | // definition = this.options._base.extend(definition) 37 | // } 38 | if (type === 'directive' && typeof definition === 'function') { 39 | definition = { bind: definition, update: definition } 40 | } 41 | /** 42 | * react-vue change 43 | */ 44 | if (type === 'component' && typeof definition === 'function') { 45 | id = capitalize(camelize(id)) 46 | if (definition.name) { 47 | const _id = capitalize(camelize(definition.name)) 48 | if (_id !== id) { 49 | this.options[type + 's'][_id] = definition 50 | } 51 | } 52 | } 53 | 54 | this.options[type + 's'][id] = definition 55 | return definition 56 | } 57 | } 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /src/core/global-api/extend.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { ASSET_TYPES } from 'shared/constants' 4 | import { warn, extend, mergeOptions } from '../util/index' 5 | import { defineComputed, proxy } from '../instance/state' 6 | 7 | export function initExtend(Vue: GlobalAPI) { 8 | /** 9 | * Each instance constructor, including Vue, has a unique 10 | * cid. This enables us to create wrapped "child 11 | * constructors" for prototypal inheritance and cache them. 12 | */ 13 | Vue.cid = 0 14 | let cid = 1 15 | 16 | /** 17 | * Class inheritance 18 | */ 19 | Vue.extend = function(extendOptions: Object): Function { 20 | extendOptions = extendOptions || {} 21 | const Super = this 22 | const SuperId = Super.cid 23 | const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}) 24 | if (cachedCtors[SuperId]) { 25 | return cachedCtors[SuperId] 26 | } 27 | 28 | const name = extendOptions.name || Super.options.name 29 | if (process.env.NODE_ENV !== 'production') { 30 | if (!/^[a-zA-Z][\w-]*$/.test(name)) { 31 | warn( 32 | 'Invalid component name: "' + 33 | name + 34 | '". Component names ' + 35 | 'can only contain alphanumeric characters and the hyphen, ' + 36 | 'and must start with a letter.', 37 | ) 38 | } 39 | } 40 | 41 | const Sub = function VueComponent(options) { 42 | this._init(options) 43 | } 44 | Sub.prototype = Object.create(Super.prototype) 45 | Sub.prototype.constructor = Sub 46 | Sub.cid = cid++ 47 | Sub.options = mergeOptions(Super.options, extendOptions) 48 | Sub['super'] = Super 49 | 50 | // For props and computed properties, we define the proxy getters on 51 | // the Vue instances at extension time, on the extended prototype. This 52 | // avoids Object.defineProperty calls for each instance created. 53 | if (Sub.options.props) { 54 | initProps(Sub) 55 | } 56 | if (Sub.options.computed) { 57 | initComputed(Sub) 58 | } 59 | 60 | // allow further extension/mixin/plugin usage 61 | Sub.extend = Super.extend 62 | Sub.mixin = Super.mixin 63 | Sub.use = Super.use 64 | 65 | // create asset registers, so extended classes 66 | // can have their private assets too. 67 | ASSET_TYPES.forEach(function(type) { 68 | Sub[type] = Super[type] 69 | }) 70 | // enable recursive self-lookup 71 | if (name) { 72 | Sub.options.components[name] = Sub 73 | } 74 | 75 | // keep a reference to the super options at extension time. 76 | // later at instantiation we can check if Super's options have 77 | // been updated. 78 | Sub.superOptions = Super.options 79 | Sub.extendOptions = extendOptions 80 | Sub.sealedOptions = extend({}, Sub.options) 81 | 82 | // cache constructor 83 | cachedCtors[SuperId] = Sub 84 | return Sub 85 | } 86 | } 87 | 88 | function initProps(Comp) { 89 | const props = Comp.options.props 90 | for (const key in props) { 91 | proxy(Comp.prototype, `_props`, key) 92 | } 93 | } 94 | 95 | function initComputed(Comp) { 96 | const computed = Comp.options.computed 97 | for (const key in computed) { 98 | defineComputed(Comp.prototype, key, computed[key]) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/core/global-api/mixin.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { mergeOptions } from '../util/index' 4 | 5 | export function initMixin(Vue: GlobalAPI) { 6 | Vue.mixin = function(mixin: Object) { 7 | this.options = mergeOptions(this.options, mixin) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/core/global-api/use.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { toArray } from '../util/index' 4 | 5 | export function initUse(Vue: GlobalAPI) { 6 | Vue.use = function(plugin: Function | Object) { 7 | /* istanbul ignore if */ 8 | if (plugin.installed) { 9 | return 10 | } 11 | // additional parameters 12 | const args = toArray(arguments, 1) 13 | args.unshift(this) 14 | if (typeof plugin.install === 'function') { 15 | plugin.install.apply(plugin, args) 16 | } else if (typeof plugin === 'function') { 17 | plugin.apply(null, args) 18 | } 19 | plugin.installed = true 20 | return this 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/core/index.js: -------------------------------------------------------------------------------- 1 | import Vue from './instance/index' 2 | import { initGlobalAPI } from './global-api/index' 3 | import { isServerRendering } from 'core/util/env' 4 | 5 | initGlobalAPI(Vue) 6 | 7 | Object.defineProperty(Vue.prototype, '$isServer', { 8 | get: isServerRendering, 9 | }) 10 | 11 | Vue.version = '__VERSION__' 12 | 13 | export default Vue 14 | -------------------------------------------------------------------------------- /src/core/instance/index.js: -------------------------------------------------------------------------------- 1 | import { initMixin } from './init' 2 | import { stateMixin } from './state' 3 | /** 4 | * react-vue change 5 | */ 6 | // import { renderMixin } from './render' 7 | import { eventsMixin } from './events' 8 | /** 9 | * react-vue change 10 | */ 11 | // import { lifecycleMixin } from './lifecycle' 12 | import { 13 | warn, 14 | // nextTick, 15 | } from '../util/index' 16 | 17 | function Vue(options) { 18 | if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue)) { 19 | warn('Vue is a constructor and should be called with the `new` keyword') 20 | } 21 | 22 | if (process.env.NODE_ENV !== 'production') { 23 | warn( 24 | 'Vue Native has been deprecated and is no longer being maintained. Please consider migrating to React Native or use NativeScript-Vue instead', 25 | ) 26 | } 27 | /** 28 | * react-vue change 29 | */ 30 | if (options) { 31 | if (options.reactVueSlots) { 32 | this.$slots = options.reactVueSlots 33 | } 34 | if (options.reactVueForceUpdate) { 35 | this.$forceUpdate = options.reactVueForceUpdate 36 | } 37 | } 38 | this._init(options) 39 | } 40 | 41 | initMixin(Vue) 42 | stateMixin(Vue) 43 | eventsMixin(Vue) 44 | 45 | /** 46 | * react-vue change 47 | */ 48 | // lifecycleMixin(Vue) 49 | // renderMixin(Vue) 50 | 51 | export default Vue 52 | -------------------------------------------------------------------------------- /src/core/instance/inject.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { hasSymbol } from 'core/util/env' 4 | import { warn } from '../util/index' 5 | import { defineReactive } 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' ? provide.call(vm) : provide 11 | } 12 | } 13 | 14 | export function initInjections(vm: Component) { 15 | const result = resolveInject(vm.$options.inject, vm) 16 | if (result) { 17 | Object.keys(result).forEach(key => { 18 | /* istanbul ignore else */ 19 | if (process.env.NODE_ENV !== 'production') { 20 | defineReactive(vm, key, result[key], () => { 21 | warn( 22 | `Avoid mutating an injected value directly since the changes will be ` + 23 | `overwritten whenever the provided component re-renders. ` + 24 | `injection being mutated: "${key}"`, 25 | vm, 26 | ) 27 | }) 28 | } else { 29 | defineReactive(vm, key, result[key]) 30 | } 31 | }) 32 | } 33 | } 34 | 35 | export function resolveInject(inject: any, vm: Component): ?Object { 36 | if (inject) { 37 | // inject is :any because flow is not smart enough to figure out cached 38 | // isArray here 39 | const isArray = Array.isArray(inject) 40 | const result = Object.create(null) 41 | const keys = isArray 42 | ? inject 43 | : 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 = isArray ? key : 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 | } 59 | return result 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /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, toJSON', // 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' && Proxy.toString().match(/native code/) 27 | 28 | if (hasProxy) { 29 | const isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta') 30 | config.keyCodes = new Proxy(config.keyCodes, { 31 | set(target, key, value) { 32 | if (isBuiltInModifier(key)) { 33 | warn( 34 | `Avoid overwriting built-in modifier in config.keyCodes: .${key}`, 35 | ) 36 | return false 37 | } else { 38 | target[key] = value 39 | return true 40 | } 41 | }, 42 | }) 43 | } 44 | 45 | const hasHandler = { 46 | has(target, key) { 47 | const has = key in target 48 | const isAllowed = allowedGlobals(key) || key.charAt(0) === '_' 49 | if (!has && !isAllowed) { 50 | warnNonPresent(target, key) 51 | } 52 | return has || !isAllowed 53 | }, 54 | } 55 | 56 | const getHandler = { 57 | get(target, key) { 58 | // react-vue change ```key === 'toJSON'``` prevent warn in react-native 59 | if (typeof key === 'string' && !(key in target) && key !== 'toJSON') { 60 | warnNonPresent(target, key) 61 | } 62 | return target[key] 63 | }, 64 | } 65 | 66 | initProxy = function initProxy(vm) { 67 | if (hasProxy) { 68 | // determine which proxy handler to use 69 | const options = vm.$options 70 | const handlers = 71 | options.render && options.render._withStripped ? getHandler : hasHandler 72 | vm._renderProxy = new Proxy(vm, handlers) 73 | } else { 74 | vm._renderProxy = vm 75 | } 76 | } 77 | } 78 | 79 | export { initProxy } 80 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/bind-object-props.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from 'core/config' 4 | import { isObject, warn, toObject } from 'core/util/index' 5 | 6 | /** 7 | * Runtime helper for merging v-bind="object" into a VNode's data. 8 | */ 9 | export function bindObjectProps( 10 | data: any, 11 | tag: string, 12 | value: any, 13 | asProp?: boolean, 14 | ): VNodeData { 15 | if (value) { 16 | if (!isObject(value)) { 17 | process.env.NODE_ENV !== 'production' && 18 | warn('v-bind without argument expects an Object or Array value', this) 19 | } else { 20 | if (Array.isArray(value)) { 21 | value = toObject(value) 22 | } 23 | let hash 24 | for (const key in value) { 25 | if (key === 'class' || key === 'style') { 26 | hash = data 27 | } else { 28 | const type = data.attrs && data.attrs.type 29 | hash = 30 | asProp || config.mustUseProp(tag, type, key) 31 | ? data.domProps || (data.domProps = {}) 32 | : data.attrs || (data.attrs = {}) 33 | } 34 | if (!(key in hash)) { 35 | hash[key] = value[key] 36 | } 37 | } 38 | } 39 | } 40 | return data 41 | } 42 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/check-keycodes.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from 'core/config' 4 | 5 | /** 6 | * Runtime helper for checking keyCodes from config. 7 | */ 8 | export function checkKeyCodes( 9 | eventKeyCode: number, 10 | key: string, 11 | builtInAlias: number | Array<number> | void, 12 | ): boolean { 13 | const keyCodes = config.keyCodes[key] || builtInAlias 14 | if (Array.isArray(keyCodes)) { 15 | return keyCodes.indexOf(eventKeyCode) === -1 16 | } else { 17 | return keyCodes !== eventKeyCode 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/render-list.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isObject } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering v-for lists. 7 | */ 8 | export function renderList(val: any, render: () => VNode): ?Array<VNode> { 9 | let ret: ?Array<VNode>, i, l, keys, key 10 | if (Array.isArray(val) || typeof val === 'string') { 11 | ret = new Array(val.length) 12 | for (i = 0, l = val.length; i < l; i++) { 13 | ret[i] = render(val[i], i) 14 | } 15 | } else if (typeof val === 'number') { 16 | ret = new Array(val) 17 | for (i = 0; i < val; i++) { 18 | ret[i] = render(i + 1, i) 19 | } 20 | } else if (isObject(val)) { 21 | keys = Object.keys(val) 22 | ret = new Array(keys.length) 23 | for (i = 0, l = keys.length; i < l; i++) { 24 | key = keys[i] 25 | ret[i] = render(val[key], key, i) 26 | } 27 | } 28 | return ret 29 | } 30 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/render-slot.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { extend, warn } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering <slot> 7 | */ 8 | export function renderSlot( 9 | name: string, 10 | fallback: ?Array<VNode>, 11 | props: ?Object, 12 | bindObject: ?Object, 13 | ): ?Array<VNode> { 14 | const scopedSlotFn = this.$scopedSlots[name] 15 | if (scopedSlotFn) { 16 | // scoped slot 17 | props = props || {} 18 | if (bindObject) { 19 | extend(props, bindObject) 20 | } 21 | return scopedSlotFn(props) || fallback 22 | } else { 23 | const slotNodes = this.$slots[name] 24 | // warn duplicate slot usage 25 | if (slotNodes && process.env.NODE_ENV !== 'production') { 26 | slotNodes._rendered && 27 | warn( 28 | `Duplicate presence of slot "${name}" found in the same render tree ` + 29 | `- this will likely cause render errors.`, 30 | this, 31 | ) 32 | slotNodes._rendered = true 33 | } 34 | return slotNodes || fallback 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /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<VNode> { 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) ? cloneVNodes(tree) : cloneVNode(tree) 17 | } 18 | // otherwise, render a fresh tree. 19 | tree = this._staticTrees[index] = this.$options.staticRenderFns[index].call( 20 | this._renderProxy, 21 | ) 22 | markStatic(tree, `__static__${index}`, false) 23 | return tree 24 | } 25 | 26 | /** 27 | * Runtime helper for v-once. 28 | * Effectively it means marking the node as static with a unique key. 29 | */ 30 | export function markOnce( 31 | tree: VNode | Array<VNode>, 32 | index: number, 33 | key: string, 34 | ) { 35 | markStatic(tree, `__once__${index}${key ? `_${key}` : ``}`, true) 36 | return tree 37 | } 38 | 39 | function markStatic(tree: VNode | Array<VNode>, key: string, isOnce: boolean) { 40 | if (Array.isArray(tree)) { 41 | for (let i = 0; i < tree.length; i++) { 42 | if (tree[i] && typeof tree[i] !== 'string') { 43 | markStaticNode(tree[i], `${key}_${i}`, isOnce) 44 | } 45 | } 46 | } else { 47 | markStaticNode(tree, key, isOnce) 48 | } 49 | } 50 | 51 | function markStaticNode(node, key, isOnce) { 52 | node.isStatic = true 53 | node.key = key 54 | node.isOnce = isOnce 55 | } 56 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/resolve-filter.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { identity, resolveAsset } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for resolving filters 7 | */ 8 | export function resolveFilter(id: string): Function { 9 | return resolveAsset(this.$options, 'filters', id, true) || identity 10 | } 11 | -------------------------------------------------------------------------------- /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<VNode>, 8 | context: ?Component, 9 | ): { [key: string]: Array<VNode> } { 10 | const slots = {} 11 | if (!children) { 12 | return slots 13 | } 14 | const defaultSlot = [] 15 | let name, child 16 | for (let i = 0, l = children.length; i < l; i++) { 17 | child = children[i] 18 | // named slots should only be respected if the vnode was rendered in the 19 | // same context. 20 | if ( 21 | (child.context === context || child.functionalContext === context) && 22 | child.data && 23 | (name = child.data.slot) 24 | ) { 25 | const slot = slots[name] || (slots[name] = []) 26 | if (child.tag === 'template') { 27 | slot.push.apply(slot, child.children) 28 | } else { 29 | slot.push(child) 30 | } 31 | } else { 32 | defaultSlot.push(child) 33 | } 34 | } 35 | // ignore whitespace 36 | if (!defaultSlot.every(isWhitespace)) { 37 | slots.default = defaultSlot 38 | } 39 | return slots 40 | } 41 | 42 | function isWhitespace(node: VNode): boolean { 43 | return node.isComment || node.text === ' ' 44 | } 45 | 46 | export function resolveScopedSlots( 47 | fns: Array<[string, Function]>, 48 | ): { [key: string]: Function } { 49 | const res = {} 50 | for (let i = 0; i < fns.length; i++) { 51 | res[fns[i][0]] = fns[i][1] 52 | } 53 | return res 54 | } 55 | -------------------------------------------------------------------------------- /src/core/observer/array.js: -------------------------------------------------------------------------------- 1 | /* 2 | * not type checking this file because flow doesn't play well with 3 | * dynamically accessing methods on Array prototype 4 | */ 5 | 6 | import { def } from '../util/index' 7 | 8 | const arrayProto = Array.prototype 9 | export const arrayMethods = Object.create(arrayProto) 10 | 11 | /** 12 | * Intercept mutating methods and emit events 13 | */ 14 | ;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach( 15 | function(method) { 16 | // cache original method 17 | const original = arrayProto[method] 18 | def(arrayMethods, method, function mutator() { 19 | // avoid leaking arguments: 20 | // http://jsperf.com/closure-with-arguments 21 | let i = arguments.length 22 | const args = new Array(i) 23 | while (i--) { 24 | args[i] = arguments[i] 25 | } 26 | const result = original.apply(this, args) 27 | const ob = this.__ob__ 28 | let inserted 29 | switch (method) { 30 | case 'push': 31 | inserted = args 32 | break 33 | case 'unshift': 34 | inserted = args 35 | break 36 | case 'splice': 37 | inserted = args.slice(2) 38 | break 39 | } 40 | if (inserted) ob.observeArray(inserted) 41 | // notify change 42 | ob.dep.notify() 43 | return result 44 | }) 45 | }, 46 | ) 47 | -------------------------------------------------------------------------------- /src/core/observer/dep.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import type Watcher from './watcher' 4 | import { remove } from '../util/index' 5 | 6 | let uid = 0 7 | 8 | /** 9 | * A dep is an observable that can have multiple 10 | * directives subscribing to it. 11 | */ 12 | export default class Dep { 13 | static target: ?Watcher 14 | id: number 15 | subs: Array<Watcher> 16 | 17 | constructor() { 18 | this.id = uid++ 19 | this.subs = [] 20 | } 21 | 22 | addSub(sub: Watcher) { 23 | this.subs.push(sub) 24 | } 25 | 26 | removeSub(sub: Watcher) { 27 | remove(this.subs, sub) 28 | } 29 | 30 | depend() { 31 | if (Dep.target) { 32 | Dep.target.addDep(this) 33 | } 34 | } 35 | 36 | notify() { 37 | // stabilize the subscriber list first 38 | const subs = this.subs.slice() 39 | for (let i = 0, l = subs.length; i < l; i++) { 40 | subs[i].update() 41 | } 42 | } 43 | } 44 | 45 | // the current target watcher being evaluated. 46 | // this is globally unique because there could be only one 47 | // watcher being evaluated at any time. 48 | Dep.target = null 49 | const targetStack = [] 50 | 51 | export function pushTarget(_target: Watcher) { 52 | if (Dep.target) targetStack.push(Dep.target) 53 | Dep.target = _target 54 | } 55 | 56 | export function popTarget() { 57 | Dep.target = targetStack.pop() 58 | } 59 | -------------------------------------------------------------------------------- /src/core/util/debug.js: -------------------------------------------------------------------------------- 1 | import config from '../config' 2 | import { noop } from 'shared/util' 3 | 4 | export let warn = noop 5 | export let tip = noop 6 | export let formatComponentName 7 | 8 | if (process.env.NODE_ENV !== 'production') { 9 | const hasConsole = typeof console !== 'undefined' 10 | const classifyRE = /(?:^|[-_])(\w)/g 11 | const classify = str => 12 | str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '') 13 | 14 | warn = (msg, vm) => { 15 | if (hasConsole && !config.silent) { 16 | console.error( 17 | `[Vue warn]: ${msg}` + (vm ? generateComponentTrace(vm) : ''), 18 | ) 19 | } 20 | } 21 | 22 | tip = (msg, vm) => { 23 | if (hasConsole && !config.silent) { 24 | console.warn(`[Vue tip]: ${msg}` + (vm ? generateComponentTrace(vm) : '')) 25 | } 26 | } 27 | 28 | formatComponentName = (vm, includeFile) => { 29 | if (vm.$root === vm) { 30 | return '<Root>' 31 | } 32 | let name = 33 | typeof vm === 'string' 34 | ? vm 35 | : typeof vm === 'function' && vm.options 36 | ? vm.options.name 37 | : vm._isVue 38 | ? vm.$options.name || vm.$options._componentTag 39 | : vm.name 40 | 41 | const file = vm._isVue && vm.$options.__file 42 | if (!name && file) { 43 | const match = file.match(/([^/\\]+)\.vue$/) 44 | name = match && match[1] 45 | } 46 | 47 | return ( 48 | (name ? `<${classify(name)}>` : `<Anonymous>`) + 49 | (file && includeFile !== false ? ` at ${file}` : '') 50 | ) 51 | } 52 | 53 | const repeat = (str, n) => { 54 | let res = '' 55 | while (n) { 56 | if (n % 2 === 1) res += str 57 | if (n > 1) str += str 58 | n >>= 1 59 | } 60 | return res 61 | } 62 | 63 | const generateComponentTrace = vm => { 64 | if (vm._isVue && vm.$parent) { 65 | const tree = [] 66 | let currentRecursiveSequence = 0 67 | while (vm) { 68 | if (tree.length > 0) { 69 | const last = tree[tree.length - 1] 70 | if (last.constructor === vm.constructor) { 71 | currentRecursiveSequence++ 72 | vm = vm.$parent 73 | continue 74 | } else if (currentRecursiveSequence > 0) { 75 | tree[tree.length - 1] = [last, currentRecursiveSequence] 76 | currentRecursiveSequence = 0 77 | } 78 | } 79 | tree.push(vm) 80 | vm = vm.$parent 81 | } 82 | return ( 83 | '\n\nfound in\n\n' + 84 | tree 85 | .map( 86 | (vm, i) => 87 | `${i === 0 ? '---> ' : repeat(' ', 5 + i * 2)}${ 88 | Array.isArray(vm) 89 | ? `${formatComponentName(vm[0])}... (${ 90 | vm[1] 91 | } recursive calls)` 92 | : formatComponentName(vm) 93 | }`, 94 | ) 95 | .join('\n') 96 | ) 97 | } else { 98 | return `\n\n(found in ${formatComponentName(vm)})` 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/core/util/error.js: -------------------------------------------------------------------------------- 1 | import config from '../config' 2 | import { warn } from './debug' 3 | import { inBrowser } from './env' 4 | 5 | export function handleError(err, vm, info) { 6 | if (config.errorHandler) { 7 | config.errorHandler.call(null, err, vm, info) 8 | } else { 9 | if (process.env.NODE_ENV !== 'production') { 10 | warn(`Error in ${info}: "${err.toString()}"`, vm) 11 | } 12 | /* istanbul ignore else */ 13 | if (inBrowser && typeof console !== 'undefined') { 14 | console.error(err) 15 | } else { 16 | throw err 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/core/util/index.js: -------------------------------------------------------------------------------- 1 | export * from 'shared/util' 2 | export * from './lang' 3 | export * from './env' 4 | export * from './options' 5 | export * from './debug' 6 | export * from './props' 7 | export * from './error' 8 | export { defineReactive } from '../observer/index' 9 | -------------------------------------------------------------------------------- /src/core/util/lang.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export const emptyObject = Object.freeze({}) 4 | 5 | /** 6 | * Check if a string starts with $ or _ 7 | */ 8 | export function isReserved(str: string): boolean { 9 | const c = (str + '').charCodeAt(0) 10 | return c === 0x24 || c === 0x5f 11 | } 12 | 13 | /** 14 | * Define a property. 15 | */ 16 | export function def(obj: Object, key: string, val: any, enumerable?: boolean) { 17 | Object.defineProperty(obj, key, { 18 | value: val, 19 | enumerable: !!enumerable, 20 | writable: true, 21 | configurable: true, 22 | }) 23 | } 24 | 25 | /** 26 | * Parse simple path. 27 | */ 28 | const bailRE = /[^\w.$]/ 29 | export function parsePath(path: string): any { 30 | if (bailRE.test(path)) { 31 | return 32 | } 33 | const segments = path.split('.') 34 | return function(obj) { 35 | for (let i = 0; i < segments.length; i++) { 36 | if (!obj) return 37 | obj = obj[segments[i]] 38 | } 39 | return obj 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/core/util/perf.js: -------------------------------------------------------------------------------- 1 | import { inBrowser } from './env' 2 | 3 | export let mark 4 | export let measure 5 | 6 | if (process.env.NODE_ENV !== 'production') { 7 | const perf = inBrowser && window.performance 8 | /* istanbul ignore if */ 9 | if ( 10 | perf && 11 | perf.mark && 12 | perf.measure && 13 | perf.clearMarks && 14 | perf.clearMeasures 15 | ) { 16 | mark = tag => perf.mark(tag) 17 | measure = (name, startTag, endTag) => { 18 | perf.measure(name, startTag, endTag) 19 | perf.clearMarks(startTag) 20 | perf.clearMarks(endTag) 21 | perf.clearMeasures(name) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /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 { isDef, camelize, validateProp } from '../util/index' 9 | 10 | export function createFunctionalComponent( 11 | Ctor: Class<Component>, 12 | propsData: ?Object, 13 | data: VNodeData, 14 | context: Component, 15 | children: ?Array<VNode>, 16 | ): VNode | void { 17 | const props = {} 18 | const propOptions = Ctor.options.props 19 | if (isDef(propOptions)) { 20 | for (const key in propOptions) { 21 | props[key] = validateProp(key, propOptions, propsData) 22 | } 23 | } else { 24 | if (isDef(data.attrs)) mergeProps(props, data.attrs) 25 | if (isDef(data.props)) mergeProps(props, data.props) 26 | } 27 | // ensure the createElement function in functional components 28 | // gets a unique context - this is necessary for correct named slot check 29 | const _context = Object.create(context) 30 | const h = (a, b, c, d) => createElement(_context, a, b, c, d, true) 31 | const vnode = Ctor.options.render.call(null, h, { 32 | data, 33 | props, 34 | children, 35 | parent: context, 36 | listeners: data.on || {}, 37 | injections: resolveInject(Ctor.options.inject, context), 38 | slots: () => resolveSlots(children, context), 39 | }) 40 | if (vnode instanceof VNode) { 41 | vnode.functionalContext = context 42 | if (data.slot) { 43 | ;(vnode.data || (vnode.data = {})).slot = data.slot 44 | } 45 | } 46 | return vnode 47 | } 48 | 49 | function mergeProps(to, from) { 50 | for (const key in from) { 51 | to[camelize(key)] = from[key] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /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<Component>, 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 (key !== keyInLowerCase && attrs && hasOwn(attrs, keyInLowerCase)) { 32 | tip( 33 | `Prop "${keyInLowerCase}" is passed to component ` + 34 | `${formatComponentName( 35 | tag || Ctor, 36 | )}, but the declared prop name is` + 37 | ` "${key}". ` + 38 | `Note that HTML attributes are case-insensitive and camelCased ` + 39 | `props need to use their kebab-case equivalents when using in-DOM ` + 40 | `templates. You should probably use "${altKey}" instead of "${key}".`, 41 | ) 42 | } 43 | } 44 | checkProp(res, props, key, altKey, true) || 45 | checkProp(res, attrs, key, altKey, false) 46 | } 47 | } 48 | return res 49 | } 50 | 51 | function checkProp( 52 | res: Object, 53 | hash: any, 54 | key: string, 55 | altKey: string, 56 | preserve: boolean, 57 | ): boolean { 58 | if (isDef(hash)) { 59 | if (hasOwn(hash, key)) { 60 | res[key] = hash[key] 61 | if (!preserve) { 62 | delete hash[key] 63 | } 64 | return true 65 | } else if (hasOwn(hash, altKey)) { 66 | res[key] = hash[altKey] 67 | if (!preserve) { 68 | delete hash[altKey] 69 | } 70 | return true 71 | } 72 | } 73 | return false 74 | } 75 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/get-first-component-child.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isDef } from 'shared/util' 4 | 5 | export function getFirstComponentChild(children: ?Array<VNode>): ?VNode { 6 | if (Array.isArray(children)) { 7 | for (let i = 0; i < children.length; i++) { 8 | const c = children[i] 9 | if (isDef(c) && isDef(c.componentOptions)) { 10 | return c 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export * from './merge-hook' 4 | export * from './extract-props' 5 | export * from './update-listeners' 6 | export * from './normalize-children' 7 | export * from './resolve-async-component' 8 | export * from './get-first-component-child' 9 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/merge-hook.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { createFnInvoker } from './update-listeners' 4 | import { remove, isDef, isUndef, isTrue } from 'shared/util' 5 | 6 | export function mergeVNodeHook(def: Object, hookKey: string, hook: Function) { 7 | let invoker 8 | const oldHook = def[hookKey] 9 | 10 | function wrappedHook() { 11 | hook.apply(this, arguments) 12 | // important: remove merged hook to ensure it's called only once 13 | // and prevent memory leak 14 | remove(invoker.fns, wrappedHook) 15 | } 16 | 17 | if (isUndef(oldHook)) { 18 | // no existing hook 19 | invoker = createFnInvoker([wrappedHook]) 20 | } else { 21 | /* istanbul ignore if */ 22 | if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { 23 | // already a merged invoker 24 | invoker = oldHook 25 | invoker.fns.push(wrappedHook) 26 | } else { 27 | // existing plain hook 28 | invoker = createFnInvoker([oldHook, wrappedHook]) 29 | } 30 | } 31 | 32 | invoker.merged = true 33 | def[hookKey] = invoker 34 | } 35 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/normalize-children.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import VNode, { createTextVNode } from 'core/vdom/vnode' 4 | import { isDef, isUndef, isPrimitive } from 'shared/util' 5 | 6 | // The template compiler attempts to minimize the need for normalization by 7 | // statically analyzing the template at compile time. 8 | // 9 | // For plain HTML markup, normalization can be completely skipped because the 10 | // generated render function is guaranteed to return Array<VNode>. There are 11 | // two cases where extra normalization is needed: 12 | 13 | // 1. When the children contains components - because a functional component 14 | // may return an Array instead of a single root. In this case, just a simple 15 | // normalization is needed - if any child is an Array, we flatten the whole 16 | // thing with Array.prototype.concat. It is guaranteed to be only 1-level deep 17 | // because functional components already normalize their own children. 18 | export function simpleNormalizeChildren(children: any) { 19 | for (let i = 0; i < children.length; i++) { 20 | if (Array.isArray(children[i])) { 21 | return Array.prototype.concat.apply([], children) 22 | } 23 | } 24 | return children 25 | } 26 | 27 | // 2. When the children contains constructs that always generated nested Arrays, 28 | // e.g. <template>, <slot>, v-for, or when the children is provided by user 29 | // with hand-written render functions / JSX. In such cases a full normalization 30 | // is needed to cater to all possible types of children values. 31 | export function normalizeChildren(children: any): ?Array<VNode> { 32 | return isPrimitive(children) 33 | ? [createTextVNode(children)] 34 | : Array.isArray(children) 35 | ? normalizeArrayChildren(children) 36 | : undefined 37 | } 38 | 39 | function normalizeArrayChildren( 40 | children: any, 41 | nestedIndex?: string, 42 | ): Array<VNode> { 43 | const res = [] 44 | let i, c, last 45 | for (i = 0; i < children.length; i++) { 46 | c = children[i] 47 | if (isUndef(c) || typeof c === 'boolean') continue 48 | last = res[res.length - 1] 49 | // nested 50 | if (Array.isArray(c)) { 51 | res.push.apply( 52 | res, 53 | normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`), 54 | ) 55 | } else if (isPrimitive(c)) { 56 | if (isDef(last) && isDef(last.text)) { 57 | ;(last: any).text += String(c) 58 | } else if (c !== '') { 59 | // convert primitive to vnode 60 | res.push(createTextVNode(c)) 61 | } 62 | } else { 63 | if (isDef(c.text) && isDef(last) && isDef(last.text)) { 64 | res[res.length - 1] = createTextVNode(last.text + c.text) 65 | } else { 66 | // default key for nested array children (likely generated by v-for) 67 | if (isDef(c.tag) && isUndef(c.key) && isDef(nestedIndex)) { 68 | c.key = `__vlist${(nestedIndex: any)}_${i}__` 69 | } 70 | res.push(c) 71 | } 72 | } 73 | } 74 | return res 75 | } 76 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/resolve-async-component.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn, once, isDef, isUndef, isTrue, isObject } from 'core/util/index' 4 | 5 | function ensureCtor(comp, base) { 6 | return isObject(comp) ? base.extend(comp) : comp 7 | } 8 | 9 | export function resolveAsyncComponent( 10 | factory: Function, 11 | baseCtor: Class<Component>, 12 | context: Component, 13 | ): Class<Component> | void { 14 | if (isTrue(factory.error) && isDef(factory.errorComp)) { 15 | return factory.errorComp 16 | } 17 | 18 | if (isDef(factory.resolved)) { 19 | return factory.resolved 20 | } 21 | 22 | if (isTrue(factory.loading) && isDef(factory.loadingComp)) { 23 | return factory.loadingComp 24 | } 25 | 26 | if (isDef(factory.contexts)) { 27 | // already pending 28 | factory.contexts.push(context) 29 | } else { 30 | const contexts = (factory.contexts = [context]) 31 | let sync = true 32 | 33 | const forceRender = () => { 34 | for (let i = 0, l = contexts.length; i < l; i++) { 35 | contexts[i].$forceUpdate() 36 | } 37 | } 38 | 39 | const resolve = once((res: Object | Class<Component>) => { 40 | // cache resolved 41 | factory.resolved = ensureCtor(res, baseCtor) 42 | // invoke callbacks only if this is not a synchronous resolve 43 | // (async resolves are shimmed as synchronous during SSR) 44 | if (!sync) { 45 | forceRender() 46 | } 47 | }) 48 | 49 | const reject = once(reason => { 50 | process.env.NODE_ENV !== 'production' && 51 | warn( 52 | `Failed to resolve async component: ${String(factory)}` + 53 | (reason ? `\nReason: ${reason}` : ''), 54 | ) 55 | if (isDef(factory.errorComp)) { 56 | factory.error = true 57 | forceRender() 58 | } 59 | }) 60 | 61 | const res = factory(resolve, reject) 62 | 63 | if (isObject(res)) { 64 | if (typeof res.then === 'function') { 65 | // () => Promise 66 | if (isUndef(factory.resolved)) { 67 | res.then(resolve, reject) 68 | } 69 | } else if ( 70 | isDef(res.component) && 71 | typeof res.component.then === 'function' 72 | ) { 73 | res.component.then(resolve, reject) 74 | 75 | if (isDef(res.error)) { 76 | factory.errorComp = ensureCtor(res.error, baseCtor) 77 | } 78 | 79 | if (isDef(res.loading)) { 80 | factory.loadingComp = ensureCtor(res.loading, baseCtor) 81 | if (res.delay === 0) { 82 | factory.loading = true 83 | } else { 84 | setTimeout(() => { 85 | if (isUndef(factory.resolved) && isUndef(factory.error)) { 86 | factory.loading = true 87 | forceRender() 88 | } 89 | }, res.delay || 200) 90 | } 91 | } 92 | 93 | if (isDef(res.timeout)) { 94 | setTimeout(() => { 95 | reject( 96 | process.env.NODE_ENV !== 'production' 97 | ? `timeout (${res.timeout}ms)` 98 | : null, 99 | ) 100 | }, res.timeout) 101 | } 102 | } 103 | } 104 | 105 | sync = false 106 | // return in case resolved synchronously 107 | return factory.loading ? factory.loadingComp : factory.resolved 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /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>): Function { 27 | function invoker() { 28 | const fns = invoker.fns 29 | if (Array.isArray(fns)) { 30 | for (let i = 0; i < fns.length; i++) { 31 | fns[i].apply(null, arguments) 32 | } 33 | } else { 34 | // return handler return value for single handlers 35 | return fns.apply(null, arguments) 36 | } 37 | } 38 | invoker.fns = fns 39 | return invoker 40 | } 41 | 42 | export function updateListeners( 43 | on: Object, 44 | oldOn: Object, 45 | add: Function, 46 | remove: Function, 47 | vm: Component, 48 | ) { 49 | let name, cur, old, event 50 | for (name in on) { 51 | cur = on[name] 52 | old = oldOn[name] 53 | event = normalizeEvent(name) 54 | if (isUndef(cur)) { 55 | process.env.NODE_ENV !== 'production' && 56 | 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/modules/index.js: -------------------------------------------------------------------------------- 1 | import directives from './directives' 2 | import ref from './ref' 3 | 4 | export default [ref, directives] 5 | -------------------------------------------------------------------------------- /src/core/vdom/modules/ref.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { remove } from 'shared/util' 4 | 5 | export default { 6 | create(_: any, vnode: VNodeWithData) { 7 | registerRef(vnode) 8 | }, 9 | update(oldVnode: VNodeWithData, vnode: VNodeWithData) { 10 | if (oldVnode.data.ref !== vnode.data.ref) { 11 | registerRef(oldVnode, true) 12 | registerRef(vnode) 13 | } 14 | }, 15 | destroy(vnode: VNodeWithData) { 16 | registerRef(vnode, true) 17 | }, 18 | } 19 | 20 | export function registerRef(vnode: VNodeWithData, isRemoval: ?boolean) { 21 | const key = vnode.data.ref 22 | if (!key) return 23 | 24 | const vm = vnode.context 25 | const ref = vnode.componentInstance || vnode.elm 26 | const refs = vm.$refs 27 | if (isRemoval) { 28 | if (Array.isArray(refs[key])) { 29 | remove(refs[key], ref) 30 | } else if (refs[key] === ref) { 31 | refs[key] = undefined 32 | } 33 | } else { 34 | if (vnode.data.refInFor) { 35 | if (Array.isArray(refs[key]) && refs[key].indexOf(ref) < 0) { 36 | refs[key].push(ref) 37 | } else { 38 | refs[key] = [ref] 39 | } 40 | } else { 41 | refs[key] = ref 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/core/vdom/vnode.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export default class VNode { 4 | tag: string | void 5 | data: VNodeData | void 6 | children: ?Array<VNode> 7 | text: string | void 8 | elm: Node | void 9 | ns: string | void 10 | context: Component | void // rendered in this component's scope 11 | functionalContext: Component | void // only for functional component root nodes 12 | key: string | number | void 13 | componentOptions: VNodeComponentOptions | void 14 | componentInstance: Component | void // component instance 15 | parent: VNode | void // component placeholder node 16 | raw: boolean // contains raw HTML? (server only) 17 | isStatic: boolean // hoisted static node 18 | isRootInsert: boolean // necessary for enter transition check 19 | isComment: boolean // empty comment placeholder? 20 | isCloned: boolean // is a cloned node? 21 | isOnce: boolean // is a v-once node? 22 | 23 | constructor( 24 | tag?: string, 25 | data?: VNodeData, 26 | children?: ?Array<VNode>, 27 | text?: string, 28 | elm?: Node, 29 | context?: Component, 30 | componentOptions?: VNodeComponentOptions, 31 | ) { 32 | this.tag = tag 33 | this.data = data 34 | this.children = children 35 | this.text = text 36 | this.elm = elm 37 | this.ns = undefined 38 | this.context = context 39 | this.functionalContext = undefined 40 | this.key = data && data.key 41 | this.componentOptions = componentOptions 42 | this.componentInstance = undefined 43 | this.parent = undefined 44 | this.raw = false 45 | this.isStatic = false 46 | this.isRootInsert = true 47 | this.isComment = false 48 | this.isCloned = false 49 | this.isOnce = false 50 | } 51 | 52 | // DEPRECATED: alias for componentInstance for backwards compat. 53 | /* istanbul ignore next */ 54 | get child(): Component | void { 55 | return this.componentInstance 56 | } 57 | } 58 | 59 | export const createEmptyVNode = () => { 60 | const node = new VNode() 61 | node.text = '' 62 | node.isComment = true 63 | return node 64 | } 65 | 66 | export function createTextVNode(val: string | number) { 67 | return new VNode(undefined, undefined, undefined, String(val)) 68 | } 69 | 70 | // optimized shallow clone 71 | // used for static nodes and slot nodes because they may be reused across 72 | // multiple renders, cloning them avoids errors when DOM manipulations rely 73 | // on their elm reference. 74 | export function cloneVNode(vnode: VNode): VNode { 75 | const cloned = new VNode( 76 | vnode.tag, 77 | vnode.data, 78 | vnode.children, 79 | vnode.text, 80 | vnode.elm, 81 | vnode.context, 82 | vnode.componentOptions, 83 | ) 84 | cloned.ns = vnode.ns 85 | cloned.isStatic = vnode.isStatic 86 | cloned.key = vnode.key 87 | cloned.isCloned = true 88 | return cloned 89 | } 90 | 91 | export function cloneVNodes(vnodes: Array<VNode>): Array<VNode> { 92 | const len = vnodes.length 93 | const res = new Array(len) 94 | for (let i = 0; i < len; i++) { 95 | res[i] = cloneVNode(vnodes[i]) 96 | } 97 | return res 98 | } 99 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export { parseComponent } from 'sfc/parser' 4 | export * from './compiler/index.js' 5 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/codegen/BaseGenerator.js: -------------------------------------------------------------------------------- 1 | import { COMMON, WEB, NATIVE } from '../config' 2 | import { specialObserver } from '../helpers' 3 | import { RENDER_HELPER_MODULE_NAME } from '../constants' 4 | 5 | class BaseGenerator { 6 | constructor(ast, options) { 7 | this.ast = ast 8 | this.variableDependency = [] 9 | this.slots = [] 10 | this.vueConfig = options.vueConfig || {} 11 | this.uid = 0 12 | 13 | specialObserver(COMMON, this.setVariableDependency.bind(this)) 14 | specialObserver(WEB, this.setVariableDependency.bind(this)) 15 | specialObserver(NATIVE, this.setVariableDependency.bind(this)) 16 | 17 | this.coreCode = this.genElement(this.ast).trim() 18 | } 19 | 20 | setSlots(name) { 21 | if (this.slots.indexOf(name) === -1) { 22 | this.slots.push(name) 23 | } 24 | } 25 | 26 | setVariableDependency(variable) { 27 | if (this.variableDependency.indexOf(variable) === -1) { 28 | this.variableDependency.push(variable) 29 | } 30 | } 31 | 32 | generate() { 33 | const importCode = this.generateImport() 34 | const renderCode = this.generateRender() 35 | 36 | return `${importCode} \n export default ${renderCode}` 37 | } 38 | 39 | generateImport() { 40 | return this.genDependence() 41 | } 42 | 43 | generateRender() { 44 | let render = `return ${this.coreCode}` 45 | if (this.slots.length) { 46 | const slot = `const ${COMMON.renderSlot.value} = ${ 47 | COMMON.renderSlot.name 48 | }.call(this, [${this.slots.join(',')}], this.props.children)` 49 | render = `${slot}\n${render}` 50 | } 51 | render = `function render (vm) {${render}}` 52 | return render 53 | } 54 | 55 | genDependence() { 56 | let code = `` 57 | const helperDependency = this.variableDependency.filter( 58 | v => v !== COMMON.createElement && v !== COMMON.component && v.alias, 59 | ) 60 | if (helperDependency.length) { 61 | code += 'import { ' 62 | code += helperDependency.map(v => `${v.alias} as ${v.name}`).join(',') 63 | code += ` } from '${RENDER_HELPER_MODULE_NAME}'\n` 64 | } 65 | code += `import { 66 | ${COMMON.createElement.alias} as ${COMMON.createElement.name}, 67 | ${COMMON.component.alias} as ${COMMON.component.name} 68 | } from 'react'\n` 69 | if (this.variableDependency.indexOf(COMMON.directive) !== -1) { 70 | code += `const ${COMMON.directive.component} = ${COMMON.directive.name}(${COMMON.component.name}, ${COMMON.createElement.name})\n` 71 | } 72 | if (this.variableDependency.indexOf(WEB.emptyComponent) !== -1) { 73 | code += `const ${WEB.emptyComponent.component} = ${WEB.emptyComponent.name}(${COMMON.component.name}, ${COMMON.createElement.name})\n` 74 | } 75 | if (this.variableDependency.indexOf(WEB.transition) !== -1) { 76 | code += `const ${WEB.transition.component} = ${WEB.transition.name}(${COMMON.component.name}, ${COMMON.createElement.name})\n` 77 | } 78 | // if (this.variableDependency.indexOf(WEB.transitionGroup) !== -1) { 79 | // code += `const ${WEB.transitionGroup.component} = ${WEB.transitionGroup.name}(${COMMON.component.name}, ${COMMON.createElement.name})\n` 80 | // } 81 | if (this.variableDependency.indexOf(WEB.inputComponent) !== -1) { 82 | code += `const ${WEB.inputComponent.component} = ${WEB.inputComponent.name}(${COMMON.component.name}, ${COMMON.createElement.name})\n` 83 | } 84 | return code 85 | } 86 | } 87 | 88 | export default BaseGenerator 89 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/codegen/index.js: -------------------------------------------------------------------------------- 1 | import WebRenderGenerator from './WebRenderGenerator' 2 | import NativeRenderGenerator from './NativeRenderGenerator' 3 | 4 | export { WebRenderGenerator, NativeRenderGenerator } 5 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/constants.js: -------------------------------------------------------------------------------- 1 | const TAB_INDENT = ' ' 2 | const CREATE_ELEMENT = 'createElement' 3 | const COMPONENT = 'Component' 4 | const HELPER_HEADER = '__react__vue__' 5 | const HELPER_HEADER_NATIVE = '__react__vue__native__' 6 | const RENDER_HELPER_MODULE_NAME = 'vue-native-helper' 7 | 8 | export { 9 | TAB_INDENT, 10 | CREATE_ELEMENT, 11 | COMPONENT, 12 | HELPER_HEADER, 13 | HELPER_HEADER_NATIVE, 14 | RENDER_HELPER_MODULE_NAME, 15 | } 16 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/directives/html.js: -------------------------------------------------------------------------------- 1 | export default function html(ast) { 2 | const obj = {} 3 | const directive = ast.directives.filter(v => v.name === 'html')[0] 4 | obj.name = 'dangerouslySetInnerHTML' 5 | obj.value = `{__html: '${directive.value}'}` 6 | ast.attrs = ast.attrs || [] 7 | ast.attrs.push(obj) 8 | } 9 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | import html from './html' 3 | import text from './text' 4 | 5 | export { model, html, text } 6 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/directives/text.js: -------------------------------------------------------------------------------- 1 | export default function text(ast) { 2 | const children = [] 3 | const directive = ast.directives.filter(v => v.name === 'text')[0] 4 | children.push({ 5 | type: 2, 6 | text: `{{ ${directive.value} }}`, 7 | }) 8 | ast.children = children 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/helpers.js: -------------------------------------------------------------------------------- 1 | import { isUnaryTag } from './util/index' 2 | 3 | export function specialObserver(obj, cb) { 4 | for (const key in obj) { 5 | const val = obj[key] 6 | if (typeof val === 'string') { 7 | Object.defineProperty(obj, key, { 8 | enumerable: true, 9 | configurable: true, 10 | get() { 11 | cb && cb(obj) 12 | return val 13 | }, 14 | }) 15 | } else { 16 | specialObserver(val, cb) 17 | } 18 | } 19 | } 20 | 21 | export function handleUnaryTag(ast) { 22 | if (!ast.children) { 23 | return 24 | } 25 | if (isUnaryTag(ast.tag)) { 26 | let unaryTagChildren = ast.children.shift() 27 | while (unaryTagChildren) { 28 | handleUnaryTag(unaryTagChildren) 29 | ast.parent.children.push(unaryTagChildren) 30 | unaryTagChildren = ast.children.shift() 31 | } 32 | } else { 33 | let length = ast.children.length 34 | while (length--) { 35 | handleUnaryTag(ast.children[length]) 36 | } 37 | } 38 | } 39 | 40 | export function filterDirective(ast) { 41 | const arr = ['show', 'bind'] 42 | if (Array.isArray(ast.directives)) { 43 | return ast.directives.filter(v => arr.indexOf(v.name) === -1) 44 | } else { 45 | return [] 46 | } 47 | } 48 | 49 | export function filterDirectiveBindProps(ast) { 50 | if (Array.isArray(ast.directives)) { 51 | return ast.directives.filter(v => v.name === 'bind' && v.value === '$props') 52 | } else { 53 | return [] 54 | } 55 | } 56 | 57 | export function transformSpecialNewlines(text) { 58 | return text.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029') 59 | } 60 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/index.js: -------------------------------------------------------------------------------- 1 | export * from './web' 2 | export * from './native' 3 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/modules/style.js: -------------------------------------------------------------------------------- 1 | import changeCase from 'change-case' 2 | 3 | export default function parseStyleText(cssText) { 4 | const res = {} 5 | const listDelimiter = /;(?![^(]*\))/g 6 | const propertyDelimiter = /:(.+)/ 7 | cssText.split(listDelimiter).forEach(function(item) { 8 | if (item) { 9 | const tmp = item.split(propertyDelimiter) 10 | if (tmp.length > 1) { 11 | let val = tmp[1].trim() 12 | if (isNaN(val) === false) { 13 | val = parseFloat(val) 14 | } 15 | res[changeCase.camelCase(tmp[0].trim())] = val 16 | } 17 | } 18 | }) 19 | return res 20 | } 21 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/native.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash' 2 | 3 | import { parse, processAttrs } from 'compiler/parser/index' 4 | 5 | import { NativeRenderGenerator } from 'vue-native/compiler/codegen/index' 6 | 7 | export function nativeCompiler(template, options) { 8 | let ast 9 | let importCode = '' 10 | let renderCode = '() => null' 11 | options = Object.assign( 12 | { 13 | preserveWhitespace: false, 14 | }, 15 | options, 16 | ) 17 | template = template.trim() 18 | if (template) { 19 | ast = parse(template, options) 20 | let importObj = { imports: [] } 21 | traverse(ast, options, importObj) 22 | let imports = importObj.imports 23 | const renderer = new NativeRenderGenerator(ast, options) 24 | importCode = renderer.generateImport() 25 | renderCode = renderer.generateRender() 26 | // Remove extra commas 27 | renderCode = renderCode.replace(/\},{2,}/g, '},') 28 | renderCode = renderCode.replace(/\),{2,}/g, '),') 29 | // Add imports of the render props missing from main import 30 | let requiredImports = [] 31 | imports.forEach(customImport => { 32 | if (importCode.indexOf(customImport) === -1) { 33 | requiredImports.push(customImport) 34 | } 35 | }) 36 | let requiredImportsString = requiredImports.join(',') 37 | if (requiredImportsString) { 38 | importCode = importCode.replace( 39 | /\} from 'vue-native-helper'/g, 40 | `, ${requiredImportsString} } from 'vue-native-helper'`, 41 | ) 42 | } 43 | } 44 | return { 45 | ast, 46 | importCode, 47 | renderCode, 48 | } 49 | } 50 | 51 | function traverse(ast, options, importObj, parent = null, childIndex) { 52 | // Check for render-prop and render-prop-fn within child, if yes then add that as a prop 53 | // 54 | if ( 55 | ast.attrsMap && 56 | (ast.attrsMap['render-prop'] || ast.attrsMap['render-prop-fn']) && 57 | ast.parent 58 | ) { 59 | let slotname = ast.attrsMap['render-prop'] || ast.attrsMap['render-prop-fn'] 60 | if (slotname && ast.parent) { 61 | // Get modules imported for slots 62 | let importModules = processAttrs(ast, options, true) 63 | importObj.imports = _.union(importObj.imports, importModules) 64 | if (parent) { 65 | delete parent.children[childIndex] 66 | } 67 | } 68 | } 69 | if (ast.children) { 70 | ast.children.forEach((child, index) => { 71 | if (child.ifConditions) { 72 | child.ifConditions.forEach((condition, conditionIdx) => { 73 | traverse(condition.block, options, importObj, child, conditionIdx) 74 | }) 75 | } 76 | traverse(child, options, importObj, ast, index) 77 | }) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/parser/filter-parser.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import { COMMON } from 'vue-native/compiler/config' 3 | 4 | const validDivisionCharRE = /[\w).+\-_$\]]/ 5 | 6 | export function parseFilters(exp: string): string { 7 | let inSingle = false 8 | let inDouble = false 9 | let inTemplateString = false 10 | let inRegex = false 11 | let curly = 0 12 | let square = 0 13 | let paren = 0 14 | let lastFilterIndex = 0 15 | let c, prev, i, expression, filters 16 | 17 | for (i = 0; i < exp.length; i++) { 18 | prev = c 19 | c = exp.charCodeAt(i) 20 | if (inSingle) { 21 | if (c === 0x27 && prev !== 0x5c) inSingle = false 22 | } else if (inDouble) { 23 | if (c === 0x22 && prev !== 0x5c) inDouble = false 24 | } else if (inTemplateString) { 25 | if (c === 0x60 && prev !== 0x5c) inTemplateString = false 26 | } else if (inRegex) { 27 | if (c === 0x2f && prev !== 0x5c) inRegex = false 28 | } else if ( 29 | c === 0x7c && // pipe 30 | exp.charCodeAt(i + 1) !== 0x7c && 31 | exp.charCodeAt(i - 1) !== 0x7c && 32 | !curly && 33 | !square && 34 | !paren 35 | ) { 36 | if (expression === undefined) { 37 | // first filter, end of expression 38 | lastFilterIndex = i + 1 39 | expression = exp.slice(0, i).trim() 40 | } else { 41 | pushFilter() 42 | } 43 | } else { 44 | switch (c) { 45 | case 0x22: 46 | inDouble = true 47 | break // " 48 | case 0x27: 49 | inSingle = true 50 | break // ' 51 | case 0x60: 52 | inTemplateString = true 53 | break // ` 54 | case 0x28: 55 | paren++ 56 | break // ( 57 | case 0x29: 58 | paren-- 59 | break // ) 60 | case 0x5b: 61 | square++ 62 | break // [ 63 | case 0x5d: 64 | square-- 65 | break // ] 66 | case 0x7b: 67 | curly++ 68 | break // { 69 | case 0x7d: 70 | curly-- 71 | break // } 72 | } 73 | if (c === 0x2f) { 74 | // / 75 | let j = i - 1 76 | let p 77 | // find first non-whitespace prev char 78 | for (; j >= 0; j--) { 79 | p = exp.charAt(j) 80 | if (p !== ' ') break 81 | } 82 | if (!p || !validDivisionCharRE.test(p)) { 83 | inRegex = true 84 | } 85 | } 86 | } 87 | } 88 | 89 | if (expression === undefined) { 90 | expression = exp.slice(0, i).trim() 91 | } else if (lastFilterIndex !== 0) { 92 | pushFilter() 93 | } 94 | 95 | function pushFilter() { 96 | ;(filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim()) 97 | lastFilterIndex = i + 1 98 | } 99 | 100 | if (filters) { 101 | for (i = 0; i < filters.length; i++) { 102 | expression = wrapFilter(expression, filters[i]) 103 | } 104 | } 105 | 106 | return expression 107 | } 108 | 109 | function wrapFilter(exp: string, filter: string): string { 110 | const i = filter.indexOf('(') 111 | if (i < 0) { 112 | // _f: resolveFilter 113 | return `${COMMON.resolveFilter.name}.call(vm, "${filter}")(${exp})` 114 | } else { 115 | const name = filter.slice(0, i) 116 | const args = filter.slice(i + 1) 117 | return `${COMMON.resolveFilter.name}.call(vm, "${name}")(${exp},${args}` 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/parser/text-parser.js: -------------------------------------------------------------------------------- 1 | import { cached } from 'shared/util' 2 | import { parseFilters } from './filter-parser' 3 | import { COMMON } from 'vue-native/compiler/config' 4 | 5 | const defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g 6 | const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g 7 | 8 | const buildRegex = cached(delimiters => { 9 | const open = delimiters[0].replace(regexEscapeRE, '\\amp;') 10 | const close = delimiters[1].replace(regexEscapeRE, '\\amp;') 11 | return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') 12 | }) 13 | 14 | export function parseText(text, delimiters) { 15 | const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE 16 | if (!tagRE.test(text)) { 17 | return 18 | } 19 | const tokens = [] 20 | let lastIndex = (tagRE.lastIndex = 0) 21 | let match, index 22 | while ((match = tagRE.exec(text))) { 23 | index = match.index 24 | // push text token 25 | if (index > lastIndex) { 26 | tokens.push(JSON.stringify(text.slice(lastIndex, index))) 27 | } 28 | // tag token 29 | const exp = parseFilters(match[1].trim()) 30 | tokens.push(`${COMMON.toString.name}(${exp})`) 31 | lastIndex = index + match[0].length 32 | } 33 | if (lastIndex < text.length) { 34 | tokens.push(JSON.stringify(text.slice(lastIndex))) 35 | } 36 | return tokens.join('+') 37 | } 38 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/property/ARIADOMPropertyConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | var ARIADOMPropertyConfig = { 12 | Properties: { 13 | // Global States and Properties 14 | 'aria-current': 0, // state 15 | 'aria-details': 0, 16 | 'aria-disabled': 0, // state 17 | 'aria-hidden': 0, // state 18 | 'aria-invalid': 0, // state 19 | 'aria-keyshortcuts': 0, 20 | 'aria-label': 0, 21 | 'aria-roledescription': 0, 22 | // Widget Attributes 23 | 'aria-autocomplete': 0, 24 | 'aria-checked': 0, 25 | 'aria-expanded': 0, 26 | 'aria-haspopup': 0, 27 | 'aria-level': 0, 28 | 'aria-modal': 0, 29 | 'aria-multiline': 0, 30 | 'aria-multiselectable': 0, 31 | 'aria-orientation': 0, 32 | 'aria-placeholder': 0, 33 | 'aria-pressed': 0, 34 | 'aria-readonly': 0, 35 | 'aria-required': 0, 36 | 'aria-selected': 0, 37 | 'aria-sort': 0, 38 | 'aria-valuemax': 0, 39 | 'aria-valuemin': 0, 40 | 'aria-valuenow': 0, 41 | 'aria-valuetext': 0, 42 | // Live Region Attributes 43 | 'aria-atomic': 0, 44 | 'aria-busy': 0, 45 | 'aria-live': 0, 46 | 'aria-relevant': 0, 47 | // Drag-and-Drop Attributes 48 | 'aria-dropeffect': 0, 49 | 'aria-grabbed': 0, 50 | // Relationship Attributes 51 | 'aria-activedescendant': 0, 52 | 'aria-colcount': 0, 53 | 'aria-colindex': 0, 54 | 'aria-colspan': 0, 55 | 'aria-controls': 0, 56 | 'aria-describedby': 0, 57 | 'aria-errormessage': 0, 58 | 'aria-flowto': 0, 59 | 'aria-labelledby': 0, 60 | 'aria-owns': 0, 61 | 'aria-posinset': 0, 62 | 'aria-rowcount': 0, 63 | 'aria-rowindex': 0, 64 | 'aria-rowspan': 0, 65 | 'aria-setsize': 0, 66 | }, 67 | DOMAttributeNames: {}, 68 | DOMPropertyNames: {}, 69 | } 70 | 71 | export default ARIADOMPropertyConfig 72 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/property/EventConstant.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | /** 12 | * Types of raw signals from the browser caught at the top level. 13 | */ 14 | var topLevelTypes = { 15 | topAbort: null, 16 | topAnimationEnd: null, 17 | topAnimationIteration: null, 18 | topAnimationStart: null, 19 | topBlur: null, 20 | topCanPlay: null, 21 | topCanPlayThrough: null, 22 | topChange: null, 23 | topClick: null, 24 | topCompositionEnd: null, 25 | topCompositionStart: null, 26 | topCompositionUpdate: null, 27 | topContextMenu: null, 28 | topCopy: null, 29 | topCut: null, 30 | topDoubleClick: null, 31 | topDrag: null, 32 | topDragEnd: null, 33 | topDragEnter: null, 34 | topDragExit: null, 35 | topDragLeave: null, 36 | topDragOver: null, 37 | topDragStart: null, 38 | topDrop: null, 39 | topDurationChange: null, 40 | topEmptied: null, 41 | topEncrypted: null, 42 | topEnded: null, 43 | topError: null, 44 | topFocus: null, 45 | topInput: null, 46 | topInvalid: null, 47 | topKeyDown: null, 48 | topKeyPress: null, 49 | topKeyUp: null, 50 | topLoad: null, 51 | topLoadedData: null, 52 | topLoadedMetadata: null, 53 | topLoadStart: null, 54 | topMouseDown: null, 55 | topMouseMove: null, 56 | topMouseOut: null, 57 | topMouseOver: null, 58 | topMouseUp: null, 59 | topPaste: null, 60 | topPause: null, 61 | topPlay: null, 62 | topPlaying: null, 63 | topProgress: null, 64 | topRateChange: null, 65 | topReset: null, 66 | topScroll: null, 67 | topSeeked: null, 68 | topSeeking: null, 69 | topSelectionChange: null, 70 | topStalled: null, 71 | topSubmit: null, 72 | topSuspend: null, 73 | topTextInput: null, 74 | topTimeUpdate: null, 75 | topTouchCancel: null, 76 | topTouchEnd: null, 77 | topTouchMove: null, 78 | topTouchStart: null, 79 | topTransitionEnd: null, 80 | topVolumeChange: null, 81 | topWaiting: null, 82 | topWheel: null, 83 | } 84 | 85 | var EventConstants = { 86 | topLevelTypes: topLevelTypes, 87 | } 88 | 89 | export default EventConstants 90 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/property/ReactProps.js: -------------------------------------------------------------------------------- 1 | var reactProps = { 2 | children: true, 3 | dangerouslySetInnerHTML: true, 4 | key: true, 5 | ref: true, 6 | 7 | autoFocus: true, 8 | defaultValue: true, 9 | valueLink: true, 10 | defaultChecked: true, 11 | checkedLink: true, 12 | innerHTML: true, 13 | suppressContentEditableWarning: true, 14 | onFocusIn: true, 15 | onFocusOut: true, 16 | } 17 | 18 | export default reactProps 19 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/property/index.js: -------------------------------------------------------------------------------- 1 | import ARIADOMPropertyConfig from './ARIADOMPropertyConfig' 2 | import HTMLDOMPropertyConfig from './HTMLDOMPropertyConfig' 3 | import SVGDOMPropertyConfig from './SVGDOMPropertyConfig' 4 | import EventConstant from './EventConstant' 5 | import ReactProps from './ReactProps' 6 | 7 | const propertyMap = {} 8 | 9 | Object.keys(ARIADOMPropertyConfig.Properties).forEach(v => { 10 | propertyMap[v.toLowerCase()] = v 11 | }) 12 | 13 | Object.keys(HTMLDOMPropertyConfig.Properties).forEach(v => { 14 | propertyMap[v.toLowerCase()] = v 15 | }) 16 | 17 | Object.keys(SVGDOMPropertyConfig.DOMAttributeNames).forEach(v => { 18 | propertyMap[SVGDOMPropertyConfig.DOMAttributeNames[v]] = v 19 | }) 20 | 21 | Object.keys(EventConstant.topLevelTypes) 22 | .map(v => { 23 | return v.replace(/^top/, 'on') 24 | }) 25 | .forEach(v => { 26 | propertyMap[v.toLowerCase()] = v 27 | }) 28 | 29 | Object.keys(ReactProps).map(v => { 30 | propertyMap[v.toLowerCase()] = v 31 | }) 32 | 33 | export default propertyMap 34 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/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 = ( 12 | tag: string, 13 | type: ?string, 14 | attr: string, 15 | ): boolean => { 16 | return ( 17 | (attr === 'value' && acceptValue(tag) && type !== 'button') || 18 | (attr === 'selected' && tag === 'option') || 19 | (attr === 'checked' && tag === 'input') || 20 | (attr === 'muted' && tag === 'video') 21 | ) 22 | } 23 | 24 | export const isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck') 25 | 26 | export const isBooleanAttr = makeMap( 27 | 'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' + 28 | 'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' + 29 | 'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' + 30 | 'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' + 31 | 'required,reversed,scoped,seamless,selected,sortable,translate,' + 32 | 'truespeed,typemustmatch,visible', 33 | ) 34 | 35 | export const xlinkNS = 'http://www.w3.org/1999/xlink' 36 | 37 | export const isXlink = (name: string): boolean => { 38 | return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' 39 | } 40 | 41 | export const getXlinkProp = (name: string): string => { 42 | return isXlink(name) ? name.slice(6, name.length) : '' 43 | } 44 | 45 | export const isFalsyAttrValue = (val: any): boolean => { 46 | return val == null || val === false 47 | } 48 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/util/index.js: -------------------------------------------------------------------------------- 1 | export * from './attrs' 2 | export * from './element' 3 | -------------------------------------------------------------------------------- /src/platforms/vue-native/compiler/web.js: -------------------------------------------------------------------------------- 1 | import { parse } from 'compiler/parser/index' 2 | 3 | import { WebRenderGenerator } from 'vue-native/compiler/codegen/index' 4 | 5 | import { 6 | isPreTag, 7 | isUnaryTag, 8 | canBeLeftOpenTag, 9 | isReservedTag, 10 | getTagNamespace, 11 | } from './util/index' 12 | 13 | const baseOptions = { 14 | expectHTML: true, 15 | isPreTag, 16 | isUnaryTag, 17 | canBeLeftOpenTag, 18 | isReservedTag, 19 | getTagNamespace, 20 | } 21 | 22 | export function compile(template, options) { 23 | let ast 24 | let code 25 | template = template.trim() 26 | if (template) { 27 | ast = parse(template, Object.assign({}, baseOptions, options)) 28 | const renderer = new WebRenderGenerator(ast, options) 29 | code = renderer.generate() 30 | } else { 31 | code = 'export default () => null' 32 | } 33 | return { 34 | ast, 35 | code, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/platforms/vue-native/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | import Vue from './runtime/index' 4 | import observer from './observer' 5 | 6 | Vue.observer = observer 7 | 8 | export default Vue 9 | -------------------------------------------------------------------------------- /src/platforms/vue-native/observer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Reference to mobx https://github.com/mobxjs/mobx-react-vue/blob/master/src/observer.js 3 | */ 4 | 5 | import React from 'react' 6 | import Watcher from 'core/observer/watcher' 7 | 8 | export default function observer(componentClass) { 9 | if ( 10 | typeof componentClass === 'function' && 11 | (!componentClass.prototype || !componentClass.prototype.render) && 12 | !componentClass.isReactClass && 13 | // eslint-disable-next-line no-prototype-builtins 14 | !React.Component.isPrototypeOf(componentClass) 15 | ) { 16 | class ObserverComponent extends React.Component { 17 | render() { 18 | return componentClass.call(this, this.props, this.context) 19 | } 20 | } 21 | ObserverComponent.displayName = 22 | componentClass.displayName || componentClass.name 23 | ObserverComponent.contextTypes = componentClass.contextTypes 24 | ObserverComponent.propTypes = componentClass.propTypes 25 | ObserverComponent.defaultProps = componentClass.defaultProps 26 | return observer(ObserverComponent) 27 | } 28 | 29 | if (!componentClass) { 30 | throw new Error("Please pass a valid component to 'observer'") 31 | } 32 | 33 | const target = componentClass.prototype || componentClass 34 | mixinLifecycleEvents(target) 35 | return componentClass 36 | } 37 | 38 | function mixinLifecycleEvents(target) { 39 | for (const key in lifecycleMixin) { 40 | if ( 41 | key === 'shouldComponentUpdate' && 42 | typeof target.shouldComponentUpdate === 'function' 43 | ) { 44 | continue 45 | } 46 | patch(target, key) 47 | } 48 | } 49 | 50 | const lifecycleMixin = { 51 | UNSAFE_componentWillMount() { 52 | const cb = this.forceUpdate.bind(this) 53 | const render = this.render.bind(this) 54 | const watcher = new Watcher({ _watchers: [] }, render, cb, { lazy: true }) 55 | this.render = watcher.get.bind(watcher) 56 | watcher.lazy = false 57 | watcher.run = cb 58 | this.$vuewatcher = watcher 59 | }, 60 | componentWillUnmount() { 61 | this.$vuewatcher.teardown() 62 | }, 63 | shouldComponentUpdate(nextProps, nextState) { 64 | if (this.state !== nextState) { 65 | return true 66 | } 67 | return isObjectShallowModified(this.props, nextProps) 68 | }, 69 | } 70 | 71 | function patch(target, funcName) { 72 | const base = target[funcName] 73 | const mixinFunc = lifecycleMixin[funcName] 74 | target[funcName] = !base 75 | ? function() { 76 | return mixinFunc.apply(this, arguments) 77 | } 78 | : function() { 79 | mixinFunc.apply(this, arguments) 80 | return base.apply(this, arguments) 81 | } 82 | } 83 | 84 | function isObjectShallowModified(prev, next) { 85 | if ( 86 | prev == null || 87 | next == null || 88 | typeof prev !== 'object' || 89 | typeof next !== 'object' 90 | ) { 91 | return prev !== next 92 | } 93 | const keys = Object.keys(prev) 94 | if (keys.length !== Object.keys(next).length) { 95 | return true 96 | } 97 | let key 98 | for (let i = keys.length - 1; i >= 0; i--) { 99 | key = keys[i] 100 | if (next[key] !== prev[key]) { 101 | return true 102 | } 103 | } 104 | return false 105 | } 106 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/components/buildInputComponent.js: -------------------------------------------------------------------------------- 1 | import { WEB } from 'vue-native/compiler/config' 2 | 3 | import { buildMixin } from './buildMixin' 4 | 5 | import { handleProps } from '../render-helpers/handleProps' 6 | 7 | export function buildInputComponent(Component, createElement) { 8 | return class Input extends buildMixin.apply(this, arguments) { 9 | constructor(props) { 10 | super(props) 11 | this.state = { 12 | props: {}, 13 | } 14 | } 15 | buildStateProps(props) { 16 | const stateProps = super.buildStateProps(props) 17 | 18 | const onChangeFn = 19 | stateProps.onChange || stateProps.onInput || function() {} 20 | stateProps.onChange = event => { 21 | this.setState({ 22 | props: Object.assign({}, this.state.props, { 23 | value: event.target.value, 24 | }), 25 | }) 26 | return onChangeFn(event) 27 | } 28 | 29 | return handleProps(stateProps, props[WEB.inputComponent.tag]) 30 | } 31 | setStateProps(props) { 32 | const stateProps = this.buildStateProps(props) 33 | this.setState({ 34 | props: stateProps, 35 | }) 36 | } 37 | UNSAFE_componentWillMount() { 38 | this.setStateProps(this.props) 39 | } 40 | UNSAFE_componentWillReceiveProps(nextProps) { 41 | this.setStateProps(nextProps) 42 | } 43 | render() { 44 | return createElement( 45 | this.props[WEB.inputComponent.tag], 46 | this.state.props, 47 | this.state.props.children, 48 | ) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/components/buildMixin.js: -------------------------------------------------------------------------------- 1 | export function buildMixin(Component) { 2 | return class Mixin extends Component { 3 | constructor(props) { 4 | super(props) 5 | /** 6 | * for vue, every component should have a ref to represent node element 7 | */ 8 | this._ref = null 9 | } 10 | 11 | setRef(ref) { 12 | if (ref) { 13 | if (ref._reactInternalInstance && ref.vm === undefined) { 14 | this._ref = ref._ref || ref 15 | } else { 16 | this._ref = ref.vm || ref 17 | } 18 | } 19 | } 20 | 21 | buildStateProps(props) { 22 | const stateProps = Object.assign({}, props) 23 | const originRef = stateProps.ref 24 | stateProps.ref = ref => { 25 | this.setRef(ref) 26 | if (typeof originRef === 'function') { 27 | return originRef(ref) 28 | } else { 29 | return ref 30 | } 31 | } 32 | 33 | return stateProps 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/components/buildWebEmptyComponent.js: -------------------------------------------------------------------------------- 1 | export function buildWebEmptyComponent(Component, createElement) { 2 | return class EmptyComponent extends Component { 3 | constructor(props) { 4 | super(props) 5 | this._ref = null 6 | this.state = { 7 | props: {}, 8 | } 9 | } 10 | setRef(ref) { 11 | this._ref = ref 12 | } 13 | buildStateProps(props) { 14 | const stateProps = Object.assign({}, props) 15 | const originRef = stateProps.ref 16 | stateProps.ref = ref => { 17 | this.setRef(ref) 18 | if (typeof originRef === 'function') { 19 | return originRef(ref) 20 | } else { 21 | return ref 22 | } 23 | } 24 | return stateProps 25 | } 26 | UNSAFE_componentWillMount() { 27 | this.setState({ 28 | props: this.buildStateProps(this.props), 29 | }) 30 | } 31 | UNSAFE_componentWillReceiveProps(nextProps) { 32 | this.setState({ 33 | props: this.buildStateProps(nextProps), 34 | }) 35 | } 36 | unwrap(wrapper) { 37 | if (wrapper.parentNode) { 38 | const docFrag = document.createDocumentFragment() 39 | while (wrapper.firstChild) { 40 | const child = wrapper.removeChild(wrapper.firstChild) 41 | docFrag.appendChild(child) 42 | } 43 | wrapper.parentNode.replaceChild(docFrag, wrapper) 44 | } 45 | } 46 | componentDidMount() { 47 | // this.unwrap(this._ref) 48 | } 49 | componentDidUpdate() { 50 | // this.unwrap(this._ref) 51 | } 52 | render() { 53 | const { tag, children } = this.state.props 54 | return createElement(tag || 'view', this.state.props, children) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/components/buildWebInputComponent.js: -------------------------------------------------------------------------------- 1 | import { buildInputComponent } from './buildInputComponent' 2 | 3 | const buildWebInputComponent = buildInputComponent 4 | 5 | export { buildWebInputComponent } 6 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/components/index.js: -------------------------------------------------------------------------------- 1 | export { buildComponent } from './buildComponent' 2 | export { buildDirective } from './buildDirective' 3 | export { buildWebTransition } from './buildWebTransition' 4 | export { buildWebEmptyComponent } from './buildWebEmptyComponent' 5 | export { buildWebInputComponent } from './buildWebInputComponent' 6 | export { buildNativeComponent } from './buildNativeComponent' 7 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | 3 | export default { 4 | model, 5 | } 6 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/directives/model.js: -------------------------------------------------------------------------------- 1 | import { looseEqual, looseIndexOf } from 'shared/util' 2 | import { warn } from 'core/util/index' 3 | 4 | function setSelected(el, binding, vm) { 5 | const value = binding.value 6 | const isMultiple = el.multiple 7 | if (isMultiple && !Array.isArray(value)) { 8 | process.env.NODE_ENV !== 'production' && 9 | warn( 10 | `<select multiple v-model="${binding.expression}"> ` + 11 | `expects an Array value for its binding, but got ${Object.prototype.toString 12 | .call(value) 13 | .slice(8, -1)}`, 14 | vm, 15 | ) 16 | return 17 | } 18 | let selected, option 19 | for (let i = 0, l = el.options.length; i < l; i++) { 20 | option = el.options[i] 21 | if (isMultiple) { 22 | selected = looseIndexOf(value, option.value) > -1 23 | if (option.selected !== selected) { 24 | option.selected = selected 25 | } 26 | } else { 27 | if (looseEqual(option.value, value)) { 28 | if (el.selectedIndex !== i) { 29 | el.selectedIndex = i 30 | } 31 | return 32 | } 33 | } 34 | } 35 | if (!isMultiple) { 36 | el.selectedIndex = -1 37 | } 38 | } 39 | 40 | function setCheckBox(el, binding) { 41 | let value = binding.value 42 | try { 43 | value = JSON.parse(value) 44 | } catch (e) {} 45 | if (value) { 46 | el.setAttribute('checked', value) 47 | } else { 48 | el.removeAttribute('checked') 49 | } 50 | } 51 | 52 | export default { 53 | bind(el, binding) { 54 | if (el.tagName) { 55 | const lowerTagName = el.tagName.toLowerCase() 56 | if (lowerTagName === 'select') { 57 | setSelected(el, binding, this) 58 | } else if (lowerTagName === 'input') { 59 | if (el.getAttribute('type') === 'checkbox') { 60 | setCheckBox(el, binding) 61 | } 62 | } 63 | } 64 | }, 65 | update(el, binding) { 66 | if (el.tagName) { 67 | const lowerTagName = el.tagName.toLowerCase() 68 | if (lowerTagName === 'select') { 69 | setSelected(el, binding, this) 70 | } else if (lowerTagName === 'input') { 71 | if (el.getAttribute('type') === 'checkbox') { 72 | setCheckBox(el, binding) 73 | } 74 | } 75 | } 76 | }, 77 | } 78 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/helpers.js: -------------------------------------------------------------------------------- 1 | import platformDirectives from './directives/index.js' 2 | 3 | export { platformDirectives } 4 | export * from './render-helpers/index.js' 5 | // export * from './buildComponent.js' 6 | export * from './components/index.js' 7 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'core/index' 2 | 3 | import { lifeCycleMixin } from './lifeCycle' 4 | import { renderMixin } from './render' 5 | 6 | lifeCycleMixin(Vue) 7 | renderMixin(Vue) 8 | 9 | export default Vue 10 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/lifeCycle.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { remove } from 'core/util/index' 4 | 5 | export function lifeCycleMixin(Vue: Class<Component>) { 6 | Vue.prototype.$destroy = function() { 7 | const vm: Component = this 8 | if (vm._isBeingDestroyed) { 9 | return 10 | } 11 | // callHook(vm, 'beforeDestroy') 12 | vm._isBeingDestroyed = true 13 | // remove self from parent 14 | const parent = vm.$parent 15 | if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { 16 | remove(parent.$children, vm) 17 | } 18 | // teardown watchers 19 | if (vm._watcher) { 20 | vm._watcher.teardown() 21 | } 22 | let i = vm._watchers.length 23 | while (i--) { 24 | vm._watchers[i].teardown() 25 | } 26 | // remove reference from data ob 27 | // frozen object may not have observer. 28 | if (vm._data.__ob__) { 29 | vm._data.__ob__.vmCount-- 30 | } 31 | // call the last hook... 32 | vm._isDestroyed = true 33 | // invoke destroy hooks on current rendered tree 34 | // vm.__patch__(vm._vnode, null) 35 | // fire destroyed hook 36 | // callHook(vm, 'destroyed') 37 | // turn off all instance listeners. 38 | vm.$off() 39 | // remove __vue__ reference 40 | if (vm.$el) { 41 | vm.$el.__vue__ = null 42 | } 43 | // remove reference to DOM nodes (prevents leak) 44 | vm.$options._parentElm = vm.$options._refElm = null 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/bindNativeClass.js: -------------------------------------------------------------------------------- 1 | function classBinding(c) { 2 | const type = Object.prototype.toString.call(c) 3 | if (type === '[object Object]') { 4 | return Object.keys(c).filter(k => { 5 | return !!c[k] 6 | }) 7 | } else if (type === '[object Array]') { 8 | return c 9 | .map(v => { 10 | return classBinding(v) 11 | }) 12 | .reduce((acc, val) => { 13 | return acc.concat(val) 14 | }, []) 15 | } 16 | return c 17 | } 18 | 19 | export function bindNativeClass(obj) { 20 | let arr = [] 21 | const style = [] 22 | if (obj.dynamicClass) { 23 | arr = arr.concat(classBinding(obj.dynamicClass)) 24 | } 25 | if (obj.staticClass) { 26 | arr = arr.concat(obj.staticClass.split(/\s+/)) 27 | } 28 | arr.forEach(v => { 29 | // commit cf4d099 change moved here 30 | style.push(this.css[v]) 31 | }) 32 | if (obj.parentClass) { 33 | style.push(obj.parentClass) 34 | } 35 | return style 36 | } 37 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/bindNativeStyle.js: -------------------------------------------------------------------------------- 1 | export function bindNativeStyle(styleBinding, staticStyle, showStyle) { 2 | if (styleBinding === undefined) { 3 | styleBinding = {} 4 | } 5 | staticStyle = Object.assign({}, staticStyle, showStyle) 6 | if (staticStyle.display === '') { 7 | delete showStyle.display 8 | } 9 | const type = Object.prototype.toString.call(styleBinding) 10 | if (type === '[object Object]') { 11 | return Object.assign({}, styleBinding, staticStyle) 12 | } else if (type === '[object Array]') { 13 | return styleBinding 14 | .map(v => { 15 | return bindNativeStyle(v, staticStyle, showStyle) 16 | }) 17 | .reduce((acc, val) => Object.assign(acc, val), {}) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/bindWebClass.js: -------------------------------------------------------------------------------- 1 | export function bindWebClass(c) { 2 | const type = Object.prototype.toString.call(c) 3 | if (type === '[object Object]') { 4 | return Object.keys(c) 5 | .filter(k => { 6 | return !!c[k] 7 | }) 8 | .join(' ') 9 | } else if (type === '[object Array]') { 10 | return c 11 | .map(v => { 12 | return bindWebClass(v) 13 | }) 14 | .join(' ') 15 | } 16 | return c 17 | } 18 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/bindWebStyle.js: -------------------------------------------------------------------------------- 1 | import { cached, camelize } from 'shared/util' 2 | 3 | const prefixes = ['Webkit', 'Moz', 'ms'] 4 | 5 | let testEl 6 | const normalize = cached(function(prop) { 7 | testEl = testEl || document.createElement('div') 8 | prop = camelize(prop) 9 | if (prop !== 'filter' && prop in testEl.style) { 10 | return prop 11 | } 12 | const upper = prop.charAt(0).toUpperCase() + prop.slice(1) 13 | for (let i = 0; i < prefixes.length; i++) { 14 | const prefixed = prefixes[i] + upper 15 | if (prefixed in testEl.style) { 16 | return prefixed 17 | } 18 | } 19 | }) 20 | 21 | export function bindWebStyle(styleBinding, staticStyle, showStyle) { 22 | if (styleBinding === undefined) { 23 | styleBinding = {} 24 | } 25 | staticStyle = Object.assign({}, staticStyle, showStyle) 26 | const type = Object.prototype.toString.call(styleBinding) 27 | if (type === '[object Object]') { 28 | const normalizedStyle = {} 29 | styleBinding = Object.assign({}, styleBinding, staticStyle) 30 | for (const key in styleBinding) { 31 | normalizedStyle[normalize(key)] = styleBinding[key] 32 | } 33 | return normalizedStyle 34 | } else if (type === '[object Array]') { 35 | return styleBinding 36 | .map(v => { 37 | return bindWebStyle(v, staticStyle, showStyle) 38 | }) 39 | .reduce((acc, val) => Object.assign(acc, val), {}) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/checkKeyCodes.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import { warn } from 'core/util/index' 3 | /** 4 | * Runtime helper for checking keyCodes. 5 | */ 6 | export function checkKeyCodes( 7 | vm: Object, 8 | eventKeyCode: number, 9 | key: string, 10 | builtInAlias: number | Array<number> | void, 11 | ): boolean { 12 | let configKeyCodes = {} 13 | try { 14 | configKeyCodes = vm.$options._base.config.keyCodes 15 | } catch (e) { 16 | warn( 17 | 'vue-native checkKeyCodes vm.$options._base.config.keyCodes catch error', 18 | ) 19 | } 20 | const keyCodes = configKeyCodes[key] || builtInAlias 21 | if (Array.isArray(keyCodes)) { 22 | return keyCodes.indexOf(eventKeyCode) === -1 23 | } else { 24 | return keyCodes !== eventKeyCode 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/dynamicComponent.js: -------------------------------------------------------------------------------- 1 | import { pascalCaseTag } from '../components/util.js' 2 | 3 | export function dynamicComponent(vm, name) { 4 | let componentName 5 | if (typeof name === 'string') { 6 | componentName = vm.$options.components[pascalCaseTag(name)] 7 | } else { 8 | componentName = name 9 | } 10 | return componentName 11 | } 12 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/event.js: -------------------------------------------------------------------------------- 1 | export function event(fn) { 2 | if (Array.isArray(fn)) { 3 | return function() { 4 | return fn.map(v => v.apply(this, arguments)) 5 | } 6 | } else { 7 | return fn 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/handleProps.js: -------------------------------------------------------------------------------- 1 | import { HELPER_HEADER } from 'vue-native/compiler/constants' 2 | import propertyMap from 'vue-native/compiler/property/index' 3 | import { isReservedTag, isUnaryTag } from 'vue-native/compiler/util/index' 4 | 5 | export function handleProps(props, tag) { 6 | let handledProps = {} 7 | if (typeof tag === 'string' && isReservedTag(tag)) { 8 | for (const key in props) { 9 | const prop = propertyMap[key.toLowerCase()] 10 | if ( 11 | prop && 12 | props[key] !== undefined && 13 | key.indexOf(HELPER_HEADER) !== 0 14 | ) { 15 | handledProps[prop] = props[key] 16 | } 17 | } 18 | } else { 19 | handledProps = props 20 | } 21 | if (isUnaryTag(tag)) { 22 | delete handledProps.children 23 | } 24 | return handledProps 25 | } 26 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | export * from 'shared/util' 5 | export { renderList } from './renderList' 6 | export { renderSlot } from './renderSlot' 7 | export { bindWebClass } from './bindWebClass' 8 | export { bindNativeClass } from './bindNativeClass' 9 | export { bindWebStyle } from './bindWebStyle' 10 | export { bindNativeStyle } from './bindNativeStyle' 11 | export { mergeNativeStyleAndNativeClass } from './mergeNativeStyleAndNativeClass' 12 | export { checkKeyCodes } from './checkKeyCodes' 13 | export { template } from './template' 14 | export { event } from './event' 15 | export { mergeCssModule } from './mergeCssModule' 16 | export { dynamicComponent } from './dynamicComponent' 17 | export { resolveFilter } from './resolveFilter' 18 | export { handleProps } from './handleProps' 19 | export { mergeProps } from './mergeProps' 20 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/mergeCssModule.js: -------------------------------------------------------------------------------- 1 | export function mergeCssModule(computed, cssModules) { 2 | const _computed = Object.create(computed || null) 3 | Object.keys(cssModules).forEach(function(key) { 4 | var module = cssModules[key] 5 | _computed[key] = function() { 6 | return module 7 | } 8 | }) 9 | return _computed 10 | } 11 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/mergeNativeStyleAndNativeClass.js: -------------------------------------------------------------------------------- 1 | export function mergeNativeStyleAndNativeClass(nativeClass, nativeStyle) { 2 | let resultant = [] 3 | if (nativeClass) { 4 | if (Object.prototype.toString.call(nativeClass) === '[object Array]') { 5 | nativeClass.forEach(function(classObj) { 6 | resultant.push(classObj) 7 | }) 8 | } else if ( 9 | Object.prototype.toString.call(nativeClass) === '[object Object]' 10 | ) { 11 | resultant.push(nativeClass) 12 | } 13 | } 14 | if (nativeStyle) { 15 | if (Object.prototype.toString.call(nativeStyle) === '[object Array]') { 16 | nativeStyle.forEach(function(classObj) { 17 | resultant.push(classObj) 18 | }) 19 | } else if ( 20 | Object.prototype.toString.call(nativeStyle) === '[object Object]' 21 | ) { 22 | resultant.push(nativeStyle) 23 | } 24 | } 25 | return resultant 26 | } 27 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/mergeProps.js: -------------------------------------------------------------------------------- 1 | export function mergeProps() { 2 | const args = Array.prototype.slice.call(arguments, 0).filter(v => v) 3 | const obj = {} 4 | args.forEach(o => { 5 | Object.keys(o).forEach(k => { 6 | if (!obj[k]) { 7 | obj[k] = [] 8 | } 9 | obj[k].push(o[k]) 10 | }) 11 | }) 12 | for (const k in obj) { 13 | const l = obj[k].length 14 | if (l === 1) { 15 | obj[k] = obj[k][0] 16 | } else if (l > 1) { 17 | const _p = obj[k] 18 | if (typeof _p[0] === 'function') { 19 | obj[k] = function() { 20 | for (let i = 0; i < l; i++) { 21 | typeof _p[i] === 'function' && _p[i].apply(this, arguments) 22 | } 23 | }.bind(this) 24 | } else { 25 | obj[k] = obj[k][l - 1] 26 | } 27 | } 28 | } 29 | return obj 30 | } 31 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/renderList.js: -------------------------------------------------------------------------------- 1 | export { renderList } from 'core/instance/render-helpers/render-list' 2 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/renderSlot.js: -------------------------------------------------------------------------------- 1 | import { COMMON } from 'vue-native/compiler/config' 2 | 3 | export function renderSlot(names, children) { 4 | const hitSlot = {} 5 | const defaultSlot = [] 6 | if (children == null) { 7 | return () => {} 8 | } 9 | if (!Array.isArray(children)) { 10 | children = [children] 11 | } 12 | children = children.filter(v => v != null) 13 | children.forEach(v => { 14 | if (v.type === COMMON.template.type) { 15 | if (v['dataSlot'] === undefined) { 16 | defaultSlot.push(v.render) 17 | } 18 | return 19 | } 20 | if (v.props === undefined || v.props['dataSlot'] === undefined) { 21 | defaultSlot.push(v) 22 | } 23 | }) 24 | names.forEach(v => { 25 | children.forEach(_v => { 26 | if (typeof _v === 'string' || typeof _v === 'number') { 27 | return 28 | } 29 | if (_v.type === COMMON.template.type) { 30 | if (v === _v['dataSlot']) { 31 | hitSlot[v] = _v.render 32 | } 33 | return 34 | } 35 | if (v === _v.props['dataSlot']) { 36 | hitSlot[v] = _v 37 | } 38 | return 39 | }) 40 | }) 41 | function render(name, props) { 42 | let target 43 | if (name === undefined) { 44 | target = defaultSlot.length === 0 ? undefined : defaultSlot 45 | } else { 46 | target = hitSlot[name] 47 | } 48 | if (typeof target === 'function') { 49 | return target(props) 50 | } else if (Array.isArray(target)) { 51 | return target.map(v => { 52 | if (typeof v === 'function') { 53 | return v(props) 54 | } else { 55 | return v 56 | } 57 | }) 58 | } else { 59 | return target 60 | } 61 | } 62 | return render 63 | } 64 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/resolveFilter.js: -------------------------------------------------------------------------------- 1 | export { resolveFilter } from 'core/instance/render-helpers/resolve-filter' 2 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/template.js: -------------------------------------------------------------------------------- 1 | export function template(props) { 2 | return props.children 3 | } 4 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render-helpers/transitionGroupWeb.js: -------------------------------------------------------------------------------- 1 | // unfinished 2 | 3 | export function transitionGroupWeb(Component, createElement) { 4 | return class TransitionGroup extends Component { 5 | render() { 6 | const tag = this.props.tag || 'span' 7 | return createElement(tag, null) 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/platforms/vue-native/runtime/render.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { nextTick } from 'core/util/index' 4 | 5 | export function renderMixin(Vue: Class<Component>) { 6 | Vue.prototype.$nextTick = function(fn: Function) { 7 | return nextTick(fn, this) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/vue-native/scripts/index.js: -------------------------------------------------------------------------------- 1 | import { compileVueToRn } from './compiler' 2 | import { transform } from './transformerPlugin' 3 | 4 | export default { 5 | compileVueToRn, 6 | transform, 7 | } 8 | -------------------------------------------------------------------------------- /src/platforms/vue-native/scripts/transformerPlugin.js: -------------------------------------------------------------------------------- 1 | import semver from 'semver' 2 | import traverse from 'babel-traverse' 3 | import { SourceMapConsumer } from 'source-map' 4 | 5 | import { compileVueToRn as reactVueTemplateParser } from './compiler' 6 | import { version as reactNativeVersionString } from 'react-native/package.json' 7 | 8 | // const reactNativeVersionString = require('react-native/package.json').version; 9 | const reactNativeMinorVersion = semver(reactNativeVersionString).minor 10 | 11 | let upstreamTransformer = null 12 | if (reactNativeMinorVersion >= 59) { 13 | upstreamTransformer = require('metro-react-native-babel-transformer') 14 | } else if (reactNativeMinorVersion >= 56) { 15 | upstreamTransformer = require('metro/src/reactNativeTransformer') 16 | } else if (reactNativeMinorVersion >= 52) { 17 | upstreamTransformer = require('metro/src/transformer') 18 | } else if (reactNativeMinorVersion >= 47) { 19 | upstreamTransformer = require('metro-bundler/src/transformer') 20 | } else if (reactNativeMinorVersion === 46) { 21 | upstreamTransformer = require('metro-bundler/build/transformer') 22 | } else { 23 | // handle RN <= 0.45 24 | var oldUpstreamTransformer = require('react-native/packager/transformer') 25 | upstreamTransformer = { 26 | transform({ src, filename, options }) { 27 | return oldUpstreamTransformer.transform(src, filename, options) 28 | }, 29 | } 30 | } 31 | 32 | function sourceMapAstInPlace(sourceMap, babelAst) { 33 | const consumer = new SourceMapConsumer(sourceMap) 34 | 35 | traverse.cheap(babelAst, node => { 36 | if (node.loc) { 37 | const originalStart = consumer.originalPositionFor(node.loc.start) 38 | // Removed the column mapping with original position as we have only mapped lines during generation 39 | if (originalStart.line) { 40 | node.loc.start.line = originalStart.line 41 | // node.loc.start.column = originalStart.column 42 | } 43 | const originalEnd = consumer.originalPositionFor(node.loc.end) 44 | if (originalEnd.line) { 45 | node.loc.end.line = originalEnd.line 46 | // node.loc.end.column = originalEnd.column 47 | } 48 | } 49 | }) 50 | } 51 | 52 | export function transform({ src, filename, options }) { 53 | if (typeof src === 'object') { 54 | // handle RN >= 0.46 55 | ;({ src, filename, options } = src) 56 | } 57 | const outputFile = reactVueTemplateParser(src) 58 | 59 | if (!outputFile.output) { 60 | return upstreamTransformer.transform({ 61 | src: outputFile, 62 | filename, 63 | options, 64 | }) 65 | } else { 66 | // Source Map support 67 | const babelCompileResult = upstreamTransformer.transform({ 68 | src: outputFile.output, 69 | filename, 70 | options, 71 | }) 72 | if (outputFile.mappings) { 73 | sourceMapAstInPlace(outputFile.mappings, babelCompileResult.ast) 74 | } 75 | return babelCompileResult 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/platforms/vue-native/scripts/util/addvm.js: -------------------------------------------------------------------------------- 1 | import { transform } from 'babel-core' 2 | import constants from './constants' 3 | 4 | const names = 5 | 'Infinity,undefined,NaN,isFinite,isNaN,console,' + 6 | 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + 7 | 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + 8 | 'require,' + // for webpack 9 | 'arguments' // parsed as identifier but is a special keyword... 10 | 11 | const hash = Object.create(null) 12 | names.split(',').forEach(function(name) { 13 | hash[name] = true 14 | }) 15 | 16 | export function addvm(code) { 17 | const r = transform(code, { 18 | plugins: [ 19 | function({ types: t }) { 20 | return { 21 | visitor: { 22 | Identifier: function(path) { 23 | if ( 24 | path.parent.type === 'ObjectProperty' && 25 | path.parent.key === path.node 26 | ) 27 | return 28 | if ( 29 | t.isDeclaration(path.parent.type) && 30 | path.parent.id === path.node 31 | ) 32 | return 33 | if ( 34 | t.isFunction(path.parent.type) && 35 | path.parent.params.indexOf(path.node) > -1 36 | ) 37 | return 38 | if ( 39 | path.parent.type === 'Property' && 40 | path.parent.key === path.node && 41 | !path.parent.computed 42 | ) 43 | return 44 | if ( 45 | path.parent.type === 'MemberExpression' && 46 | path.parent.property === path.node && 47 | !path.parent.computed 48 | ) 49 | return 50 | if (path.parent.type === 'ArrayPattern') return 51 | if (path.parent.type === 'ImportSpecifier') return 52 | if (path.scope.hasBinding(path.node.name)) return 53 | if (hash[path.node.name]) return 54 | if (path.node.name.indexOf(constants.HELPER_HEADER) === 0) return 55 | path.node.name = `vm['${path.node.name}']` 56 | }, 57 | }, 58 | } 59 | }, 60 | ], 61 | }) 62 | return r.code 63 | } 64 | -------------------------------------------------------------------------------- /src/platforms/vue-native/scripts/util/constants.js: -------------------------------------------------------------------------------- 1 | const HELPER_HEADER = '__react__vue__' 2 | const SCRIPT_OPTIONS = `${HELPER_HEADER}options` 3 | const TEMPLATE_RENDER = `${HELPER_HEADER}render` 4 | const REACT_NATIVE = `${HELPER_HEADER}ReactNative` 5 | const BUILD_COMPONENT = `${HELPER_HEADER}buildNativeComponent` 6 | const COMPONENT_BUILDED = `${HELPER_HEADER}ComponentBuilded` 7 | const VUE = `${HELPER_HEADER}Vue` 8 | const REACT = `${HELPER_HEADER}React` 9 | const COMPONENT = `${HELPER_HEADER}Component` 10 | const PROP_TYPE = `${HELPER_HEADER}PropType` 11 | const OBSERVER = `${HELPER_HEADER}observer` 12 | const CSS = `${HELPER_HEADER}css` 13 | 14 | export default { 15 | HELPER_HEADER, 16 | SCRIPT_OPTIONS, 17 | TEMPLATE_RENDER, 18 | REACT_NATIVE, 19 | BUILD_COMPONENT, 20 | COMPONENT_BUILDED, 21 | VUE, 22 | REACT, 23 | COMPONENT, 24 | PROP_TYPE, 25 | OBSERVER, 26 | CSS, 27 | } 28 | -------------------------------------------------------------------------------- /src/platforms/vue-native/scripts/util/parseCss.js: -------------------------------------------------------------------------------- 1 | import { parseTransform } from './parseTransform' 2 | 3 | const camelizeRE = /-(\w)/g 4 | 5 | function camelize(str) { 6 | return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')) 7 | } 8 | 9 | function parseDeclarations(declarations) { 10 | const declarationObj = {} 11 | 12 | // Comments and @media blocks don't have declarations at the top level. 13 | if (declarations) { 14 | declarations.forEach(function(declaration) { 15 | if (declaration.type === 'declaration') { 16 | let value = declaration.value 17 | if (/px$/.test(value)) { 18 | value = parseFloat(value.replace(/px$/, '')) 19 | } else if ( 20 | declaration.property !== 'font-weight' && 21 | isNaN(value) === false 22 | ) { 23 | value = parseFloat(value) 24 | } 25 | if (declaration.property === 'transform') { 26 | value = parseTransform(value) 27 | } 28 | declarationObj[camelize(declaration.property)] = value 29 | } 30 | }) 31 | } 32 | 33 | return declarationObj 34 | } 35 | 36 | export function parseCss(ast) { 37 | const obj = {} 38 | if (ast.type === 'stylesheet') { 39 | ast.stylesheet.rules.forEach(function(rule) { 40 | const declarationObj = parseDeclarations(rule.declarations) 41 | if (rule.selectors) { 42 | rule.selectors.forEach(function(selector) { 43 | if (selector.indexOf('.') === 0) { 44 | obj[selector.replace(/^\./, '')] = declarationObj 45 | } 46 | }) 47 | } 48 | }) 49 | } 50 | return obj 51 | } 52 | -------------------------------------------------------------------------------- /src/platforms/web/compiler.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export { parseComponent } from 'sfc/parser' 4 | export { compile, compileToFunctions } from './compiler/index' 5 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/directives/html.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addProp } from 'compiler/helpers' 4 | 5 | export default function html(el: ASTElement, dir: ASTDirective) { 6 | if (dir.value) { 7 | addProp(el, 'innerHTML', `_s(${dir.value})`) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | import text from './text' 3 | import html from './html' 4 | 5 | export default { 6 | model, 7 | text, 8 | html, 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/directives/text.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addProp } from 'compiler/helpers' 4 | 5 | export default function text(el: ASTElement, dir: ASTDirective) { 6 | if (dir.value) { 7 | addProp(el, 'textContent', `_s(${dir.value})`) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isUnaryTag, canBeLeftOpenTag } from './util' 4 | import { genStaticKeys } from 'shared/util' 5 | import { createCompiler } from 'compiler/index' 6 | 7 | import modules from './modules/index' 8 | import directives from './directives/index' 9 | 10 | import { 11 | isPreTag, 12 | mustUseProp, 13 | isReservedTag, 14 | getTagNamespace, 15 | } from '../util/index' 16 | 17 | export const baseOptions: CompilerOptions = { 18 | expectHTML: true, 19 | modules, 20 | directives, 21 | isPreTag, 22 | isUnaryTag, 23 | mustUseProp, 24 | canBeLeftOpenTag, 25 | isReservedTag, 26 | getTagNamespace, 27 | staticKeys: genStaticKeys(modules), 28 | } 29 | 30 | const { compile, compileToFunctions } = createCompiler(baseOptions) 31 | export { compile, compileToFunctions } 32 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/modules/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parseText } from 'compiler/parser/text-parser' 4 | import { getAndRemoveAttr, getBindingAttr, baseWarn } from 'compiler/helpers' 5 | 6 | function transformNode(el: ASTElement, options: CompilerOptions) { 7 | const warn = options.warn || baseWarn 8 | const staticClass = getAndRemoveAttr(el, 'class') 9 | if (process.env.NODE_ENV !== 'production' && staticClass) { 10 | const expression = parseText(staticClass, options.delimiters) 11 | if (expression) { 12 | warn( 13 | `class="${staticClass}": ` + 14 | 'Interpolation inside attributes has been removed. ' + 15 | 'Use v-bind or the colon shorthand instead. For example, ' + 16 | 'instead of <div class="{{ val }}">, use <div :class="val">.', 17 | ) 18 | } 19 | } 20 | if (staticClass) { 21 | el.staticClass = JSON.stringify(staticClass) 22 | } 23 | const classBinding = getBindingAttr(el, 'class', false /* getStatic */) 24 | if (classBinding) { 25 | el.classBinding = classBinding 26 | } 27 | } 28 | 29 | function genData(el: ASTElement): string { 30 | let data = '' 31 | if (el.staticClass) { 32 | data += `staticClass:${el.staticClass},` 33 | } 34 | if (el.classBinding) { 35 | data += `class:${el.classBinding},` 36 | } 37 | return data 38 | } 39 | 40 | export default { 41 | staticKeys: ['staticClass'], 42 | transformNode, 43 | genData, 44 | } 45 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/modules/index.js: -------------------------------------------------------------------------------- 1 | import klass from './class' 2 | import style from './style' 3 | 4 | export default [klass, style] 5 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/modules/style.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parseText } from 'compiler/parser/text-parser' 4 | import { parseStyleText } from 'web/util/style' 5 | import { getAndRemoveAttr, getBindingAttr, baseWarn } from 'compiler/helpers' 6 | 7 | function transformNode(el: ASTElement, options: CompilerOptions) { 8 | const warn = options.warn || baseWarn 9 | const staticStyle = getAndRemoveAttr(el, 'style') 10 | if (staticStyle) { 11 | /* istanbul ignore if */ 12 | if (process.env.NODE_ENV !== 'production') { 13 | const expression = parseText(staticStyle, options.delimiters) 14 | if (expression) { 15 | warn( 16 | `style="${staticStyle}": ` + 17 | 'Interpolation inside attributes has been removed. ' + 18 | 'Use v-bind or the colon shorthand instead. For example, ' + 19 | 'instead of <div style="{{ val }}">, use <div :style="val">.', 20 | ) 21 | } 22 | } 23 | el.staticStyle = JSON.stringify(parseStyleText(staticStyle)) 24 | } 25 | 26 | const styleBinding = getBindingAttr(el, 'style', false /* getStatic */) 27 | if (styleBinding) { 28 | el.styleBinding = styleBinding 29 | } 30 | } 31 | 32 | function genData(el: ASTElement): string { 33 | let data = '' 34 | if (el.staticStyle) { 35 | data += `staticStyle:${el.staticStyle},` 36 | } 37 | if (el.styleBinding) { 38 | data += `style:(${el.styleBinding}),` 39 | } 40 | return data 41 | } 42 | 43 | export default { 44 | staticKeys: ['staticStyle'], 45 | transformNode, 46 | genData, 47 | } 48 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { makeMap } from 'shared/util' 4 | 5 | export const isUnaryTag = makeMap( 6 | 'area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' + 7 | 'link,meta,param,source,track,wbr', 8 | ) 9 | 10 | // Elements that you can, intentionally, leave open 11 | // (and which close themselves) 12 | export const canBeLeftOpenTag = makeMap( 13 | 'colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source', 14 | ) 15 | 16 | // HTML5 tags https://html.spec.whatwg.org/multipage/indices.html#elements-3 17 | // Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content 18 | export const isNonPhrasingTag = makeMap( 19 | 'address,article,aside,base,blockquote,body,caption,col,colgroup,dd,' + 20 | 'details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,' + 21 | 'h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,' + 22 | 'optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,' + 23 | 'title,tr,track', 24 | ) 25 | -------------------------------------------------------------------------------- /src/platforms/web/runtime-with-compiler.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from 'core/config' 4 | import { warn, cached } from 'core/util/index' 5 | import { mark, measure } from 'core/util/perf' 6 | 7 | import Vue from './runtime/index' 8 | import { query } from './util/index' 9 | import { shouldDecodeNewlines } from './util/compat' 10 | import { compileToFunctions } from './compiler/index' 11 | 12 | const idToTemplate = cached(id => { 13 | const el = query(id) 14 | return el && el.innerHTML 15 | }) 16 | 17 | const mount = Vue.prototype.$mount 18 | Vue.prototype.$mount = function( 19 | el?: string | Element, 20 | hydrating?: boolean, 21 | ): Component { 22 | el = el && query(el) 23 | 24 | /* istanbul ignore if */ 25 | if (el === document.body || el === document.documentElement) { 26 | process.env.NODE_ENV !== 'production' && 27 | warn( 28 | `Do not mount Vue to <html> or <body> - mount to normal elements instead.`, 29 | ) 30 | return this 31 | } 32 | 33 | const options = this.$options 34 | // resolve template/el and convert to render function 35 | if (!options.render) { 36 | let template = options.template 37 | if (template) { 38 | if (typeof template === 'string') { 39 | if (template.charAt(0) === '#') { 40 | template = idToTemplate(template) 41 | /* istanbul ignore if */ 42 | if (process.env.NODE_ENV !== 'production' && !template) { 43 | warn( 44 | `Template element not found or is empty: ${options.template}`, 45 | this, 46 | ) 47 | } 48 | } 49 | } else if (template.nodeType) { 50 | template = template.innerHTML 51 | } else { 52 | if (process.env.NODE_ENV !== 'production') { 53 | warn('invalid template option:' + template, this) 54 | } 55 | return this 56 | } 57 | } else if (el) { 58 | template = getOuterHTML(el) 59 | } 60 | if (template) { 61 | /* istanbul ignore if */ 62 | if (process.env.NODE_ENV !== 'production' && config.performance && mark) { 63 | mark('compile') 64 | } 65 | 66 | const { render, staticRenderFns } = compileToFunctions( 67 | template, 68 | { 69 | shouldDecodeNewlines, 70 | delimiters: options.delimiters, 71 | }, 72 | this, 73 | ) 74 | options.render = render 75 | options.staticRenderFns = staticRenderFns 76 | 77 | /* istanbul ignore if */ 78 | if (process.env.NODE_ENV !== 'production' && config.performance && mark) { 79 | mark('compile end') 80 | measure(`${this._name} compile`, 'compile', 'compile end') 81 | } 82 | } 83 | } 84 | return mount.call(this, el, hydrating) 85 | } 86 | 87 | /** 88 | * Get outerHTML of elements, taking care 89 | * of SVG elements in IE as well. 90 | */ 91 | function getOuterHTML(el: Element): string { 92 | if (el.outerHTML) { 93 | return el.outerHTML 94 | } else { 95 | const container = document.createElement('div') 96 | container.appendChild(el.cloneNode(true)) 97 | return container.innerHTML 98 | } 99 | } 100 | 101 | Vue.compile = compileToFunctions 102 | 103 | export default Vue 104 | -------------------------------------------------------------------------------- /src/platforms/web/runtime.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import Vue from './runtime/index' 4 | 5 | export default Vue 6 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/class-util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | /** 4 | * Add class with compatibility for SVG since classList is not supported on 5 | * SVG elements in IE 6 | */ 7 | export function addClass(el: HTMLElement, cls: ?string) { 8 | /* istanbul ignore if */ 9 | if (!cls || !(cls = cls.trim())) { 10 | return 11 | } 12 | 13 | /* istanbul ignore else */ 14 | if (el.classList) { 15 | if (cls.indexOf(' ') > -1) { 16 | cls.split(/\s+/).forEach(c => el.classList.add(c)) 17 | } else { 18 | el.classList.add(cls) 19 | } 20 | } else { 21 | const cur = ` ${el.getAttribute('class') || ''} ` 22 | if (cur.indexOf(' ' + cls + ' ') < 0) { 23 | el.setAttribute('class', (cur + cls).trim()) 24 | } 25 | } 26 | } 27 | 28 | /** 29 | * Remove class with compatibility for SVG since classList is not supported on 30 | * SVG elements in IE 31 | */ 32 | export function removeClass(el: HTMLElement, cls: ?string) { 33 | /* istanbul ignore if */ 34 | if (!cls || !(cls = cls.trim())) { 35 | return 36 | } 37 | 38 | /* istanbul ignore else */ 39 | if (el.classList) { 40 | if (cls.indexOf(' ') > -1) { 41 | cls.split(/\s+/).forEach(c => el.classList.remove(c)) 42 | } else { 43 | el.classList.remove(cls) 44 | } 45 | } else { 46 | let cur = ` ${el.getAttribute('class') || ''} ` 47 | const tar = ' ' + cls + ' ' 48 | while (cur.indexOf(tar) >= 0) { 49 | cur = cur.replace(tar, ' ') 50 | } 51 | el.setAttribute('class', cur.trim()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/components/index.js: -------------------------------------------------------------------------------- 1 | import Transition from './transition' 2 | import TransitionGroup from './transition-group' 3 | 4 | export default { 5 | Transition, 6 | TransitionGroup, 7 | } 8 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | import show from './show' 3 | 4 | export default { 5 | model, 6 | show, 7 | } 8 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/directives/show.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isIE9 } from 'core/util/env' 4 | import { enter, leave } from '../modules/transition' 5 | 6 | // recursively search for possible transition defined inside the component root 7 | function locateNode(vnode: VNode): VNodeWithData { 8 | return vnode.componentInstance && (!vnode.data || !vnode.data.transition) 9 | ? locateNode(vnode.componentInstance._vnode) 10 | : vnode 11 | } 12 | 13 | export default { 14 | bind(el: any, { value }: VNodeDirective, vnode: VNodeWithData) { 15 | vnode = locateNode(vnode) 16 | const transition = vnode.data && vnode.data.transition 17 | const originalDisplay = (el.__vOriginalDisplay = 18 | el.style.display === 'none' ? '' : el.style.display) 19 | if (value && transition && !isIE9) { 20 | vnode.data.show = true 21 | enter(vnode, () => { 22 | el.style.display = originalDisplay 23 | }) 24 | } else { 25 | el.style.display = value ? originalDisplay : 'none' 26 | } 27 | }, 28 | 29 | update(el: any, { value, oldValue }: VNodeDirective, vnode: VNodeWithData) { 30 | /* istanbul ignore if */ 31 | if (value === oldValue) return 32 | vnode = locateNode(vnode) 33 | const transition = vnode.data && vnode.data.transition 34 | if (transition && !isIE9) { 35 | vnode.data.show = true 36 | if (value) { 37 | enter(vnode, () => { 38 | el.style.display = el.__vOriginalDisplay 39 | }) 40 | } else { 41 | leave(vnode, () => { 42 | el.style.display = 'none' 43 | }) 44 | } 45 | } else { 46 | el.style.display = value ? el.__vOriginalDisplay : 'none' 47 | } 48 | }, 49 | 50 | unbind( 51 | el: any, 52 | binding: VNodeDirective, 53 | vnode: VNodeWithData, 54 | oldVnode: VNodeWithData, 55 | isDestroy: boolean, 56 | ) { 57 | if (!isDestroy) { 58 | el.style.display = el.__vOriginalDisplay 59 | } 60 | }, 61 | } 62 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import Vue from 'core/index' 4 | import config from 'core/config' 5 | import { extend, noop } from 'shared/util' 6 | import { mountComponent } from 'core/instance/lifecycle' 7 | import { devtools, inBrowser, isChrome } from 'core/util/index' 8 | 9 | import { 10 | query, 11 | mustUseProp, 12 | isReservedTag, 13 | isReservedAttr, 14 | getTagNamespace, 15 | isUnknownElement, 16 | } from 'web/util/index' 17 | 18 | import { patch } from './patch' 19 | import platformDirectives from './directives/index' 20 | import platformComponents from './components/index' 21 | 22 | // install platform specific utils 23 | Vue.config.mustUseProp = mustUseProp 24 | Vue.config.isReservedTag = isReservedTag 25 | Vue.config.isReservedAttr = isReservedAttr 26 | Vue.config.getTagNamespace = getTagNamespace 27 | Vue.config.isUnknownElement = isUnknownElement 28 | 29 | // install platform runtime directives & components 30 | extend(Vue.options.directives, platformDirectives) 31 | extend(Vue.options.components, platformComponents) 32 | 33 | // install platform patch function 34 | Vue.prototype.__patch__ = inBrowser ? patch : noop 35 | 36 | // public mount method 37 | Vue.prototype.$mount = function( 38 | el?: string | Element, 39 | hydrating?: boolean, 40 | ): Component { 41 | el = el && inBrowser ? query(el) : undefined 42 | return mountComponent(this, el, hydrating) 43 | } 44 | 45 | // devtools global hook 46 | /* istanbul ignore next */ 47 | setTimeout(() => { 48 | if (config.devtools) { 49 | if (devtools) { 50 | devtools.emit('init', Vue) 51 | } else if (process.env.NODE_ENV !== 'production' && isChrome) { 52 | console[console.info ? 'info' : 'log']( 53 | 'Download the Vue Devtools extension for a better development experience:\n' + 54 | 'https://github.com/vuejs/vue-devtools', 55 | ) 56 | } 57 | } 58 | if ( 59 | process.env.NODE_ENV !== 'production' && 60 | config.productionTip !== false && 61 | inBrowser && 62 | typeof console !== 'undefined' 63 | ) { 64 | console[console.info ? 'info' : 'log']( 65 | `You are running Vue in development mode.\n` + 66 | `Make sure to turn on production mode when deploying for production.\n` + 67 | `See more tips at https://vuejs.org/guide/deployment.html`, 68 | ) 69 | } 70 | }, 0) 71 | 72 | export default Vue 73 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/modules/attrs.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isIE9 } from 'core/util/env' 4 | 5 | import { extend, isDef, isUndef } from 'shared/util' 6 | 7 | import { 8 | isXlink, 9 | xlinkNS, 10 | getXlinkProp, 11 | isBooleanAttr, 12 | isEnumeratedAttr, 13 | isFalsyAttrValue, 14 | } from 'web/util/index' 15 | 16 | function updateAttrs(oldVnode: VNodeWithData, vnode: VNodeWithData) { 17 | if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) { 18 | return 19 | } 20 | let key, cur, old 21 | const elm = vnode.elm 22 | const oldAttrs = oldVnode.data.attrs || {} 23 | let attrs: any = vnode.data.attrs || {} 24 | // clone observed objects, as the user probably wants to mutate it 25 | if (isDef(attrs.__ob__)) { 26 | attrs = vnode.data.attrs = extend({}, attrs) 27 | } 28 | 29 | for (key in attrs) { 30 | cur = attrs[key] 31 | old = oldAttrs[key] 32 | if (old !== cur) { 33 | setAttr(elm, key, cur) 34 | } 35 | } 36 | // #4391: in IE9, setting type can reset value for input[type=radio] 37 | /* istanbul ignore if */ 38 | if (isIE9 && attrs.value !== oldAttrs.value) { 39 | setAttr(elm, 'value', attrs.value) 40 | } 41 | for (key in oldAttrs) { 42 | if (isUndef(attrs[key])) { 43 | if (isXlink(key)) { 44 | elm.removeAttributeNS(xlinkNS, getXlinkProp(key)) 45 | } else if (!isEnumeratedAttr(key)) { 46 | elm.removeAttribute(key) 47 | } 48 | } 49 | } 50 | } 51 | 52 | function setAttr(el: Element, key: string, value: any) { 53 | if (isBooleanAttr(key)) { 54 | // set attribute for blank value 55 | // e.g. <option disabled>Select one</option> 56 | if (isFalsyAttrValue(value)) { 57 | el.removeAttribute(key) 58 | } else { 59 | el.setAttribute(key, key) 60 | } 61 | } else if (isEnumeratedAttr(key)) { 62 | el.setAttribute( 63 | key, 64 | isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true', 65 | ) 66 | } else if (isXlink(key)) { 67 | if (isFalsyAttrValue(value)) { 68 | el.removeAttributeNS(xlinkNS, getXlinkProp(key)) 69 | } else { 70 | el.setAttributeNS(xlinkNS, key, value) 71 | } 72 | } else { 73 | if (isFalsyAttrValue(value)) { 74 | el.removeAttribute(key) 75 | } else { 76 | el.setAttribute(key, value) 77 | } 78 | } 79 | } 80 | 81 | export default { 82 | create: updateAttrs, 83 | update: updateAttrs, 84 | } 85 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/modules/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isDef, isUndef } from 'shared/util' 4 | 5 | import { concat, stringifyClass, genClassForVnode } from 'web/util/index' 6 | 7 | function updateClass(oldVnode: any, vnode: any) { 8 | const el = vnode.elm 9 | const data: VNodeData = vnode.data 10 | const oldData: VNodeData = oldVnode.data 11 | if ( 12 | isUndef(data.staticClass) && 13 | isUndef(data.class) && 14 | (isUndef(oldData) || 15 | (isUndef(oldData.staticClass) && isUndef(oldData.class))) 16 | ) { 17 | return 18 | } 19 | 20 | let cls = genClassForVnode(vnode) 21 | 22 | // handle transition classes 23 | const transitionClass = el._transitionClasses 24 | if (isDef(transitionClass)) { 25 | cls = concat(cls, stringifyClass(transitionClass)) 26 | } 27 | 28 | // set the class 29 | if (cls !== el._prevClass) { 30 | el.setAttribute('class', cls) 31 | el._prevClass = cls 32 | } 33 | } 34 | 35 | export default { 36 | create: updateClass, 37 | update: updateClass, 38 | } 39 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/modules/dom-props.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isDef, isUndef, extend, toNumber } from 'shared/util' 4 | 5 | function updateDOMProps(oldVnode: VNodeWithData, vnode: VNodeWithData) { 6 | if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) { 7 | return 8 | } 9 | let key, cur 10 | const elm: any = vnode.elm 11 | const oldProps = oldVnode.data.domProps || {} 12 | let props = vnode.data.domProps || {} 13 | // clone observed objects, as the user probably wants to mutate it 14 | if (isDef(props.__ob__)) { 15 | props = vnode.data.domProps = extend({}, props) 16 | } 17 | 18 | for (key in oldProps) { 19 | if (isUndef(props[key])) { 20 | elm[key] = '' 21 | } 22 | } 23 | for (key in props) { 24 | cur = props[key] 25 | // ignore children if the node has textContent or innerHTML, 26 | // as these will throw away existing DOM nodes and cause removal errors 27 | // on subsequent patches (#3360) 28 | if (key === 'textContent' || key === 'innerHTML') { 29 | if (vnode.children) vnode.children.length = 0 30 | if (cur === oldProps[key]) continue 31 | } 32 | 33 | if (key === 'value') { 34 | // store value as _value as well since 35 | // non-string values will be stringified 36 | elm._value = cur 37 | // avoid resetting cursor position when value is the same 38 | const strCur = cur == null ? '' : String(cur) 39 | if (shouldUpdateValue(elm, vnode, strCur)) { 40 | elm.value = strCur 41 | } 42 | } else { 43 | elm[key] = cur 44 | } 45 | } 46 | } 47 | 48 | // check platforms/web/util/attrs.js acceptValue 49 | type acceptValueElm = HTMLInputElement | HTMLSelectElement | HTMLOptionElement 50 | 51 | function shouldUpdateValue( 52 | elm: acceptValueElm, 53 | vnode: VNodeWithData, 54 | checkVal: string, 55 | ): boolean { 56 | return ( 57 | !elm.composing && 58 | (vnode.tag === 'option' || 59 | isDirty(elm, checkVal) || 60 | isInputChanged(elm, checkVal)) 61 | ) 62 | } 63 | 64 | function isDirty(elm: acceptValueElm, checkVal: string): boolean { 65 | // return true when textbox (.number and .trim) loses focus and its value is not equal to the updated value 66 | return document.activeElement !== elm && elm.value !== checkVal 67 | } 68 | 69 | function isInputChanged(elm: any, newVal: string): boolean { 70 | const value = elm.value 71 | const modifiers = elm._vModifiers // injected by v-model runtime 72 | if ((isDef(modifiers) && modifiers.number) || elm.type === 'number') { 73 | return toNumber(value) !== toNumber(newVal) 74 | } 75 | if (isDef(modifiers) && modifiers.trim) { 76 | return value.trim() !== newVal.trim() 77 | } 78 | return value !== newVal 79 | } 80 | 81 | export default { 82 | create: updateDOMProps, 83 | update: updateDOMProps, 84 | } 85 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/modules/events.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isDef, isUndef } from 'shared/util' 4 | import { updateListeners } from 'core/vdom/helpers/index' 5 | import { isChrome, isIE, supportsPassive } from 'core/util/env' 6 | import { 7 | RANGE_TOKEN, 8 | CHECKBOX_RADIO_TOKEN, 9 | } from 'web/compiler/directives/model' 10 | 11 | // normalize v-model event tokens that can only be determined at runtime. 12 | // it's important to place the event as the first in the array because 13 | // the whole point is ensuring the v-model callback gets called before 14 | // user-attached handlers. 15 | function normalizeEvents(on) { 16 | let event 17 | /* istanbul ignore if */ 18 | if (isDef(on[RANGE_TOKEN])) { 19 | // IE input[type=range] only supports `change` event 20 | event = isIE ? 'change' : 'input' 21 | on[event] = [].concat(on[RANGE_TOKEN], on[event] || []) 22 | delete on[RANGE_TOKEN] 23 | } 24 | if (isDef(on[CHECKBOX_RADIO_TOKEN])) { 25 | // Chrome fires microtasks in between click/change, leads to #4521 26 | event = isChrome ? 'click' : 'change' 27 | on[event] = [].concat(on[CHECKBOX_RADIO_TOKEN], on[event] || []) 28 | delete on[CHECKBOX_RADIO_TOKEN] 29 | } 30 | } 31 | 32 | let target: HTMLElement 33 | 34 | function add( 35 | event: string, 36 | handler: Function, 37 | once: boolean, 38 | capture: boolean, 39 | passive: boolean, 40 | ) { 41 | if (once) { 42 | const oldHandler = handler 43 | const _target = target // save current target element in closure 44 | handler = function(ev) { 45 | const res = 46 | arguments.length === 1 47 | ? oldHandler(ev) 48 | : oldHandler.apply(null, arguments) 49 | if (res !== null) { 50 | remove(event, handler, capture, _target) 51 | } 52 | } 53 | } 54 | target.addEventListener( 55 | event, 56 | handler, 57 | supportsPassive ? { capture, passive } : capture, 58 | ) 59 | } 60 | 61 | function remove( 62 | event: string, 63 | handler: Function, 64 | capture: boolean, 65 | _target?: HTMLElement, 66 | ) { 67 | ;(_target || target).removeEventListener(event, handler, capture) 68 | } 69 | 70 | function updateDOMListeners(oldVnode: VNodeWithData, vnode: VNodeWithData) { 71 | if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) { 72 | return 73 | } 74 | const on = vnode.data.on || {} 75 | const oldOn = oldVnode.data.on || {} 76 | target = vnode.elm 77 | normalizeEvents(on) 78 | updateListeners(on, oldOn, add, remove, vnode.context) 79 | } 80 | 81 | export default { 82 | create: updateDOMListeners, 83 | update: updateDOMListeners, 84 | } 85 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/modules/index.js: -------------------------------------------------------------------------------- 1 | import attrs from './attrs' 2 | import klass from './class' 3 | import events from './events' 4 | import domProps from './dom-props' 5 | import style from './style' 6 | import transition from './transition' 7 | 8 | export default [attrs, klass, events, domProps, style, transition] 9 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/modules/style.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getStyle, normalizeStyleBinding } from 'web/util/style' 4 | import { cached, camelize, extend, isDef, isUndef } from 'shared/util' 5 | 6 | const cssVarRE = /^--/ 7 | const importantRE = /\s*!important$/ 8 | const setProp = (el, name, val) => { 9 | /* istanbul ignore if */ 10 | if (cssVarRE.test(name)) { 11 | el.style.setProperty(name, val) 12 | } else if (importantRE.test(val)) { 13 | el.style.setProperty(name, val.replace(importantRE, ''), 'important') 14 | } else { 15 | const normalizedName = normalize(name) 16 | if (Array.isArray(val)) { 17 | // Support values array created by autoprefixer, e.g. 18 | // {display: ["-webkit-box", "-ms-flexbox", "flex"]} 19 | // Set them one by one, and the browser will only set those it can recognize 20 | for (let i = 0, len = val.length; i < len; i++) { 21 | el.style[normalizedName] = val[i] 22 | } 23 | } else { 24 | el.style[normalizedName] = val 25 | } 26 | } 27 | } 28 | 29 | const prefixes = ['Webkit', 'Moz', 'ms'] 30 | 31 | let testEl 32 | const normalize = cached(function(prop) { 33 | testEl = testEl || document.createElement('div') 34 | prop = camelize(prop) 35 | if (prop !== 'filter' && prop in testEl.style) { 36 | return prop 37 | } 38 | const upper = prop.charAt(0).toUpperCase() + prop.slice(1) 39 | for (let i = 0; i < prefixes.length; i++) { 40 | const prefixed = prefixes[i] + upper 41 | if (prefixed in testEl.style) { 42 | return prefixed 43 | } 44 | } 45 | }) 46 | 47 | function updateStyle(oldVnode: VNodeWithData, vnode: VNodeWithData) { 48 | const data = vnode.data 49 | const oldData = oldVnode.data 50 | 51 | if ( 52 | isUndef(data.staticStyle) && 53 | isUndef(data.style) && 54 | isUndef(oldData.staticStyle) && 55 | isUndef(oldData.style) 56 | ) { 57 | return 58 | } 59 | 60 | let cur, name 61 | const el: any = vnode.elm 62 | const oldStaticStyle: any = oldData.staticStyle 63 | const oldStyleBinding: any = oldData.normalizedStyle || oldData.style || {} 64 | 65 | // if static style exists, stylebinding already merged into it when doing normalizeStyleData 66 | const oldStyle = oldStaticStyle || oldStyleBinding 67 | 68 | const style = normalizeStyleBinding(vnode.data.style) || {} 69 | 70 | // store normalized style under a different key for next diff 71 | // make sure to clone it if it's reactive, since the user likley wants 72 | // to mutate it. 73 | vnode.data.normalizedStyle = isDef(style.__ob__) ? extend({}, style) : style 74 | 75 | const newStyle = getStyle(vnode, true) 76 | 77 | for (name in oldStyle) { 78 | if (isUndef(newStyle[name])) { 79 | setProp(el, name, '') 80 | } 81 | } 82 | for (name in newStyle) { 83 | cur = newStyle[name] 84 | if (cur !== oldStyle[name]) { 85 | // ie9 setting to null has no effect, must use empty string 86 | setProp(el, name, cur == null ? '' : cur) 87 | } 88 | } 89 | } 90 | 91 | export default { 92 | create: updateStyle, 93 | update: updateStyle, 94 | } 95 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/node-ops.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { namespaceMap } from 'web/util/index' 4 | 5 | export function createElement(tagName: string, vnode: VNode): Element { 6 | const elm = document.createElement(tagName) 7 | if (tagName !== 'select') { 8 | return elm 9 | } 10 | // false or null will remove the attribute but undefined will not 11 | if ( 12 | vnode.data && 13 | vnode.data.attrs && 14 | vnode.data.attrs.multiple !== undefined 15 | ) { 16 | elm.setAttribute('multiple', 'multiple') 17 | } 18 | return elm 19 | } 20 | 21 | export function createElementNS(namespace: string, tagName: string): Element { 22 | return document.createElementNS(namespaceMap[namespace], tagName) 23 | } 24 | 25 | export function createTextNode(text: string): Text { 26 | return document.createTextNode(text) 27 | } 28 | 29 | export function createComment(text: string): Comment { 30 | return document.createComment(text) 31 | } 32 | 33 | export function insertBefore( 34 | parentNode: Node, 35 | newNode: Node, 36 | referenceNode: Node, 37 | ) { 38 | parentNode.insertBefore(newNode, referenceNode) 39 | } 40 | 41 | export function removeChild(node: Node, child: Node) { 42 | node.removeChild(child) 43 | } 44 | 45 | export function appendChild(node: Node, child: Node) { 46 | node.appendChild(child) 47 | } 48 | 49 | export function parentNode(node: Node): ?Node { 50 | return node.parentNode 51 | } 52 | 53 | export function nextSibling(node: Node): ?Node { 54 | return node.nextSibling 55 | } 56 | 57 | export function tagName(node: Element): string { 58 | return node.tagName 59 | } 60 | 61 | export function setTextContent(node: Node, text: string) { 62 | node.textContent = text 63 | } 64 | 65 | export function setAttribute(node: Element, key: string, val: string) { 66 | node.setAttribute(key, val) 67 | } 68 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/patch.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import * as nodeOps from 'web/runtime/node-ops' 4 | import { createPatchFunction } from 'core/vdom/patch' 5 | import baseModules from 'core/vdom/modules/index' 6 | import platformModules from 'web/runtime/modules/index' 7 | 8 | // the directive module should be applied last, after all 9 | // built-in modules have been applied. 10 | const modules = platformModules.concat(baseModules) 11 | 12 | export const patch: Function = createPatchFunction({ nodeOps, modules }) 13 | -------------------------------------------------------------------------------- /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 = ( 12 | tag: string, 13 | type: ?string, 14 | attr: string, 15 | ): boolean => { 16 | return ( 17 | (attr === 'value' && acceptValue(tag) && type !== 'button') || 18 | (attr === 'selected' && tag === 'option') || 19 | (attr === 'checked' && tag === 'input') || 20 | (attr === 'muted' && tag === 'video') 21 | ) 22 | } 23 | 24 | export const isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck') 25 | 26 | export const isBooleanAttr = makeMap( 27 | 'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' + 28 | 'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' + 29 | 'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' + 30 | 'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' + 31 | 'required,reversed,scoped,seamless,selected,sortable,translate,' + 32 | 'truespeed,typemustmatch,visible', 33 | ) 34 | 35 | export const xlinkNS = 'http://www.w3.org/1999/xlink' 36 | 37 | export const isXlink = (name: string): boolean => { 38 | return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' 39 | } 40 | 41 | export const getXlinkProp = (name: string): string => { 42 | return isXlink(name) ? name.slice(6, name.length) : '' 43 | } 44 | 45 | export const isFalsyAttrValue = (val: any): boolean => { 46 | return val == null || val === false 47 | } 48 | -------------------------------------------------------------------------------- /src/platforms/web/util/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isDef, isUndef, 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 genClassFromData(data) 21 | } 22 | 23 | function mergeClassData( 24 | child: VNodeData, 25 | parent: VNodeData, 26 | ): { 27 | staticClass: string, 28 | class: any, 29 | } { 30 | return { 31 | staticClass: concat(child.staticClass, parent.staticClass), 32 | class: isDef(child.class) ? [child.class, parent.class] : parent.class, 33 | } 34 | } 35 | 36 | function genClassFromData(data: Object): string { 37 | const dynamicClass = data.class 38 | const staticClass = data.staticClass 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 (isUndef(value)) { 52 | return '' 53 | } 54 | if (typeof value === 'string') { 55 | return value 56 | } 57 | let res = '' 58 | if (Array.isArray(value)) { 59 | let stringified 60 | for (let i = 0, l = value.length; i < l; i++) { 61 | if (isDef(value[i])) { 62 | if ( 63 | isDef((stringified = stringifyClass(value[i]))) && 64 | stringified !== '' 65 | ) { 66 | res += stringified + ' ' 67 | } 68 | } 69 | } 70 | return res.slice(0, -1) 71 | } 72 | if (isObject(value)) { 73 | for (const key in value) { 74 | if (value[key]) res += key + ' ' 75 | } 76 | return res.slice(0, -1) 77 | } 78 | /* istanbul ignore next */ 79 | return res 80 | } 81 | -------------------------------------------------------------------------------- /src/platforms/web/util/compat.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { inBrowser } from 'core/util/index' 4 | 5 | // check whether current browser encodes a char inside attribute values 6 | function shouldDecode(content: string, encoded: string): boolean { 7 | const div = document.createElement('div') 8 | div.innerHTML = `<div a="${content}">` 9 | return div.innerHTML.indexOf(encoded) > 0 10 | } 11 | 12 | // #3663 13 | // IE encodes newlines inside attribute values while other browsers don't 14 | export const shouldDecodeNewlines = inBrowser 15 | ? shouldDecode('\n', ' ') 16 | : false 17 | -------------------------------------------------------------------------------- /src/platforms/web/util/element.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { inBrowser } from 'core/util/env' 4 | import { makeMap } from 'shared/util' 5 | 6 | export const namespaceMap = { 7 | svg: 'http://www.w3.org/2000/svg', 8 | math: 'http://www.w3.org/1998/Math/MathML', 9 | } 10 | 11 | export const isHTMLTag = makeMap( 12 | 'html,body,base,head,link,meta,style,title,' + 13 | 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' + 14 | 'div,dd,dl,dt,figcaption,figure,hr,img,li,main,ol,p,pre,ul,' + 15 | 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' + 16 | 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' + 17 | 'embed,object,param,source,canvas,script,noscript,del,ins,' + 18 | 'caption,col,colgroup,table,thead,tbody,td,th,tr,' + 19 | 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' + 20 | 'output,progress,select,textarea,' + 21 | 'details,dialog,menu,menuitem,summary,' + 22 | 'content,element,shadow,template', 23 | ) 24 | 25 | // this map is intentionally selective, only covering SVG elements that may 26 | // contain child elements. 27 | export const isSVG = makeMap( 28 | 'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' + 29 | 'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' + 30 | 'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view', 31 | true, 32 | ) 33 | 34 | export const isPreTag = (tag: ?string): boolean => tag === 'pre' 35 | 36 | export const isReservedTag = (tag: string): ?boolean => { 37 | return isHTMLTag(tag) || isSVG(tag) 38 | } 39 | 40 | export function getTagNamespace(tag: string): ?string { 41 | if (isSVG(tag)) { 42 | return 'svg' 43 | } 44 | // basic support for MathML 45 | // note it doesn't support other MathML elements being component roots 46 | if (tag === 'math') { 47 | return 'math' 48 | } 49 | } 50 | 51 | const unknownElementCache = Object.create(null) 52 | export function isUnknownElement(tag: string): boolean { 53 | /* istanbul ignore if */ 54 | if (!inBrowser) { 55 | return true 56 | } 57 | if (isReservedTag(tag)) { 58 | return false 59 | } 60 | tag = tag.toLowerCase() 61 | /* istanbul ignore if */ 62 | if (unknownElementCache[tag] != null) { 63 | return unknownElementCache[tag] 64 | } 65 | const el = document.createElement(tag) 66 | if (tag.indexOf('-') > -1) { 67 | // http://stackoverflow.com/a/28210364/1070244 68 | return (unknownElementCache[tag] = 69 | el.constructor === window.HTMLUnknownElement || 70 | el.constructor === window.HTMLElement) 71 | } else { 72 | return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString())) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/platforms/web/util/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn } from 'core/util/index' 4 | 5 | export * from './attrs' 6 | export * from './class' 7 | export * from './element' 8 | 9 | /** 10 | * Query an element selector if it's not an element already. 11 | */ 12 | export function query(el: string | Element): Element { 13 | if (typeof el === 'string') { 14 | const selected = document.querySelector(el) 15 | if (!selected) { 16 | process.env.NODE_ENV !== 'production' && 17 | warn('Cannot find element: ' + el) 18 | return document.createElement('div') 19 | } 20 | return selected 21 | } else { 22 | return el 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /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 ? extend(data.staticStyle, style) : style 24 | } 25 | 26 | // normalize possible array / string values into Object 27 | export function normalizeStyleBinding(bindingStyle: any): ?Object { 28 | if (Array.isArray(bindingStyle)) { 29 | return toObject(bindingStyle) 30 | } 31 | if (typeof bindingStyle === 'string') { 32 | return parseStyleText(bindingStyle) 33 | } 34 | return bindingStyle 35 | } 36 | 37 | /** 38 | * parent component style should be after child's 39 | * so that parent component's style could override it 40 | */ 41 | export function getStyle(vnode: VNode, checkChild: boolean): Object { 42 | const res = {} 43 | let styleData 44 | 45 | if (checkChild) { 46 | let childNode = vnode 47 | while (childNode.componentInstance) { 48 | childNode = childNode.componentInstance._vnode 49 | if (childNode.data && (styleData = normalizeStyleData(childNode.data))) { 50 | extend(res, styleData) 51 | } 52 | } 53 | } 54 | 55 | if ((styleData = normalizeStyleData(vnode.data))) { 56 | extend(res, styleData) 57 | } 58 | 59 | let parentNode = vnode 60 | while ((parentNode = parentNode.parent)) { 61 | if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) { 62 | extend(res, styleData) 63 | } 64 | } 65 | return res 66 | } 67 | -------------------------------------------------------------------------------- /src/sfc/parser.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import deindent from 'de-indent' 4 | import { parseHTML } from 'compiler/parser/html-parser' 5 | import { makeMap } from 'shared/util' 6 | 7 | const splitRE = /\r?\n/g 8 | const replaceRE = /./g 9 | const isSpecialTag = makeMap('script,style,template', true) 10 | 11 | type Attribute = { 12 | name: string, 13 | value: string, 14 | } 15 | 16 | /** 17 | * Parse a single-file component (*.vue) file into an SFC Descriptor Object. 18 | */ 19 | export function parseComponent( 20 | content: string, 21 | options?: Object = {}, 22 | ): SFCDescriptor { 23 | const sfc: SFCDescriptor = { 24 | template: null, 25 | script: null, 26 | styles: [], 27 | customBlocks: [], 28 | } 29 | let depth = 0 30 | let currentBlock: ?(SFCBlock | SFCCustomBlock) = null 31 | 32 | function start( 33 | tag: string, 34 | attrs: Array<Attribute>, 35 | unary: boolean, 36 | start: number, 37 | end: number, 38 | ) { 39 | if (depth === 0) { 40 | currentBlock = { 41 | type: tag, 42 | content: '', 43 | start: end, 44 | attrs: attrs.reduce((cumulated, { name, value }) => { 45 | cumulated[name] = value || true 46 | return cumulated 47 | }, Object.create(null)), 48 | } 49 | if (isSpecialTag(tag)) { 50 | checkAttrs(currentBlock, attrs) 51 | if (tag === 'style') { 52 | sfc.styles.push(currentBlock) 53 | } else { 54 | sfc[tag] = currentBlock 55 | } 56 | } else { 57 | // custom blocks 58 | sfc.customBlocks.push(currentBlock) 59 | } 60 | } 61 | if (!unary) { 62 | depth++ 63 | } 64 | } 65 | 66 | function checkAttrs(block: SFCBlock, attrs: Array<Attribute>) { 67 | for (let i = 0; i < attrs.length; i++) { 68 | const attr = attrs[i] 69 | if (attr.name === 'lang') { 70 | block.lang = attr.value 71 | } 72 | if (attr.name === 'scoped') { 73 | block.scoped = true 74 | } 75 | if (attr.name === 'module') { 76 | block.module = attr.value || true 77 | } 78 | if (attr.name === 'src') { 79 | block.src = attr.value 80 | } 81 | } 82 | } 83 | 84 | // eslint-disable-next-line no-unused-vars 85 | function end(tag: string, start: number, end: number) { 86 | if (depth === 1 && currentBlock) { 87 | currentBlock.end = start 88 | let text = deindent(content.slice(currentBlock.start, currentBlock.end)) 89 | // pad content so that linters and pre-processors can output correct 90 | // line numbers in errors and warnings 91 | if (currentBlock.type !== 'template' && options.pad) { 92 | text = padContent(currentBlock, options.pad) + text 93 | } 94 | currentBlock.content = text 95 | currentBlock = null 96 | } 97 | depth-- 98 | } 99 | 100 | function padContent( 101 | block: SFCBlock | SFCCustomBlock, 102 | pad: true | 'line' | 'space', 103 | ) { 104 | if (pad === 'space') { 105 | return content.slice(0, block.start).replace(replaceRE, ' ') 106 | } else { 107 | const offset = content.slice(0, block.start).split(splitRE).length 108 | const padChar = block.type === 'script' && !block.lang ? '//\n' : '\n' 109 | return Array(offset).join(padChar) 110 | } 111 | } 112 | 113 | parseHTML(content, { 114 | start, 115 | end, 116 | }) 117 | 118 | return sfc 119 | } 120 | -------------------------------------------------------------------------------- /src/shared/constants.js: -------------------------------------------------------------------------------- 1 | export const SSR_ATTR = 'data-server-rendered' 2 | 3 | /** 4 | * List of soon to be deprecated packages in react native v0.63 5 | */ 6 | export const deprecatedPackages = [ 7 | 'AlertIOS', 8 | 'AsyncStorage', 9 | 'CheckBox', 10 | 'Clipboard', 11 | 'DatePickerAndroid', 12 | 'DatePickerIOS', 13 | 'ImagePickerIOS', 14 | 'ImageStore', 15 | 'ListView', 16 | 'MaskedViewIOS', 17 | 'NetInfo', 18 | 'Picker', 19 | 'PickerIOS', 20 | 'ProgressBarAndroid', 21 | 'ProgressViewIOS', 22 | 'PushNotificationIOS', 23 | 'SegmentedControlIOS', 24 | 'Slider', 25 | 'StatusBarIOS', 26 | 'SwipeableListView', 27 | 'ViewPagerAndroid', 28 | 'WebView', 29 | 'DatePickerIOS', 30 | 'DatePickerAndroid', 31 | 'TimePickerAndroid', 32 | 'StatusBarIOS', 33 | ] 34 | 35 | export const ASSET_TYPES = ['component', 'directive', 'filter'] 36 | 37 | export const LIFECYCLE_HOOKS = [ 38 | 'beforeCreate', 39 | 'created', 40 | 'beforeMount', 41 | 'mounted', 42 | 'beforeUpdate', 43 | 'updated', 44 | 'beforeDestroy', 45 | 'destroyed', 46 | 'activated', 47 | 'deactivated', 48 | ] 49 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as V from "./vue"; 2 | import * as Options from "./options"; 3 | import * as Plugin from "./plugin"; 4 | import * as VNode from "./vnode"; 5 | 6 | // `Vue` in `export = Vue` must be a namespace 7 | // All available types are exported via this namespace 8 | declare namespace Vue { 9 | export type CreateElement = V.CreateElement; 10 | 11 | export type Component = Options.Component; 12 | export type AsyncComponent = Options.AsyncComponent; 13 | export type ComponentOptions<V extends Vue> = Options.ComponentOptions<V>; 14 | export type FunctionalComponentOptions = Options.FunctionalComponentOptions; 15 | export type RenderContext = Options.RenderContext; 16 | export type PropOptions = Options.PropOptions; 17 | export type ComputedOptions<V extends Vue> = Options.ComputedOptions<V>; 18 | export type WatchHandler<V extends Vue> = Options.WatchHandler<V, any>; 19 | export type WatchOptions = Options.WatchOptions; 20 | export type DirectiveFunction = Options.DirectiveFunction; 21 | export type DirectiveOptions = Options.DirectiveOptions; 22 | 23 | export type PluginFunction<T> = Plugin.PluginFunction<T>; 24 | export type PluginObject<T> = Plugin.PluginObject<T>; 25 | 26 | export type VNodeChildren = VNode.VNodeChildren; 27 | export type VNodeChildrenArrayContents = VNode.VNodeChildrenArrayContents; 28 | export type VNode = VNode.VNode; 29 | export type VNodeComponentOptions = VNode.VNodeComponentOptions; 30 | export type VNodeData = VNode.VNodeData; 31 | export type VNodeDirective = VNode.VNodeDirective; 32 | } 33 | 34 | // TS cannot merge imported class with namespace, declare a subclass to bypass 35 | declare class Vue extends V.Vue {} 36 | 37 | export = Vue; 38 | -------------------------------------------------------------------------------- /types/plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue as _Vue } from "./vue"; 2 | 3 | export type PluginFunction<T> = (Vue: typeof _Vue, options?: T) => void; 4 | 5 | export interface PluginObject<T> { 6 | install: PluginFunction<T>; 7 | [key: string]: any; 8 | } 9 | -------------------------------------------------------------------------------- /types/test/augmentation-test.ts: -------------------------------------------------------------------------------- 1 | import Vue = require("../index"); 2 | 3 | declare module "../vue" { 4 | // add instance property and method 5 | interface Vue { 6 | $instanceProperty: string; 7 | $instanceMethod(): void; 8 | } 9 | 10 | // add static property and method 11 | namespace Vue { 12 | const staticProperty: string; 13 | function staticMethod(): void; 14 | } 15 | } 16 | 17 | // augment ComponentOptions 18 | declare module "../options" { 19 | interface ComponentOptions<V extends Vue> { 20 | foo?: string; 21 | } 22 | } 23 | 24 | const vm = new Vue({ 25 | data: { 26 | a: true 27 | }, 28 | foo: "foo" 29 | }); 30 | 31 | vm.$instanceProperty; 32 | vm.$instanceMethod(); 33 | 34 | Vue.staticProperty; 35 | Vue.staticMethod(); 36 | -------------------------------------------------------------------------------- /types/test/plugin-test.ts: -------------------------------------------------------------------------------- 1 | import Vue = require("../index"); 2 | import { PluginFunction, PluginObject } from "../index"; 3 | 4 | class Option { 5 | prefix: string; 6 | suffix: string; 7 | } 8 | 9 | const plugin: PluginObject<Option> = { 10 | install(Vue, option) { 11 | if (typeof option !== "undefined") { 12 | const {prefix, suffix} = option; 13 | } 14 | } 15 | } 16 | const installer: PluginFunction<Option> = function(Vue, option) { } 17 | 18 | Vue.use(plugin, new Option); 19 | Vue.use(installer, new Option); 20 | -------------------------------------------------------------------------------- /types/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "es5", 6 | "dom", 7 | "es2015.promise", 8 | "es2015.core" 9 | ], 10 | "module": "commonjs", 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "noEmit": true 14 | }, 15 | "files": [ 16 | "../index.d.ts", 17 | "../options.d.ts", 18 | "../plugin.d.ts", 19 | "../vnode.d.ts", 20 | "../vue.d.ts", 21 | "options-test.ts", 22 | "plugin-test.ts", 23 | "vue-test.ts", 24 | "augmentation-test.ts" 25 | ], 26 | "compileOnSave": false 27 | } 28 | -------------------------------------------------------------------------------- /types/test/vue-test.ts: -------------------------------------------------------------------------------- 1 | import Vue = require("../index"); 2 | 3 | class Test extends Vue { 4 | a: number; 5 | 6 | testProperties() { 7 | this.$data; 8 | this.$el; 9 | this.$options; 10 | this.$parent; 11 | this.$root; 12 | this.$children; 13 | this.$refs; 14 | this.$slots; 15 | this.$isServer; 16 | } 17 | 18 | // test property reification 19 | $refs: { 20 | vue: Vue, 21 | element: HTMLInputElement, 22 | vues: Vue[], 23 | elements: HTMLInputElement[] 24 | } 25 | testReification() { 26 | this.$refs.vue.$data; 27 | this.$refs.element.value; 28 | this.$refs.vues[0].$data; 29 | this.$refs.elements[0].value; 30 | } 31 | 32 | testMethods() { 33 | this.$mount("#app", false); 34 | this.$forceUpdate(); 35 | this.$destroy(); 36 | this.$set({}, "key", "value"); 37 | this.$delete({}, "key"); 38 | this.$watch("a", (val: number, oldVal: number) => {}, { 39 | immediate: true, 40 | deep: false 41 | })(); 42 | this.$watch(() => this.a, (val: number) => {}); 43 | this.$on("", () => {}); 44 | this.$once("", () => {}); 45 | this.$off("", () => {}); 46 | this.$emit("", 1, 2, 3); 47 | this.$nextTick(function() { 48 | this.$nextTick; 49 | }); 50 | this.$nextTick().then(() => {}); 51 | this.$createElement("div", {}, "message"); 52 | } 53 | 54 | static testConfig() { 55 | const { config } = this; 56 | config.silent; 57 | config.optionMergeStrategies; 58 | config.devtools; 59 | config.errorHandler = (err, vm) => { 60 | if (vm instanceof Test) { 61 | vm.testProperties(); 62 | vm.testMethods(); 63 | } 64 | }; 65 | config.keyCodes = { esc: 27 }; 66 | } 67 | 68 | static testMethods() { 69 | this.extend({ 70 | data() { 71 | return { 72 | msg: "" 73 | }; 74 | } 75 | }); 76 | this.nextTick(() => {}); 77 | this.nextTick().then(() => {}); 78 | this.set({}, "", ""); 79 | this.set([true, false, true], 1, true); 80 | this.delete({}, ""); 81 | this.directive("", {bind() {}}); 82 | this.filter("", (value: number) => value); 83 | this.component("", { data: () => ({}) }); 84 | this.component("", { functional: true }); 85 | this.use; 86 | this.mixin(Test); 87 | this.compile("<div>{{ message }}</div>"); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /types/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue", 3 | "main": "index.d.ts" 4 | } 5 | -------------------------------------------------------------------------------- /types/vnode.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from "./vue"; 2 | 3 | export type ScopedSlot = (props: any) => VNodeChildrenArrayContents | string; 4 | 5 | export type VNodeChildren = VNodeChildrenArrayContents | [ScopedSlot] | string; 6 | export interface VNodeChildrenArrayContents { 7 | [x: number]: VNode | string | VNodeChildren; 8 | } 9 | 10 | export interface VNode { 11 | tag?: string; 12 | data?: VNodeData; 13 | children?: VNode[]; 14 | text?: string; 15 | elm?: Node; 16 | ns?: string; 17 | context?: Vue; 18 | key?: string | number; 19 | componentOptions?: VNodeComponentOptions; 20 | componentInstance?: Vue; 21 | parent?: VNode; 22 | raw?: boolean; 23 | isStatic?: boolean; 24 | isRootInsert: boolean; 25 | isComment: boolean; 26 | } 27 | 28 | export interface VNodeComponentOptions { 29 | Ctor: typeof Vue; 30 | propsData?: Object; 31 | listeners?: Object; 32 | children?: VNodeChildren; 33 | tag?: string; 34 | } 35 | 36 | export interface VNodeData { 37 | key?: string | number; 38 | slot?: string; 39 | scopedSlots?: { [key: string]: ScopedSlot }; 40 | ref?: string; 41 | tag?: string; 42 | staticClass?: string; 43 | class?: any; 44 | staticStyle?: { [key: string]: any }; 45 | style?: Object[] | Object; 46 | props?: { [key: string]: any }; 47 | attrs?: { [key: string]: any }; 48 | domProps?: { [key: string]: any }; 49 | hook?: { [key: string]: Function }; 50 | on?: { [key: string]: Function | Function[] }; 51 | nativeOn?: { [key: string]: Function | Function[] }; 52 | transition?: Object; 53 | show?: boolean; 54 | inlineTemplate?: { 55 | render: Function; 56 | staticRenderFns: Function[]; 57 | }; 58 | directives?: VNodeDirective[]; 59 | keepAlive?: boolean; 60 | } 61 | 62 | export interface VNodeDirective { 63 | readonly name: string; 64 | readonly value: any; 65 | readonly oldValue: any; 66 | readonly expression: any; 67 | readonly arg: string; 68 | readonly modifiers: { [key: string]: boolean }; 69 | } 70 | --------------------------------------------------------------------------------