Repository: GeekyAnts/vue-native-core Branch: master Commit: 0c705bebec66 Files: 237 Total size: 523.2 KB Directory structure: gitextract_npbnms5j/ ├── .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.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/ │ │ │ │ ├── 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 │ │ │ ├── compiler.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/ │ │ │ ├── directives/ │ │ │ │ ├── html.js │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ └── text.js │ │ │ ├── index.js │ │ │ ├── modules/ │ │ │ │ ├── class.js │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ └── util.js │ │ ├── compiler.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 │ │ ├── runtime-with-compiler.js │ │ ├── runtime.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 ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "env": { "test": { "presets": [ ["@babel/preset-env", { "targets": {"node": "current"}, "modules": "commonjs" }], "@babel/preset-flow" ] } } } ================================================ FILE: .eslintignore ================================================ flow dist packages ================================================ FILE: .eslintrc ================================================ { "root": true, "parserOptions": { "parser": "babel-eslint", "ecmaVersion": 2018, "sourceType": "module" }, "env": { "es6": true, "node": true, "browser": true }, "plugins": [ "flowtype", "prettier" ], "extends": [ "eslint:recommended", "plugin:flowtype/recommended", "plugin:prettier/recommended" ], "rules": { "prettier/prettier": ["error"], "no-console": 0, "no-useless-escape": 0, "no-empty": 0, "semi": ["error", "never"], "comma-dangle": ["error", "always-multiline"] }, "globals": { "__WEEX__": true } } ================================================ FILE: .flowconfig ================================================ [ignore] .*/__tests__/.* .*/node_modules/.* .*/examples/.* .*/packages/.* .*/scripts/.* [include] [libs] flow [options] unsafe.enable_getters_and_setters=true module.name_mapper='^compiler/\(.*\)$' -> '/src/compiler/\1' module.name_mapper='^core/\(.*\)$' -> '/src/core/\1' module.name_mapper='^shared/\(.*\)$' -> '/src/shared/\1' module.name_mapper='^web/\(.*\)$' -> '/src/platforms/web/\1' module.name_mapper='^weex/\(.*\)$' -> '/src/platforms/weex/\1' module.name_mapper='^vue-native/\(.*\)$' -> '/src/platforms/vue-native/\1' module.name_mapper='^server/\(.*\)$' -> '/src/server/\1' module.name_mapper='^entries/\(.*\)$' -> '/src/entries/\1' module.name_mapper='^sfc/\(.*\)$' -> '/src/sfc/\1' suppress_comment= \\(.\\|\n\\)*\\$flow-disable-line ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: vue-native-core ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel custom: # Replace with a single custom sponsorship URL ================================================ FILE: .github/ISSUE_TEMPLATE/---bug-report.md ================================================ --- name: "\U0001F41E Bug report" about: Spotted a bug in Vue Native? title: '' labels: '' --- **Description of the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **What I expected** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Did I use `vue-native-cli` to create the project?** Yes/No **Am I using Expo?** Yes/No - If you created your project with `vue-native init ` then yes - If you used `vue-native init --no-expo`, then no **Development platform (please complete the following information):** - OS: [e.g. iOS] - Shell/terminal: [e.g. ZSH, PowerShell] **The device on which I run my Vue Native app** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/---issue-with-the-documentation-or-website.md ================================================ --- name: "\U0001F4D6 Issue with the documentation or website" about: Spotted something incorrect or outdated in the docs? Website not rendering correctly? title: '' labels: '' --- This repository only hosts the core packages of Vue Native. 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). Issues opened here that are related to the documentation/website will be closed. ================================================ FILE: .github/ISSUE_TEMPLATE/---issue-with-vue-native-router.md ================================================ --- name: "\U0001F9ED Issue with Vue Native Router" about: Having trouble with Vue Native Router? title: '' labels: '' --- This repository only hosts the core packages of Vue Native. 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). Issues opened here that are related to Vue Native Router will be closed. ================================================ FILE: .github/ISSUE_TEMPLATE/---questions-or-help-with-usage.md ================================================ --- name: "\U0001F9D0 Questions or help with usage" about: Need help with code or Vue Native usage? title: 'Question: ' labels: '' --- The best place to find answers is the [documentation](https://www.vue-native.io/docs/installation.html) 📖 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/) ================================================ FILE: .gitignore ================================================ .DS_Store .vscode .idea node_modules *.log dist/*.gz dist/*.map dist/vue.common.min.js coverage packages/**/build.js packages/**/package-lock.json packages/**/.watchmanconfig ================================================ FILE: .prettierrc ================================================ { "parser": "babel", "semi": false, "trailingComma": "all", "singleQuote": true } ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. ## [ 0.0.1 ] - 2018-08-01 ### :zap: Improvements - Support for v-model in switch. - `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. - Named Slots support and Basic scoped slots support. ================================================ FILE: COMPONENT.md ================================================ # Vue Component API > Vue component actually runs in the React runtime. It means, you can not access [VNode](https://vuejs.org/v2/api/#VNode-Interface), you can not use [Render](https://vuejs.org/v2/guide/render-function.html). Conversely, if your previous Vue component does not involve both things, it would most likely run directly in React. * web: [react-vue-loader](https://github.com/SmallComfort/react-vue-loader) ([demo](https://github.com/SmallComfort/react-vue-material-demo)) * native: [vue-native-scripts](https://github.com/GeekyAnts/vue-native-core/tree/master/packages/vue-native-scripts) ([demo](https://github.com/SmallComfort/HackerNews)) ## Supported API #### Global Config > Use the react-vue-loader options: [```vue```](https://github.com/SmallComfort/react-vue-loader#additional-options) * [silent](https://vuejs.org/v2/api/#silent) * [optionMergeStrategies](https://vuejs.org/v2/api/#optionMergeStrategies) * [errorHandler](https://vuejs.org/v2/api/#errorHandler) * [ignoredElements](https://vuejs.org/v2/api/#ignoredElements) (web only) * [keyCodes](https://vuejs.org/v2/api/#keyCodes) (web only) #### Global API > Use the react-vue-loader options: [```vue```](https://github.com/SmallComfort/react-vue-loader#additional-options) * [Vue.extend](https://vuejs.org/v2/api/#Vue-extend) * [Vue.nextTick](https://vuejs.org/v2/api/#Vue-nextTick) * [Vue.set](https://vuejs.org/v2/api/#Vue-set) * [Vue.delete](https://vuejs.org/v2/api/#Vue-delete) * [Vue.directive](https://vuejs.org/v2/api/#Vue-directive) (web only) > [partial support](https://github.com/SmallComfort/react-vue/blob/dev/packages/react-vue/COMPONENT.md#options--assets) * [Vue.component](https://vuejs.org/v2/api/#Vue-component) * [Vue.use](https://vuejs.org/v2/api/#Vue-use) * [Vue.mixin](https://vuejs.org/v2/api/#Vue-mixin) * [Vue.version](https://vuejs.org/v2/api/#Vue-version) #### Options / Data * [data](https://vuejs.org/v2/api/#data) * [props](https://vuejs.org/v2/api/#props) * [computed](https://vuejs.org/v2/api/#computed) * [methods](https://vuejs.org/v2/api/#methods) * [watch](https://vuejs.org/v2/api/#watch) #### Options / Lifecycle Hooks * [beforeCreate](https://vuejs.org/v2/api/#beforeCreate) * [created](https://vuejs.org/v2/api/#created) * [beforeMount](https://vuejs.org/v2/api/#beforeMount) * [mounted](https://vuejs.org/v2/api/#mounted) * [beforeUpdate](https://vuejs.org/v2/api/#beforeUpdate) * [updated](https://vuejs.org/v2/api/#updated) * [beforeDestroy](https://vuejs.org/v2/api/#beforeDestroy) #### Options / Assets * [directives](https://vuejs.org/v2/api/#directives) (web only) * [Hook-Functions](https://vuejs.org/v2/guide/custom-directive.html#Hook-Functions) * bind * update * unbind * [Directive-Hook-Arguments](https://vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments) * el * binding * vnode (only one property ```context```) * [components](https://vuejs.org/v2/api/#components) #### Options / Composition * [mixins](https://vuejs.org/v2/api/#mixins) * [extends](https://vuejs.org/v2/api/#extends) > Only options object is allowed #### Options / Misc * [name](https://vuejs.org/v2/api/#name) #### Instance Properties * [vm.$data](https://vuejs.org/v2/api/#vm-data) * [vm.$props](https://vuejs.org/v2/api/#vm-props) * [vm.$el](https://vuejs.org/v2/api/#vm-el) * [vm.$options](https://vuejs.org/v2/api/#vm-options) * [vm.$parent](https://vuejs.org/v2/api/#vm-parent) * [vm.$root](https://vuejs.org/v2/api/#vm-root) * [vm.$children](https://vuejs.org/v2/api/#vm-children) * [vm.$slots](https://vuejs.org/v2/api/#vm-slots) * [vm.$refs](https://vuejs.org/v2/api/#vm-refs) #### Instance Methods / Data * [vm.$watch](https://vuejs.org/v2/api/#vm-watch) * [vm.$set](https://vuejs.org/v2/api/#vm-set) * [vm.$delete](https://vuejs.org/v2/api/#vm-delete) #### Instance Methods / Events * [vm.$on](https://vuejs.org/v2/api/#vm-on) * [vm.$once](https://vuejs.org/v2/api/#vm-once) * [vm.$off](https://vuejs.org/v2/api/#vm-off) * [vm.$emit](https://vuejs.org/v2/api/#vm-emit) #### Instance Methods / Lifecycle * [vm.$forceUpdate](https://vuejs.org/v2/api/#vm-forceUpdate) * [vm.$nextTick](https://vuejs.org/v2/api/#vm-nextTick) #### Directives * [v-text](https://vuejs.org/v2/api/#v-text) (web only) * [v-html](https://vuejs.org/v2/api/#v-html) (web only) * [v-show](https://vuejs.org/v2/api/#v-show) * [v-if](https://vuejs.org/v2/api/#v-if) * [v-else](https://vuejs.org/v2/api/#v-else) * [v-else-if](https://vuejs.org/v2/api/#v-else-if) * [v-for](https://vuejs.org/v2/api/#v-for) * [v-on](https://vuejs.org/v2/api/#v-on) (web only) * [v-bind](https://vuejs.org/v2/api/#v-bind) * [v-model](https://vuejs.org/v2/api/#v-model) (web only) > Unsupported features: [value bindings checkbox](https://vuejs.org/v2/guide/forms.html#Checkbox-1) & [value bindings select options](https://vuejs.org/v2/guide/forms.html#Select-Options) #### Special Attributes * [key](https://vuejs.org/v2/api/#key) * [ref](https://vuejs.org/v2/api/#ref) * [slot](https://vuejs.org/v2/api/#slot) * [is](https://vuejs.org/v2/api/#is) #### Built-In Components * [component](https://vuejs.org/v2/api/#component) * [transition](https://vuejs.org/v2/api/#transition) (web only) > The \ would render an extra DOM element, you can configure ```tag``` attribute to custom the extra DOM element > Unsupported features: [transition modes](https://vuejs.org/v2/guide/transitions.html#Transition-Modes) * [slot](https://vuejs.org/v2/api/#slot) #### Server-Side Rendering (web only) > Use React Server Rendering ## Unsupported API #### Global Config * ~~[performance](https://vuejs.org/v2/api/#performance)~~ * ~~[devtools](https://vuejs.org/v2/api/#devtools)~~ * ~~[productionTip](https://vuejs.org/v2/api/#productionTip)~~ #### Global API * ~~[Vue.filter](https://vuejs.org/v2/api/#Vue-filter)~~ * ~~[Vue.compile](https://vuejs.org/v2/api/#Vue-compile)~~ #### Options / Data * ~~[propsData](https://vuejs.org/v2/api/#propsData)~~ #### Options / DOM * ~~[el](https://vuejs.org/v2/api/#el)~~ * ~~[template](https://vuejs.org/v2/api/#template)~~ * ~~[render](https://vuejs.org/v2/api/#render)~~ * ~~[renderError](https://vuejs.org/v2/api/#renderError)~~ #### Options / Lifecycle Hooks * ~~[activated](https://vuejs.org/v2/api/#activated)~~ * ~~[deactivated](https://vuejs.org/v2/api/#deactivated)~~ * ~~[destroyed](https://vuejs.org/v2/api/#destroyed)~~ #### Options / Assets * ~~[filters](https://vuejs.org/v2/api/#filters)~~ #### Options / Misc * ~~[delimiters](https://vuejs.org/v2/api/#delimiters)~~ * ~~[functional](https://vuejs.org/v2/api/#functional)~~ * ~~[model](https://vuejs.org/v2/api/#model)~~ #### Instance Properties * ~~[vm.$scopedSlots](https://vuejs.org/v2/api/#vm-scopedSlots)~~ * ~~[vm.$isServer](https://vuejs.org/v2/api/#vm-isServer)~~ #### Instance Methods / Lifecycle * ~~[vm.$mount](https://vuejs.org/v2/api/#vm-mount)~~ * ~~[vm.$destroy](https://vuejs.org/v2/api/#vm-destroy)~~ #### Directives * ~~[v-pre](https://vuejs.org/v2/api/#v-pre)~~ * ~~[v-cloak](https://vuejs.org/v2/api/#v-cloak)~~ * ~~[v-once](https://vuejs.org/v2/api/#v-once)~~ #### Built-In Components * ~~[transition-group](https://vuejs.org/v2/api/#transition-group)~~ * ~~[keep-alive](https://vuejs.org/v2/api/#keep-alive)~~ #### ~~VNode Interface~~ ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013-present, Yuxi (Evan) You Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ ## ⚠️ This project has been deprecated and is no longer maintained ⚠️ # Vue Native [![No Maintenance Intended](https://unmaintained.tech/badge.svg)](http://unmaintained.tech/) Visit our website at [vue-native.io](https://vue-native.io) or read the official documentation [here](https://vue-native.io/docs/installation.html). ## Build native mobile apps using Vue Vue Native is a framework to build cross platform native mobile apps using JavaScript. It is a wrapper around the APIs of React Native. So, with Vue Native, you can do everything that you can do with React Native. With Vue Native, you get - **The simplicity of Vue.js.** Incrementally build user interfaces with the familiar syntax of HTML and CSS in single file components. - **Seamless interop with React Native.** Use core React Native components with Vue.js syntax out of the box to develop mobile apps on both iOS and Android. ## Contents - [Documentation](#documentation) - [Installation](#installation) - [Project setup with Vue Native CLI](#project-setup-with-vue-native-cli) - [Using Vue Native with a pre-existing React Native or Expo project](#using-vue-native-in-a-react-native-project) - [Available React Native components](#available-react-native-components) - [Contributors](#contributors) - [License](#license) - [Special thanks](#credits-to-react-vue) ## Documentation You can find the full documentation for Vue Native [on this website](https://vue-native.io/docs/installation.html). It covers installation and setup, component basics, routing, testing, the internal API and more. The source for the Vue Native documentation and website is hosted on a separate repo, [here](https://github.com/GeekyAnts/vue-native-website) ## About Vue Native Vue Native is developed and maintained by [GeekyAnts](https://geekyants.com?utm_source=github&utm_medium=opensource&utm_campaign=vue-native-core), specialists in [cross-platform mobile development](https://geekyants.com/solution/universal-and-cross-platform-app-development-services?utm_source=github&utm_medium=opensource&utm_campaign=vue-native-core). ### Why Choose Vue Native? - Write Vue.js code, get native mobile apps - Leverage existing Vue.js knowledge - Access React Native's ecosystem ### Professional Services Looking to build a production app with Vue Native? Our [mobile engineering team](https://geekyants.com/service/hire-mobile-app-development-services?utm_source=github&utm_medium=opensource&utm_campaign=vue-native-core) can help you: - Architect scalable mobile applications - Migrate from other frameworks - Optimize app performance [Get in touch](https://geekyants.com/hire?utm_source=github&utm_medium=opensource&utm_campaign=vue-native-core) ## Installation To install Vue Native's official CLI, run ``` $ npm install vue-native-cli -g ``` To use the CLI, you must have either [expo-cli](https://github.com/expo/expo-cli) or [react-native-cli](https://github.com/react-native-community/cli) installed globally. ## Project setup with Vue Native CLI The Vue Native CLI can be used to easily generate a fully configured Vue Native app. It wraps `expo-cli` and `react-native-cli` to generate a simple single page application (SPA) after installing and configuring dependencies from [vue-native-core](https://github.com/GeekyAnts/vue-native-core). You should have either expo-cli or react-native-cli installed as a global dependency. Refer to the [installation guide](https://vue-native.io/docs/installation.html) for details on project setup. The GitHub repository for Vue Native CLI is hosted [here](https://github.com/GeekyAnts/vue-native-cli). With the CLI, generating a Vue Native project is as easy as running the command ``` $ vue-native init ``` ## Using Vue Native in a React Native project It is possible to integrate Vue Native into a pre-existing React Native project. You can find instructions to do this [here](converting-react-native-project.md). These instructions can also be used to set up a Vue Native project from scratch. ## Available React Native components All the core components of React Native 0.63 onwards are globally registered and available to use in templates without the need to import and locally register. [The components and their React Native documentation can be found here.](https://reactnative.dev/docs/components-and-apis) All other components that were previously available, but then deprecated from React Native can still be used by installing their respective packages. For example, to use `WebView`, use the [react-native-webview](https://github.com/react-native-webview/react-native-webview) package. ## Contributors This project exists thanks to all the people who contribute. Maintained by [GeekyAnts](https://geekyants.com?utm_source=github&utm_medium=opensource&utm_campaign=vue-native-core) - [Cross-Platform Development Experts](https://geekyants.com/solution/universal-and-cross-platform-app-development-services?utm_source=github&utm_medium=opensource&utm_campaign=vue-native-core) ## License [MIT](http://opensource.org/licenses/MIT) ## Credits to [react-vue](https://github.com/SmallComfort/react-vue) A huge thanks to the author of react-vue for most of the work on Vue Native. ================================================ FILE: __tests__/unit/features/instance/init.spec.js ================================================ import Vue from 'vue-native/index' describe('Initialization', () => { function noop () {} let asserted function createCompareFn (spy) { const hasWarned = msg => { var count = spy.calls.count() var args while (count--) { args = spy.calls.argsFor(count) if (args.some(containsMsg)) { return true } } function containsMsg (arg) { return arg.toString().indexOf(msg) > -1 } } return { compare: msg => { asserted = asserted.concat(msg) var warned = Array.isArray(msg) ? msg.some(hasWarned) : hasWarned(msg) return { pass: warned, message: warned ? 'Expected message "' + msg + '" not to have been warned' : 'Expected message "' + msg + '" to have been warned' } } } } // define custom matcher for warnings beforeEach(() => { asserted = [] spyOn(console, 'warn') spyOn(console, 'error') jasmine.addMatchers({ toHaveBeenWarned: () => createCompareFn(console.error), toHaveBeenTipped: () => createCompareFn(console.warn) }) }) afterEach(done => { const warned = msg => asserted.some(assertedMsg => msg.toString().indexOf(assertedMsg) > -1) let count = console.error.calls.count() let args while (count--) { args = console.error.calls.argsFor(count) if (!warned(args[0])) { done.fail(`Unexpected console.error message: ${args[0]}`) return } } done() }) it('without new', () => { try { Vue() } catch (e) {} expect('Vue is a constructor and should be called with the `new` keyword').toHaveBeenWarned() }) it('with new', () => { expect(new Vue() instanceof Vue).toBe(true) }) }) ================================================ FILE: converting-react-native-project.md ================================================ ## Setting up a React Native project for Vue Native [Vue Native CLI](https://github.com/GeekyAnts/vue-native-cli) is the recommended way to setup a new Vue Native project. However, if you wish to setup a project from scratch, use the following steps after setting up a React Native / Expo project. ### Step 1: Install The following packages are required as runtime dependencies by Vue Native: - [vue-native-core](https://www.npmjs.com/package/vue-native-core) - [vue-native-helper](https://www.npmjs.com/package/vue-native-helper) During development, another package is required to transpile Vue Native component files (with `.vue` extensions) into JS: - [vue-native-scripts](https://www.npmjs.com/package/vue-native-scripts) To install them, run the following commands in your project directory ``` $ npm install --save vue-native-core vue-native-helper $ npm install --save-dev vue-native-scripts ``` ### Step 2: Configure the React Native bundler The Metro bundler is used by React Native to generate app bundles. It can be configured using a `metro.config.js` file. Add the following to your `metro.config.js` (make one to your project's root if you don't have one already): ```js const { getDefaultConfig } = require("metro-config"); module.exports = (async () => { const { resolver: { sourceExts } } = await getDefaultConfig(); return { transformer: { babelTransformerPath: require.resolve("./vueTransformerPlugin.js"), getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, inlineRequires: false, }, }) }, resolver: { sourceExts: [...sourceExts, "vue"] } }; })(); ``` #### NOTE to Expo users: The `app.json` file must be modified to allow `.vue` files to be recognised. ```diff { "expo": { "sdkVersion": "34.0.0", "platforms": [ "ios", "android", "web" ], ... "packagerOpts": { + "sourceExts": ["js", "json", "ts", "tsx", "vue"], "config": "metro.config.js" } } } ``` The `babelTransformPath` property above takes the path to the transformer you wish to use. In our case, we need to create a `vueTransformerPlugin.js` file to the project's root and specify supported extensions: ```js const vueNativeScripts = require("vue-native-scripts"); const upstreamTransformer = require("metro-react-native-babel-transformer"); const vueExtensions = ["vue"]; // <-- Add other extensions if needed. module.exports.transform = function ({ src, filename, options }) { if (vueExtensions.some(ext => filename.endsWith("." + ext))) { return vueNativeScripts.transform({ src, filename, options }); } return upstreamTransformer.transform({ src, filename, options }); }; ``` This file conditionally transforms files based on their extensions. `vue-native-scripts` is used for `.vue` files, while the stock React Native Babel transformer is used for other (JS) files. ## Using Vue Native components and `.vue` files In the React Native application, you can simply `import` your Vue components as follows ``` import VueComponent from './VueComponent.vue' ``` There should be a file named `VueComponent.vue` in the corresponding folder; the transformer parses this file and sends it to the React Native bundler. ================================================ FILE: flow/compiler.js ================================================ declare type CompilerOptions = { warn?: Function; // allow customizing warning in different environments; e.g. node expectHTML?: boolean; // only false for non-web builds modules?: Array; // platform specific modules; e.g. style; class staticKeys?: string; // a list of AST properties to be considered static; for optimization directives?: { [key: string]: Function }; // platform specific directives isUnaryTag?: (tag: string) => ?boolean; // check if a tag is unary for the platform canBeLeftOpenTag?: (tag: string) => ?boolean; // check if a tag can be left opened isReservedTag?: (tag: string) => ?boolean; // check if a tag is a native for the platform mustUseProp?: (tag: string, type: ?string, name: string) => boolean; // check if an attribute should be bound as a property isPreTag?: (attr: string) => ?boolean; // check if a tag needs to preserve whitespace getTagNamespace?: (tag: string) => ?string; // check the namespace for a tag transforms?: Array; // a list of transforms on parsed AST before codegen preserveWhitespace?: boolean; isFromDOM?: boolean; shouldDecodeTags?: boolean; shouldDecodeNewlines?: boolean; // runtime user-configurable delimiters?: [string, string]; // template delimiters } declare type CompiledResult = { ast: ?ASTElement; render: string; staticRenderFns: Array; errors?: Array; tips?: Array; } declare type CompiledFunctionResult = { render: Function; staticRenderFns: Array; } declare type ModuleOptions = { preTransformNode: (el: ASTElement) => void; transformNode: (el: ASTElement) => void; // transform an element's AST node postTransformNode: (el: ASTElement) => void; genData: (el: ASTElement) => string; // generate extra data string for an element transformCode?: (el: ASTElement, code: string) => string; // further transform generated code for an element staticKeys?: Array; // AST properties to be considered static } declare type ASTModifiers = { [key: string]: boolean } declare type ASTIfConditions = Array<{ exp: ?string; block: ASTElement }> declare type ASTElementHandler = { value: string; modifiers: ?ASTModifiers; } declare type ASTElementHandlers = { [key: string]: ASTElementHandler | Array; } declare type ASTDirective = { name: string; rawName: string; value: string; arg: ?string; modifiers: ?ASTModifiers; } declare type ASTNode = ASTElement | ASTText | ASTExpression declare type ASTElement = { type: 1; tag: string; attrsList: Array<{ name: string; value: string }>; attrsMap: { [key: string]: string | null }; parent: ASTElement | void; children: Array; static?: boolean; staticRoot?: boolean; staticInFor?: boolean; staticProcessed?: boolean; hasBindings?: boolean; text?: string; attrs?: Array<{ name: string; value: string }>; props?: Array<{ name: string; value: string }>; plain?: boolean; pre?: true; ns?: string; component?: string; inlineTemplate?: true; transitionMode?: string | null; slotName?: ?string; slotTarget?: ?string; slotScope?: ?string; scopedSlots?: { [name: string]: ASTElement }; ref?: string; refInFor?: boolean; if?: string; ifProcessed?: boolean; elseif?: string; else?: true; ifConditions?: ASTIfConditions; for?: string; forProcessed?: boolean; key?: string; alias?: string; iterator1?: string; iterator2?: string; staticClass?: string; classBinding?: string; staticStyle?: string; styleBinding?: string; events?: ASTElementHandlers; nativeEvents?: ASTElementHandlers; transition?: string | true; transitionOnAppear?: boolean; model?: { value: string; callback: string; expression: string; }; directives?: Array; forbidden?: true; once?: true; onceProcessed?: boolean; wrapData?: (code: string) => string; // weex specific appendAsTree?: boolean; } declare type ASTExpression = { type: 2; expression: string; text: string; static?: boolean; } declare type ASTText = { type: 3; text: string; static?: boolean; } // SFC-parser related declarations // an object format describing a single-file component. declare type SFCDescriptor = { template: ?SFCBlock; script: ?SFCBlock; styles: Array; customBlocks: Array; } declare type SFCCustomBlock = { type: string; content: string; start?: number; end?: number; src?: string; attrs: {[attribute:string]: string}; } declare type SFCBlock = { type: string; content: string; start?: number; end?: number; lang?: string; src?: string; scoped?: boolean; module?: string | boolean; } ================================================ FILE: flow/component.js ================================================ import type { Config } from '../src/core/config' import type VNode from '../src/core/vdom/vnode' import type Watcher from '../src/core/observer/watcher' declare interface Component { // constructor information static cid: number; static options: Object; // extend static extend: (options: Object) => Function; static superOptions: Object; static extendOptions: Object; static sealedOptions: Object; static super: Class; // assets static directive: (id: string, def?: Function | Object) => Function | Object | void; static component: (id: string, def?: Class | Object) => Class; static filter: (id: string, def?: Function) => Function | void; // public properties $el: any; // so that we can attach __vue__ to it $data: Object; $options: ComponentOptions; $parent: Component | void; $root: Component; $children: Array; $refs: { [key: string]: Component | Element | Array | void }; $slots: { [key: string]: Array }; $scopedSlots: { [key: string]: () => VNodeChildren }; $vnode: VNode; // the placeholder node for the component in parent's render tree $isServer: boolean; $props: Object; // public methods $mount: (el?: Element | string, hydrating?: boolean) => Component; $forceUpdate: () => void; $destroy: () => void; $set: (target: Object | Array, key: string | number, val: T) => T; $delete: (target: Object | Array, key: string | number) => void; $watch: (expOrFn: string | Function, cb: Function, options?: Object) => Function; $on: (event: string | Array, fn: Function) => Component; $once: (event: string, fn: Function) => Component; $off: (event?: string | Array, fn?: Function) => Component; $emit: (event: string, ...args: Array) => Component; $nextTick: (fn: Function) => void; $createElement: (tag?: string | Component, data?: Object, children?: VNodeChildren) => VNode; // private properties _uid: number; _name: string; // this only exists in dev mode _isVue: true; _self: Component; _renderProxy: Component; _renderContext: ?Component; _watcher: Watcher; _watchers: Array; _computedWatchers: { [key: string]: Watcher }; _data: Object; _props: Object; _events: Object; _inactive: boolean | null; _directInactive: boolean; _isMounted: boolean; _isDestroyed: boolean; _isBeingDestroyed: boolean; _vnode: ?VNode; // self root node _staticTrees: ?Array; _hasHookEvent: boolean; _provided: ?Object; // private methods // lifecycle _init: Function; _mount: (el?: Element | void, hydrating?: boolean) => Component; _update: (vnode: VNode, hydrating?: boolean) => void; // rendering _render: () => VNode; __patch__: (a: Element | VNode | void, b: VNode) => any; // createElement // _c is internal that accepts `normalizationType` optimization hint _c: (vnode?: VNode, data?: VNodeData, children?: VNodeChildren, normalizationType?: number) => VNode | void; // renderStatic _m: (index: number, isInFor?: boolean) => VNode | VNodeChildren; // markOnce _o: (vnode: VNode | Array, index: number, key: string) => VNode | VNodeChildren; // toString _s: (value: mixed) => string; // text to VNode _v: (value: string | number) => VNode; // toNumber _n: (value: string) => number | string; // empty vnode _e: () => VNode; // loose equal _q: (a: mixed, b: mixed) => boolean; // loose indexOf _i: (arr: Array, val: mixed) => number; // resolveFilter _f: (id: string) => Function; // renderList _l: (val: mixed, render: Function) => ?Array; // renderSlot _t: (name: string, fallback: ?Array, props: ?Object) => ?Array; // apply v-bind object _b: (data: any, value: any, asProp?: boolean) => VNodeData; // check custom keyCode _k: (eventKeyCode: number, key: string, builtInAlias: number | Array | void) => boolean; // resolve scoped slots _u: (scopedSlots: Array<[string, Function]>) => { [key: string]: Function }; // allow dynamic method registration [key: string]: any } ================================================ FILE: flow/global-api.js ================================================ declare interface GlobalAPI { cid: number; options: Object; config: Config; util: Object; extend: (options: Object) => Function; set: (target: Object | Array, key: string | number, value: T) => T; delete: (target: Object| Array, key: string | number) => void; nextTick: (fn: Function, context?: Object) => void; use: (plugin: Function | Object) => void; mixin: (mixin: Object) => void; compile: (template: string) => { render: Function, staticRenderFns: Array }; directive: (id: string, def?: Function | Object) => Function | Object | void; component: (id: string, def?: Class | Object) => Class; filter: (id: string, def?: Function) => Function | void; // allow dynamic method registration [key: string]: any } ================================================ FILE: flow/modules.js ================================================ declare module 'he' { declare function escape(html: string): string; declare function decode(html: string): string; } declare module 'source-map' { declare class SourceMapGenerator { setSourceContent(filename: string, content: string): void; addMapping(mapping: Object): void; toString(): string; } declare class SourceMapConsumer { originalPositionFor(position: { line: number; column: number; }): { source: ?string; line: ?number; column: ?number; }; } } declare module 'lru-cache' { declare var exports: { (): any } } declare module 'de-indent' { declare var exports: { (input: string): string } } declare module 'serialize-javascript' { declare var exports: { (input: string, options: { isJSON: boolean }): string } } declare module 'lodash.template' { declare var exports: { (input: string, options: { interpolate: RegExp, escape: RegExp }): Function } } declare module 'change-case' { declare var exports: { (): any } } ================================================ FILE: flow/options.js ================================================ declare type InternalComponentOptions = { _isComponent: true; parent: Component; propsData: ?Object; _parentVnode: VNode; _parentListeners: ?Object; _renderChildren: ?Array; _componentTag: ?string; _parentElm: ?Node; _refElm: ?Node; render?: Function; staticRenderFns?: Array } declare type ComponentOptions = { // data data: Object | Function | void; props?: { [key: string]: PropOptions }; propsData?: ?Object; computed?: { [key: string]: Function | { get?: Function; set?: Function; cache?: boolean } }; methods?: { [key: string]: Function }; watch?: { [key: string]: Function | string }; // DOM el?: string | Element; template?: string; render: (h: () => VNode) => VNode; renderError?: (h: () => VNode, err: Error) => VNode; staticRenderFns?: Array<() => VNode>; // lifecycle beforeCreate?: Function; created?: Function; beforeMount?: Function; mounted?: Function; beforeUpdate?: Function; updated?: Function; activated?: Function; deactivated?: Function; beforeDestroy?: Function; destroyed?: Function; // assets directives?: { [key: string]: Object }; components?: { [key: string]: Class }; transitions?: { [key: string]: Object }; filters?: { [key: string]: Function }; // context provide?: { [key: string | Symbol]: any } | () => { [key: string | Symbol]: any }; inject?: { [key: string]: string | Symbol } | Array; // component v-model customization model?: { prop?: string; event?: string; }; // misc parent?: Component; mixins?: Array; name?: string; extends?: Class | Object; delimiters?: [string, string]; // private _isComponent?: true; _propKeys?: Array; _parentVnode?: VNode; _parentListeners?: ?Object; _renderChildren?: ?Array; _componentTag: ?string; _scopeId: ?string; _base: Class; _parentElm: ?Node; _refElm: ?Node; } declare type PropOptions = { type: Function | Array | null; default: any; required: ?boolean; validator: ?Function; } ================================================ FILE: flow/ssr.js ================================================ declare type ComponentWithCacheContext = { type: 'ComponentWithCache'; bufferIndex: number; buffer: Array; key: string; } declare type ElementContext = { type: 'Element'; children: Array; rendered: number; endTag: string; total: number; } declare type ComponentContext = { type: 'Component'; prevActive: Component; } declare type RenderState = ComponentContext | ComponentWithCacheContext | ElementContext ================================================ FILE: flow/vnode.js ================================================ declare type VNodeChildren = Array | string declare type VNodeComponentOptions = { Ctor: Class; propsData: ?Object; listeners: ?Object; children: ?Array; tag?: string; } declare type MountedComponentVNode = { context: Component; componentOptions: VNodeComponentOptions; componentInstance: Component; parent: VNode; data: VNodeData; } // interface for vnodes in update modules declare type VNodeWithData = { tag: string; data: VNodeData; children: ?Array; text: void; elm: any; ns: string | void; context: Component; key: string | number | void; parent?: VNodeWithData; componentInstance?: Component; isRootInsert: boolean; } declare interface VNodeData { key?: string | number; slot?: string; ref?: string; pre?: boolean; tag?: string; staticClass?: string; class?: any; staticStyle?: { [key: string]: any }; style?: Array | Object; normalizedStyle?: Object; props?: { [key: string]: any }; attrs?: { [key: string]: string }; domProps?: { [key: string]: any }; hook?: { [key: string]: Function }; on?: ?{ [key: string]: Function | Array }; nativeOn?: { [key: string]: Function | Array }; transition?: Object; show?: boolean; // marker for v-show inlineTemplate?: { render: Function; staticRenderFns: Array; }; directives?: Array; keepAlive?: boolean; scopedSlots?: { [key: string]: Function }; model?: { value: any; callback: Function; }; } declare type VNodeDirective = { name: string; rawName: string; value?: any; oldValue?: any; arg?: string; modifiers?: ASTModifiers; def?: Object; } ================================================ FILE: jest.config.json ================================================ { "verbose": true, "testMatch": [ "/**/__tests__/**/*.(test|spec).js" ], "moduleDirectories": [ "node_modules" ], "moduleNameMapper": { "^vue-native(.*)$": "/src/platforms/vue-native$1", "^vue(.*)$": "/src/platforms/web/runtime-with-compiler$1", "^compiler(.*)$": "/src/compiler$1", "^core(.*)$": "/src/core$1", "^shared(.*)$": "/src/shared$1", "^web(.*)$": "/src/platforms/web$1", "^sfc(.*)$": "/src/sfc$1" }, "collectCoverage": true, "collectCoverageFrom": [ "/src/platforms/vue-native/**/*.js", "!**/node_modules/**" ], "coverageDirectory": "/coverage", "testURL": "http://localhost" } ================================================ FILE: jsconfig.json ================================================ { "compilerOptions": { "moduleResolution": "node", "baseUrl": "src", "paths": { "vue/*": ["platforms/web/runtime-with-compiler/*"], "compiler/*": ["compiler/*"], "core/*": ["core/*"], "shared/*": ["shared/*"], "web/*": ["platforms/web/*"], "vue-native/*": ["platforms/vue-native/*"], "sfc/*": ["sfc/*"] } }, "exclude": ["node_modules", "packages"] } ================================================ FILE: package.json ================================================ { "name": "vue-native-core", "version": "0.3.1", "description": "Create mobile apps using vuejs", "main": "packages/vue-native-core/index.js", "typings": "types/index.d.ts", "repository": { "type": "git", "url": "https://github.com/GeekyAnts/vue-native-core.git" }, "keywords": [ "vue", "native", "mobile" ], "engines": { "node": "^4.5 || >=5.10" }, "engineStrict": true, "author": "Geekyants (https://geekyants.io/)", "license": "MIT", "bugs": { "url": "https://github.com/GeekyAnts/vue-native-core/issues" }, "files": [ "src", "packages", "types/*.d.ts" ], "scripts": { "lint": "eslint src scripts", "lint:fix": "eslint --fix src scripts", "flow": "flow check", "test:unit": "jest --config ./jest.config.json", "test": "npm run lint && npm run flow && npm run test:unit", "build": "node scripts/build.js", "release": "bash scripts/release.sh" }, "husky": { "hooks": { "pre-commit": "npm run lint:fix" } }, "homepage": "https://github.com/GeekyAnts/vue-native-core", "devDependencies": { "@babel/core": "^7.5.5", "@babel/preset-env": "^7.5.5", "@babel/preset-flow": "^7.0.0", "babel-core": "^6.9.0", "babel-eslint": "^10.0.3", "babel-helper-vue-jsx-merge-props": "^2.0.2", "babel-jest": "^26.0.1", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-syntax-jsx": "^6.18.0", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-plugin-transform-vue-jsx": "^3.2.0", "babel-preset-flow-vue": "^1.0.0", "babel-preset-react": "^6.24.1", "buble": "^0.19.8", "change-case": "^3.0.1", "cross-spawn": "^5.0.1", "de-indent": "^1.0.2", "eslint": "^6.5.1", "eslint-config-prettier": "^6.5.0", "eslint-plugin-flowtype": "^4.3.0", "eslint-plugin-prettier": "^3.1.1", "flow-bin": "^0.39.0", "hash-sum": "^1.0.2", "he": "^1.1.0", "husky": "^3.0.8", "jasmine": "^2.5.2", "jasmine-core": "^2.5.2", "jest": "^26.0.1", "js-beautify": "^1.6.14", "lodash": "^4.17.20", "lodash.template": "^4.4.0", "lru-cache": "^4.0.2", "prettier": "^1.18.2", "react": "16.8.6", "resolve": "^1.2.0", "rollup": "^1.17.0", "rollup-plugin-alias": "^1.5.2", "rollup-plugin-buble": "^0.19.8", "rollup-plugin-flow-no-whitespace": "^1.0.0", "rollup-plugin-prettier": "^0.6.0", "rollup-plugin-replace": "^2.2.0", "serialize-javascript": "^3.1.0", "typescript": "^2.1.6", "uglify-js": "^3.6.0" } } ================================================ FILE: packages/vue-native-core/README.md ================================================ # vue-native-core > 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). Find the `vue-native-core` repository [here](https://github.com/GeekyAnts/vue-native-core). For the official documentation, visit [this](https://vue-native.io/docs/installation.html) website. ================================================ FILE: packages/vue-native-core/index.js ================================================ module.exports = require('./build') ================================================ FILE: packages/vue-native-core/package.json ================================================ { "name": "vue-native-core", "version": "0.3.1", "description": "Library with core functionalities to create Vue Native components", "main": "index.js", "repository": { "type": "git", "url": "git+https://github.com/GeekyAnts/vue-native-core" }, "keywords": [ "react", "vue" ], "author": "Geekyants (https://geekyants.io/)", "license": "MIT", "bugs": { "url": "https://github.com/GeekyAnts/vue-native-core/issues" }, "homepage": "https://github.com/GeekyAnts/vue-native-core/tree/master/packages/vue-native-core", "peerDependencies": { "react": "^16.3.0" } } ================================================ FILE: packages/vue-native-helper/README.md ================================================ # vue-native-helper > 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). Find the `vue-native-core` repository [here](https://github.com/GeekyAnts/vue-native-core). For the official documentation, visit [this](https://vue-native.io/docs/installation.html) website. ================================================ FILE: packages/vue-native-helper/index.js ================================================ module.exports = require('./build') ================================================ FILE: packages/vue-native-helper/package.json ================================================ { "name": "vue-native-helper", "version": "0.3.1", "description": "Helper library with utilities for vue-native-core", "main": "index.js", "repository": { "type": "git", "url": "git+https://github.com/GeekyAnts/vue-native-core" }, "keywords": [ "react", "vue", "helper" ], "author": "Geekyants (https://geekyants.io/)", "license": "MIT", "bugs": { "url": "https://github.com/GeekyAnts/vue-native-core/issues" }, "homepage": "https://github.com/GeekyAnts/vue-native-core#readme", "peerDependencies": { "react": "^16.3.0" } } ================================================ FILE: packages/vue-native-scripts/.npmignore ================================================ node_modules npm-debug.log test/output docs/_book .DS_Store .idea *.iml ================================================ FILE: packages/vue-native-scripts/README.md ================================================ # vue-native-scripts > This package is auto-generated. For pull requests please work with [src/platforms/vue-native/scripts/index.js](https://github.com/GeekyAnts/vue-native-core/tree/develop/src/platforms/vue-native/scripts). Compile and transform Vue components to React Native. Find the `vue-native-core` repository [here](https://github.com/GeekyAnts/vue-native-core). For the official documentation, visit [this](https://vue-native.io/docs/installation.html) website. ## Usage [Vue Native CLI](https://github.com/GeekyAnts/vue-native-cli) is the recommended way to setup a new Vue Native project. However, if you wish to setup a project from scratch, use the following steps after setting up a React Native / Expo project. ### Step 1: Install The following packages are required as runtime dependencies by Vue Native: - [vue-native-core](https://www.npmjs.com/package/vue-native-core) - [vue-native-helper](https://www.npmjs.com/package/vue-native-helper) During development, another package is required to transpile Vue Native component files (with `.vue` extensions) into JS: - [vue-native-scripts](https://www.npmjs.com/package/vue-native-scripts) To install them, run the following commands in your project directory ``` $ npm install --save vue-native-core vue-native-helper $ npm install --save-dev vue-native-scripts ``` ### Step 2: Configure the React Native bundler The Metro bundler is used by React Native to generate app bundles. It can be configured using a `metro.config.js` file. Add the following to your `metro.config.js` (make one to your project's root if you don't have one already): ```js const { getDefaultConfig } = require("metro-config"); module.exports = (async () => { const { resolver: { sourceExts } } = await getDefaultConfig(); return { transformer: { babelTransformerPath: require.resolve("./vueTransformerPlugin.js"), getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, inlineRequires: false, }, }) }, resolver: { sourceExts: [...sourceExts, "vue"] } }; })(); ``` The `babelTransformPath` property above takes the path to the transformer you wish to use. In our case, we need to create a `vueTransformerPlugin.js` file to the project's root and specify supported extensions: ```js // For React Native version 0.59 or later var upstreamTransformer = require("metro-react-native-babel-transformer"); // You will need to use different transformers for different React Native versions // However, versions older than v0.59 are no longer supported by Vue Native // For React Native version 0.56 - 0.58 // var upstreamTransformer = require("metro/src/reactNativeTransformer"); // For React Native version 0.52 - 0.55 // var upstreamTransformer = require("metro/src/transformer"); // For React Native version 0.47 - 0.51 // var upstreamTransformer = require("metro-bundler/src/transformer"); // For React Native version 0.46 // var upstreamTransformer = require("metro-bundler/build/transformer"); var vueNaiveScripts = require("vue-native-scripts"); var vueExtensions = ["vue"]; // <-- Add other extensions if needed. module.exports.transform = function({ src, filename, options }) { if (vueExtensions.some(ext => filename.endsWith("." + ext))) { return vueNaiveScripts.transform({ src, filename, options }); } return upstreamTransformer.transform({ src, filename, options }); }; ``` This file conditionally transforms files based on their extensions. `vue-native-scripts` is used for `.vue` files, while the stock React Native Babel transformer is used for other (JS) files. ## Using Vue Native components and `.vue` files In the React Native application, you can simply `import` your Vue components as follows ``` import VueComponent from './VueComponent.vue' ``` There should be a file named `VueComponent.vue` in the corresponding folder; the transformer parses this file and sends it to the React Native bundler. ================================================ FILE: packages/vue-native-scripts/bin/vue-native-script.js ================================================ #!/usr/bin/env node const spawn = require('cross-spawn'); const script = process.argv[2]; const args = process.argv.slice(3); const validCommands = ['compiler']; if(validCommands.indexOf(script) !== -1) { const result = spawn.sync( 'node', ['--no-deprecation', require.resolve('../scripts/' + script + '.js')].concat(args), { stdio: 'inherit' } ); process.exit(result.status); } else { console.log( `Invalid command '${script}'. Please check if you need to update react-native-scripts.` ); } ================================================ FILE: packages/vue-native-scripts/index.js ================================================ module.exports = require('./build.js') ================================================ FILE: packages/vue-native-scripts/package.json ================================================ { "name": "vue-native-scripts", "version": "0.3.1", "description": "Compile Vue Native components to React Native", "main": "index.js", "scripts": {}, "bin": { "vue-native-scripts": "./bin/vue-native-script.js" }, "author": "Geekyants (https://geekyants.io/)", "license": "MIT", "dependencies": { "babel-core": "^6.25.0", "babel-traverse": "^6.26.0", "cross-spawn": "^5.1.0", "css-parse": "^2.0.0", "hash-sum": "^1.0.2", "js-beautify": "^1.6.14", "line-number": "^0.1.0", "parse5": "^5.0.0", "path": "^0.12.7", "semver": "^6.2.0", "source-map": "~0.6.0", "vue-native-template-compiler": "^0.3.0" }, "peerDependencies": { "react-native": "^0.59.0", "metro": "^0.51.0", "metro-bundler": "*", "metro-react-native-babel-transformer": "^0.51.0" }, "bugs": { "url": "https://github.com/GeekyAnts/vue-native-core/issues" }, "repository": { "type": "git", "url": "git+https://github.com/GeekyAnts/vue-native-core/tree/master/packages/vue-native-scripts" } } ================================================ FILE: packages/vue-native-template-compiler/README.md ================================================ # vue-native-template-compiler > 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). Find the `vue-native-core` repository [here](https://github.com/GeekyAnts/vue-native-core). For the official documentation, visit [this](https://vue-native.io/docs/installation.html) website. ================================================ FILE: packages/vue-native-template-compiler/index.js ================================================ module.exports = require('./build') ================================================ FILE: packages/vue-native-template-compiler/package.json ================================================ { "name": "vue-native-template-compiler", "version": "0.3.1", "description": "Vue Native template compiler, dependency of vue-native-scripts. It can also be used as a stand-alone compiler", "main": "index.js", "repository": { "type": "git", "url": "git+https://github.com/GeekyAnts/vue-native-core" }, "keywords": [ "react", "vue", "template", "compiler" ], "author": "Geekyants (https://geekyants.io/)", "license": "MIT", "bugs": { "url": "https://github.com/GeekyAnts/vue-native-core/issues" }, "homepage": "https://github.com/GeekyAnts/vue-native-core/tree/master/packages/vue-native-template-compiler", "dependencies": { "he": "^1.1.0", "de-indent": "^1.0.2", "change-case": "^3.0.1" } } ================================================ FILE: scripts/.eslintrc ================================================ { "env": { "es6": true, }, "rules": { "camelcase": 0, }, } ================================================ FILE: scripts/alias.js ================================================ const path = require('path') module.exports = { vue: path.resolve(__dirname, '../src/platforms/web/runtime-with-compiler'), compiler: path.resolve(__dirname, '../src/compiler'), core: path.resolve(__dirname, '../src/core'), shared: path.resolve(__dirname, '../src/shared'), web: path.resolve(__dirname, '../src/platforms/web'), 'vue-native': path.resolve(__dirname, '../src/platforms/vue-native'), sfc: path.resolve(__dirname, '../src/sfc'), } ================================================ FILE: scripts/build.js ================================================ const fs = require('fs') const path = require('path') const zlib = require('zlib') const rollup = require('rollup') const uglify = require('uglify-js') if (!fs.existsSync('dist')) { fs.mkdirSync('dist') } let builds = require('./config').getAllBuilds() // filter builds via command line arg if (process.argv[2]) { const filters = process.argv[2].split(',') builds = builds.filter(b => { return filters.some(f => b.dest.indexOf(f) > -1) }) } build(builds) function build(builds) { let built = 0 const total = builds.length const next = () => { buildEntry(builds[built]) .then(() => { built++ if (built < total) { next() } }) .catch(logError) } next() } function buildEntry(config) { const isProd = /min\.js$/.test(config.dest) const output = config.output const { file, banner } = output return rollup .rollup(config) .then(bundle => bundle.generate(output)) .then(({ output: [{ code }] }) => { if (isProd) { const minified = (banner ? banner + '\n' : '') + uglify.minify(code, { fromString: true, output: { screw_ie8: true, ascii_only: true, }, compress: { pure_funcs: ['makeMap'], }, }).code return write(file, minified, true) } else { return write(file, code) } }) } function write(dest, code, zip) { return new Promise((resolve, reject) => { function report(extra) { console.log( blue(path.relative(process.cwd(), dest)) + ' ' + getSize(code) + (extra || ''), ) resolve() } fs.writeFile(dest, code, err => { if (err) return reject(err) if (zip) { zlib.gzip(code, (err, zipped) => { if (err) return reject(err) report(' (gzipped: ' + getSize(zipped) + ')') }) } else { report() } }) }) } function getSize(code) { return (code.length / 1024).toFixed(2) + 'kb' } function logError(e) { console.log(e) } function blue(str) { return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m' } ================================================ FILE: scripts/ci.sh ================================================ set -e npm test # report coverage stats for non-PRs if [[ -z $CI_PULL_REQUEST ]]; then cat ./coverage/lcov.info | ./node_modules/.bin/codecov fi ================================================ FILE: scripts/config.js ================================================ const path = require('path') const buble = require('rollup-plugin-buble') const alias = require('rollup-plugin-alias') const replace = require('rollup-plugin-replace') const flow = require('rollup-plugin-flow-no-whitespace') const prettier = require('rollup-plugin-prettier') const version = '2.2.6' // const banner = // "/*!\n" + // " * Vue Native v" + // version + // "\n" + // " * (c) 2018-" + // new Date().getFullYear() + // " GeekyAnts Software Pvt. Ltd.\n" + // " * Released under the MIT License.\n" + // " */" const aliases = require('./alias') const resolve = p => { const base = p.split('/')[0] if (aliases[base]) { return path.resolve(aliases[base], p.slice(base.length + 1)) } else { return path.resolve(__dirname, '../', p) } } const builds = { 'vue-native-core': { entry: resolve('vue-native/index.js'), dest: resolve('packages/vue-native-core/build.js'), format: 'cjs', external: ['react'], }, 'vue-native-helper': { entry: resolve('vue-native/runtime/helpers.js'), dest: resolve('packages/vue-native-helper/build.js'), format: 'cjs', }, 'vue-native-scripts': { entry: resolve('vue-native/scripts/index.js'), dest: resolve('packages/vue-native-scripts/build.js'), format: 'cjs', external: [] .concat( Object.keys( require('../packages/vue-native-scripts/package.json').dependencies, ), ) .concat( Object.keys( require('../packages/vue-native-scripts/package.json') .peerDependencies, ), ), }, 'vue-native-template-compiler': { entry: resolve('vue-native/compiler.js'), dest: resolve('packages/vue-native-template-compiler/build.js'), format: 'cjs', external: ['change-case', 'he', 'de-indent', 'lodash'], }, } function genConfig(opts) { const config = { input: opts.entry, output: { file: opts.dest, format: opts.format, banner: opts.banner, name: 'Vue', }, external: opts.external, plugins: [ replace({ __VERSION__: version, }), flow(), buble(), alias(Object.assign({}, aliases, opts.alias)), prettier(), ].concat(opts.plugins || []), onwarn: (msg, warn) => { if (!/Circular/.test(msg)) { warn(msg) } }, } if (opts.env) { config.plugins.push( replace({ 'process.env.NODE_ENV': JSON.stringify(opts.env), }), ) } return config } if (process.env.TARGET) { module.exports = genConfig(builds[process.env.TARGET]) } else { exports.getBuild = name => genConfig(builds[name]) exports.getAllBuilds = () => Object.keys(builds).map(name => genConfig(builds[name])) } ================================================ FILE: scripts/git-hooks/pre-commit ================================================ #!/usr/bin/env bash files_to_lint=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js$') if [ -n "$files_to_lint" ]; then NODE_ENV=production eslint --quiet $files_to_lint fi ================================================ FILE: scripts/release.sh ================================================ set -e CURRENT_BRANCH=$(git branch --show-current) if [ $CURRENT_BRANCH != "master" ]; then echo "This script can only be run in the master branch. Exiting..." exit 1 fi # get the version number from the user read -e -p "Enter the new Vue Native version: " VERSION if [[ -z $VERSION ]]; then echo "No version entered. Exiting..." exit 0 fi # Decide the NPM tag. # Tag should be set to 'next' for pre-release versions # and will be 'latest' by default read -p "Is this a pre-release version? (y/n) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then echo "Using npm tag 'next' for this release." TAG='next' else echo "Using npm tag 'latest' for this release." TAG='latest' fi read -p "Releasing $VERSION with npm tag $TAG - are you sure? (y/n) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then echo "Releasing $VERSION (with npm tag $TAG)..." # bump package versions # packages: # - vue-native-core # - vue-native-helper # - vue-native-scripts # - vue-native-template-compiler cd packages/vue-native-core npm version $VERSION cd - cd packages/vue-native-helper npm version $VERSION cd - cd packages/vue-native-scripts npm version $VERSION cd - cd packages/vue-native-template-compiler npm version $VERSION cd - # build # the build needs to be generated after the version bump # because the Vue version comes from packages/vue-native-core/package.json # refer to scripts/config.js VERSION=$VERSION npm run build # commit git add -A git commit -m "[build] $VERSION" # publish packages # vue-native-core has already been published by np # packages: # - vue-native-core # - vue-native-helper # - vue-native-scripts # - vue-native-template-compiler cd packages/vue-native-core npm publish --tag $TAG cd - cd packages/vue-native-helper npm publish --tag $TAG cd - cd packages/vue-native-scripts npm publish --tag $TAG cd - cd packages/vue-native-template-compiler npm publish --tag $TAG cd - # Update version in main package.json and commit npm version $VERSION # Push the tags and version update git push origin v$VERSION git push origin master echo "\nPublished v$VERSION!" fi ================================================ FILE: src/compiler/codegen/events.js ================================================ /* @flow */ const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/ const simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/ // keyCode aliases const keyCodes: { [key: string]: number | Array } = { esc: 27, tab: 9, enter: 13, space: 32, up: 38, left: 37, right: 39, down: 40, delete: [8, 46], } // #4868: modifiers that prevent the execution of the listener // need to explicitly return null so that we can determine whether to remove // the listener for .once const genGuard = condition => `if(${condition})return null;` const modifierCode: { [key: string]: string } = { stop: '$event.stopPropagation();', prevent: '$event.preventDefault();', self: genGuard(`$event.target !== $event.currentTarget`), ctrl: genGuard(`!$event.ctrlKey`), shift: genGuard(`!$event.shiftKey`), alt: genGuard(`!$event.altKey`), meta: genGuard(`!$event.metaKey`), left: genGuard(`'button' in $event && $event.button !== 0`), middle: genGuard(`'button' in $event && $event.button !== 1`), right: genGuard(`'button' in $event && $event.button !== 2`), } export function genHandlers( events: ASTElementHandlers, native: boolean, warn: Function, ): string { let res = native ? 'nativeOn:{' : 'on:{' for (const name in events) { const handler = events[name] // #5330: warn click.right, since right clicks do not actually fire click events. if ( process.env.NODE_ENV !== 'production' && name === 'click' && handler && handler.modifiers && handler.modifiers.right ) { warn( `Use "contextmenu" instead of "click.right" since right clicks ` + `do not actually fire "click" events.`, ) } res += `"${name}":${genHandler(name, handler)},` } return res.slice(0, -1) + '}' } function genHandler( name: string, handler: ASTElementHandler | Array, ): string { if (!handler) { return 'function(){}' } if (Array.isArray(handler)) { return `[${handler.map(handler => genHandler(name, handler)).join(',')}]` } const isMethodPath = simplePathRE.test(handler.value) const isFunctionExpression = fnExpRE.test(handler.value) if (!handler.modifiers) { return isMethodPath || isFunctionExpression ? handler.value : `function($event){${handler.value}}` // inline statement } else { let code = '' let genModifierCode = '' const keys = [] for (const key in handler.modifiers) { if (modifierCode[key]) { genModifierCode += modifierCode[key] // left/right if (keyCodes[key]) { keys.push(key) } } else { keys.push(key) } } if (keys.length) { code += genKeyFilter(keys) } // Make sure modifiers like prevent and stop get executed after key filtering if (genModifierCode) { code += genModifierCode } const handlerCode = isMethodPath ? handler.value + '($event)' : isFunctionExpression ? `(${handler.value})($event)` : handler.value return `function($event){${code}${handlerCode}}` } } function genKeyFilter(keys: Array): string { return `if(!('button' in $event)&&${keys .map(genFilterCode) .join('&&')})return null;` } function genFilterCode(key: string): string { const keyVal = parseInt(key, 10) if (keyVal) { return `$event.keyCode!==${keyVal}` } const alias = keyCodes[key] return `_k($event.keyCode,${JSON.stringify(key)}${ alias ? ',' + JSON.stringify(alias) : '' })` } ================================================ FILE: src/compiler/codegen/index.js ================================================ /* @flow */ import { genHandlers } from './events' import { baseWarn, pluckModuleFunction } from '../helpers' import baseDirectives from '../directives/index' import { camelize, no } from 'shared/util' type TransformFunction = (el: ASTElement, code: string) => string type DataGenFunction = (el: ASTElement) => string type DirectiveFunction = ( el: ASTElement, dir: ASTDirective, warn: Function, ) => boolean // configurable state let warn let transforms: Array let dataGenFns: Array let platformDirectives let isPlatformReservedTag let staticRenderFns let onceCount let currentOptions export function generate( ast: ASTElement | void, options: CompilerOptions, ): { render: string, staticRenderFns: Array, } { // save previous staticRenderFns so generate calls can be nested const prevStaticRenderFns: Array = staticRenderFns const currentStaticRenderFns: Array = (staticRenderFns = []) const prevOnceCount = onceCount onceCount = 0 currentOptions = options warn = options.warn || baseWarn transforms = pluckModuleFunction(options.modules, 'transformCode') dataGenFns = pluckModuleFunction(options.modules, 'genData') platformDirectives = options.directives || {} isPlatformReservedTag = options.isReservedTag || no const code = ast ? genElement(ast) : '_c("div")' staticRenderFns = prevStaticRenderFns onceCount = prevOnceCount return { render: `with(this){return ${code}}`, staticRenderFns: currentStaticRenderFns, } } function genElement(el: ASTElement): string { if (el.staticRoot && !el.staticProcessed) { return genStatic(el) } else if (el.once && !el.onceProcessed) { return genOnce(el) } else if (el.for && !el.forProcessed) { return genFor(el) } else if (el.if && !el.ifProcessed) { return genIf(el) } else if (el.tag === 'template' && !el.slotTarget) { return genChildren(el) || 'void 0' } else if (el.tag === 'slot') { return genSlot(el) } else { // component or element let code if (el.component) { code = genComponent(el.component, el) } else { const data = el.plain ? undefined : genData(el) const children = el.inlineTemplate ? null : genChildren(el, true) code = `_c('${el.tag}'${ data ? `,${data}` : '' // data }${ children ? `,${children}` : '' // children })` } // module transforms for (let i = 0; i < transforms.length; i++) { code = transforms[i](el, code) } return code } } // hoist static sub-trees out function genStatic(el: ASTElement): string { el.staticProcessed = true staticRenderFns.push(`with(this){return ${genElement(el)}}`) return `_m(${staticRenderFns.length - 1}${el.staticInFor ? ',true' : ''})` } // v-once function genOnce(el: ASTElement): string { el.onceProcessed = true if (el.if && !el.ifProcessed) { return genIf(el) } else if (el.staticInFor) { let key = '' let parent = el.parent while (parent) { if (parent.for) { key = parent.key break } parent = parent.parent } if (!key) { process.env.NODE_ENV !== 'production' && warn(`v-once can only be used inside v-for that is keyed. `) return genElement(el) } return `_o(${genElement(el)},${onceCount++}${key ? `,${key}` : ``})` } else { return genStatic(el) } } function genIf(el: any): string { el.ifProcessed = true // avoid recursion return genIfConditions(el.ifConditions.slice()) } function genIfConditions(conditions: ASTIfConditions): string { if (!conditions.length) { return '_e()' } const condition = conditions.shift() if (condition.exp) { return `(${condition.exp})?${genTernaryExp( condition.block, )}:${genIfConditions(conditions)}` } else { return `${genTernaryExp(condition.block)}` } // v-if with v-once should generate code like (a)?_m(0):_m(1) function genTernaryExp(el) { return el.once ? genOnce(el) : genElement(el) } } function genFor(el: any): string { const exp = el.for const alias = el.alias const iterator1 = el.iterator1 ? `,${el.iterator1}` : '' const iterator2 = el.iterator2 ? `,${el.iterator2}` : '' if ( process.env.NODE_ENV !== 'production' && maybeComponent(el) && el.tag !== 'slot' && el.tag !== 'template' && !el.key ) { warn( `<${el.tag} v-for="${alias} in ${exp}">: component lists rendered with ` + `v-for should have explicit keys. ` + `See https://vuejs.org/guide/list.html#key for more info.`, true /* tip */, ) } el.forProcessed = true // avoid recursion return ( `_l((${exp}),` + `function(${alias}${iterator1}${iterator2}){` + `return ${genElement(el)}` + '})' ) } function genData(el: ASTElement): string { let data = '{' // directives first. // directives may mutate the el's other properties before they are generated. const dirs = genDirectives(el) if (dirs) data += dirs + ',' // key if (el.key) { data += `key:${el.key},` } // ref if (el.ref) { data += `ref:${el.ref},` } if (el.refInFor) { data += `refInFor:true,` } // pre if (el.pre) { data += `pre:true,` } // record original tag name for components using "is" attribute if (el.component) { data += `tag:"${el.tag}",` } // module data generation functions for (let i = 0; i < dataGenFns.length; i++) { data += dataGenFns[i](el) } // attributes if (el.attrs) { data += `attrs:{${genProps(el.attrs)}},` } // DOM props if (el.props) { data += `domProps:{${genProps(el.props)}},` } // event handlers if (el.events) { data += `${genHandlers(el.events, false, warn)},` } if (el.nativeEvents) { data += `${genHandlers(el.nativeEvents, true, warn)},` } // slot target if (el.slotTarget) { data += `slot:${el.slotTarget},` } // scoped slots if (el.scopedSlots) { data += `${genScopedSlots(el.scopedSlots)},` } // component v-model if (el.model) { data += `model:{value:${el.model.value},callback:${el.model.callback},expression:${el.model.expression}},` } // inline-template if (el.inlineTemplate) { const inlineTemplate = genInlineTemplate(el) if (inlineTemplate) { data += `${inlineTemplate},` } } data = data.replace(/,$/, '') + '}' // v-bind data wrap if (el.wrapData) { data = el.wrapData(data) } return data } function genDirectives(el: ASTElement): string | void { const dirs = el.directives if (!dirs) return let res = 'directives:[' let hasRuntime = false let i, l, dir, needRuntime for (i = 0, l = dirs.length; i < l; i++) { dir = dirs[i] needRuntime = true const gen: DirectiveFunction = platformDirectives[dir.name] || baseDirectives[dir.name] if (gen) { // compile-time directive that manipulates AST. // returns true if it also needs a runtime counterpart. needRuntime = !!gen(el, dir, warn) } if (needRuntime) { hasRuntime = true res += `{name:"${dir.name}",rawName:"${dir.rawName}"${ dir.value ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : '' }${dir.arg ? `,arg:"${dir.arg}"` : ''}${ dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : '' }},` } } if (hasRuntime) { return res.slice(0, -1) + ']' } } function genInlineTemplate(el: ASTElement): ?string { const ast = el.children[0] if ( process.env.NODE_ENV !== 'production' && (el.children.length > 1 || ast.type !== 1) ) { warn('Inline-template components must have exactly one child element.') } if (ast.type === 1) { const inlineRenderFns = generate(ast, currentOptions) return `inlineTemplate:{render:function(){${ inlineRenderFns.render }},staticRenderFns:[${inlineRenderFns.staticRenderFns .map(code => `function(){${code}}`) .join(',')}]}` } } function genScopedSlots(slots: { [key: string]: ASTElement }): string { return `scopedSlots:_u([${Object.keys(slots) .map(key => genScopedSlot(key, slots[key])) .join(',')}])` } function genScopedSlot(key: string, el: ASTElement) { return ( `[${key},function(${String(el.attrsMap.scope)}){` + `return ${ el.tag === 'template' ? genChildren(el) || 'void 0' : genElement(el) }}]` ) } function genChildren(el: ASTElement, checkSkip?: boolean): string | void { const children = el.children if (children.length) { const el: any = children[0] // optimize single v-for if ( children.length === 1 && el.for && el.tag !== 'template' && el.tag !== 'slot' ) { return genElement(el) } const normalizationType = checkSkip ? getNormalizationType(children) : 0 return `[${children.map(genNode).join(',')}]${ normalizationType ? `,${normalizationType}` : '' }` } } // determine the normalization needed for the children array. // 0: no normalization needed // 1: simple normalization needed (possible 1-level deep nested array) // 2: full normalization needed function getNormalizationType(children: Array): number { let res = 0 for (let i = 0; i < children.length; i++) { const el: ASTNode = children[i] if (el.type !== 1) { continue } if ( needsNormalization(el) || (el.ifConditions && el.ifConditions.some(c => needsNormalization(c.block))) ) { res = 2 break } if ( maybeComponent(el) || (el.ifConditions && el.ifConditions.some(c => maybeComponent(c.block))) ) { res = 1 } } return res } function needsNormalization(el: ASTElement): boolean { return el.for !== undefined || el.tag === 'template' || el.tag === 'slot' } function maybeComponent(el: ASTElement): boolean { return !isPlatformReservedTag(el.tag) } function genNode(node: ASTNode): string { if (node.type === 1) { return genElement(node) } else { return genText(node) } } function genText(text: ASTText | ASTExpression): string { return `_v(${ text.type === 2 ? text.expression // no need for () because already wrapped in _s() : transformSpecialNewlines(JSON.stringify(text.text)) })` } function genSlot(el: ASTElement): string { const slotName = el.slotName || '"default"' const children = genChildren(el) let res = `_t(${slotName}${children ? `,${children}` : ''}` const attrs = el.attrs && `{${el.attrs.map(a => `${camelize(a.name)}:${a.value}`).join(',')}}` const bind = el.attrsMap['v-bind'] if ((attrs || bind) && !children) { res += `,null` } if (attrs) { res += `,${attrs}` } if (bind) { res += `${attrs ? '' : ',null'},${bind}` } return res + ')' } // componentName is el.component, take it as argument to shun flow's pessimistic refinement function genComponent(componentName: string, el: ASTElement): string { const children = el.inlineTemplate ? null : genChildren(el, true) return `_c(${componentName},${genData(el)}${children ? `,${children}` : ''})` } function genProps(props: Array<{ name: string, value: string }>): string { let res = '' for (let i = 0; i < props.length; i++) { const prop = props[i] res += `"${prop.name}":${transformSpecialNewlines(prop.value)},` } return res.slice(0, -1) } // #3895, #4268 function transformSpecialNewlines(text: string): string { return text.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029') } ================================================ FILE: src/compiler/directives/bind.js ================================================ /* @flow */ export default function bind(el: ASTElement, dir: ASTDirective) { el.wrapData = (code: string) => { return `_b(${code},'${el.tag}',${dir.value}${ dir.modifiers && dir.modifiers.prop ? ',true' : '' })` } } ================================================ FILE: src/compiler/directives/index.js ================================================ /* @flow */ import bind from './bind' import { noop } from 'shared/util' export default { bind, cloak: noop, } ================================================ FILE: src/compiler/directives/model.js ================================================ /* @flow */ /** * Cross-platform code generation for component v-model */ export function genComponentModel( el: ASTElement, value: string, modifiers: ?ASTModifiers, ): ?boolean { const { number, trim } = modifiers || {} const baseValueExpression = '$$v' let valueExpression = baseValueExpression if (trim) { valueExpression = `(typeof ${baseValueExpression} === 'string'` + `? ${baseValueExpression}.trim()` + `: ${baseValueExpression})` } if (number) { valueExpression = `_n(${valueExpression})` } const assignment = genAssignmentCode(value, valueExpression) el.model = { value: `(${value})`, expression: `"${value}"`, callback: `function (${baseValueExpression}) {${assignment}}`, } } /** * Cross-platform codegen helper for generating v-model value assignment code. */ export function genAssignmentCode(value: string, assignment: string): string { const modelRs = parseModel(value) if (modelRs.idx === null) { return `${value}=${assignment}` } else { return ( `var $$exp = ${modelRs.exp}, $$idx = ${modelRs.idx};` + `if (!Array.isArray($$exp)){` + `${value}=${assignment}}` + `else{$$exp.splice($$idx, 1, ${assignment})}` ) } } /** * parse directive model to do the array update transform. a[idx] = val => $$a.splice($$idx, 1, val) * * for loop possible cases: * * - test * - test[idx] * - test[test1[idx]] * - test["a"][idx] * - xxx.test[a[a].test1[idx]] * - test.xxx.a["asa"][test1[idx]] * */ let len, str, chr, index, expressionPos, expressionEndPos export function parseModel(val: string): Object { str = val len = str.length index = expressionPos = expressionEndPos = 0 if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) { return { exp: val, idx: null, } } while (!eof()) { chr = next() /* istanbul ignore if */ if (isStringStart(chr)) { parseString(chr) } else if (chr === 0x5b) { parseBracket(chr) } } return { exp: val.substring(0, expressionPos), idx: val.substring(expressionPos + 1, expressionEndPos), } } function next(): number { return str.charCodeAt(++index) } function eof(): boolean { return index >= len } function isStringStart(chr: number): boolean { return chr === 0x22 || chr === 0x27 } function parseBracket(chr: number): void { let inBracket = 1 expressionPos = index while (!eof()) { chr = next() if (isStringStart(chr)) { parseString(chr) continue } if (chr === 0x5b) inBracket++ if (chr === 0x5d) inBracket-- if (inBracket === 0) { expressionEndPos = index break } } } function parseString(chr: number): void { const stringQuote = chr while (!eof()) { chr = next() if (chr === stringQuote) { break } } } ================================================ FILE: src/compiler/error-detector.js ================================================ /* @flow */ import { dirRE, onRE } from './parser/index' // these keywords should not appear inside expressions, but operators like // typeof, instanceof and in are allowed const prohibitedKeywordRE = new RegExp( '\\b' + ( 'do,if,for,let,new,try,var,case,else,with,await,break,catch,class,const,' + 'super,throw,while,yield,delete,export,import,return,switch,default,' + 'extends,finally,continue,debugger,function,arguments' ) .split(',') .join('\\b|\\b') + '\\b', ) // these unary operators should not be used as property/method names const unaryOperatorsRE = new RegExp( '\\b' + 'delete,typeof,void'.split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)', ) // check valid identifier for v-for const identRE = /[A-Za-z_$][\w$]*/ // strip strings in expressions const stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g // detect problematic expressions in a template export function detectErrors(ast: ?ASTNode): Array { const errors: Array = [] if (ast) { checkNode(ast, errors) } return errors } function checkNode(node: ASTNode, errors: Array) { if (node.type === 1) { for (const name in node.attrsMap) { if (dirRE.test(name)) { const value = node.attrsMap[name] if (value) { if (name === 'v-for') { checkFor(node, `v-for="${value}"`, errors) } else if (onRE.test(name)) { checkEvent(value, `${name}="${value}"`, errors) } else { checkExpression(value, `${name}="${value}"`, errors) } } } } if (node.children) { for (let i = 0; i < node.children.length; i++) { checkNode(node.children[i], errors) } } } else if (node.type === 2) { checkExpression(node.expression, node.text, errors) } } function checkEvent(exp: string, text: string, errors: Array) { const stipped = exp.replace(stripStringRE, '') const keywordMatch: any = stipped.match(unaryOperatorsRE) if (keywordMatch && stipped.charAt(keywordMatch.index - 1) !== '$') { errors.push( `avoid using JavaScript unary operator as property name: ` + `"${keywordMatch[0]}" in expression ${text.trim()}`, ) } checkExpression(exp, text, errors) } function checkFor(node: ASTElement, text: string, errors: Array) { checkExpression(node.for || '', text, errors) checkIdentifier(node.alias, 'v-for alias', text, errors) checkIdentifier(node.iterator1, 'v-for iterator', text, errors) checkIdentifier(node.iterator2, 'v-for iterator', text, errors) } function checkIdentifier( ident: ?string, type: string, text: string, errors: Array, ) { if (typeof ident === 'string' && !identRE.test(ident)) { errors.push(`invalid ${type} "${ident}" in expression: ${text.trim()}`) } } function checkExpression(exp: string, text: string, errors: Array) { try { new Function(`return ${exp}`) } catch (e) { const keywordMatch = exp .replace(stripStringRE, '') .match(prohibitedKeywordRE) if (keywordMatch) { errors.push( `avoid using JavaScript keyword as property name: ` + `"${keywordMatch[0]}" in expression ${text.trim()}`, ) } else { errors.push(`invalid expression: ${text.trim()}`) } } } ================================================ FILE: src/compiler/helpers.js ================================================ /* @flow */ import { parseFilters } from './parser/filter-parser' export function baseWarn(msg: string) { console.error(`[Vue compiler]: ${msg}`) } export function pluckModuleFunction( modules: ?Array, key: string, ): Array { return modules ? modules.map(m => m[key]).filter(_ => _) : [] } export function addProp(el: ASTElement, name: string, value: string) { ;(el.props || (el.props = [])).push({ name, value }) } export function addAttr(el: ASTElement, name: string, value: string) { ;(el.attrs || (el.attrs = [])).push({ name, value }) } export function addDirective( el: ASTElement, name: string, rawName: string, value: string, arg: ?string, modifiers: ?ASTModifiers, ) { ;(el.directives || (el.directives = [])).push({ name, rawName, value, arg, modifiers, }) } export function addHandler( el: ASTElement, name: string, value: string, modifiers: ?ASTModifiers, important?: boolean, warn?: Function, ) { // warn prevent and passive modifier /* istanbul ignore if */ if ( process.env.NODE_ENV !== 'production' && warn && modifiers && modifiers.prevent && modifiers.passive ) { warn( "passive and prevent can't be used together. " + "Passive handler can't prevent default event.", ) } // check capture modifier if (modifiers && modifiers.capture) { delete modifiers.capture name = '!' + name // mark the event as captured } if (modifiers && modifiers.once) { delete modifiers.once name = '~' + name // mark the event as once } /* istanbul ignore if */ if (modifiers && modifiers.passive) { delete modifiers.passive name = '&' + name // mark the event as passive } let events if (modifiers && modifiers.native) { delete modifiers.native events = el.nativeEvents || (el.nativeEvents = {}) } else { events = el.events || (el.events = {}) } const newHandler = { value, modifiers } const handlers = events[name] /* istanbul ignore if */ if (Array.isArray(handlers)) { important ? handlers.unshift(newHandler) : handlers.push(newHandler) } else if (handlers) { events[name] = important ? [newHandler, handlers] : [handlers, newHandler] } else { events[name] = newHandler } } export function getBindingAttr( el: ASTElement, name: string, getStatic?: boolean, ): ?string { const dynamicValue = getAndRemoveAttr(el, ':' + name) || getAndRemoveAttr(el, 'v-bind:' + name) if (dynamicValue != null) { return parseFilters(dynamicValue) } else if (getStatic !== false) { const staticValue = getAndRemoveAttr(el, name) if (staticValue != null) { return JSON.stringify(staticValue) } } } export function getAndRemoveAttr(el: ASTElement, name: string): ?string { let val if ((val = el.attrsMap[name]) != null) { const list = el.attrsList for (let i = 0, l = list.length; i < l; i++) { if (list[i].name === name) { list.splice(i, 1) break } } } return val } ================================================ FILE: src/compiler/index.js ================================================ /* @flow */ import { parse } from './parser/index' import { optimize } from './optimizer' import { generate } from './codegen/index' import { detectErrors } from './error-detector' import { extend, noop } from 'shared/util' import { warn, tip } from 'core/util/debug' function baseCompile( template: string, options: CompilerOptions, ): CompiledResult { const ast = parse(template.trim(), options) optimize(ast, options) const code = generate(ast, options) return { ast, render: code.render, staticRenderFns: code.staticRenderFns, } } function makeFunction(code, errors) { try { return new Function(code) } catch (err) { errors.push({ err, code }) return noop } } export function createCompiler(baseOptions: CompilerOptions) { const functionCompileCache: { [key: string]: CompiledFunctionResult, } = Object.create(null) function compile( template: string, options?: CompilerOptions, ): CompiledResult { const finalOptions = Object.create(baseOptions) const errors = [] const tips = [] finalOptions.warn = (msg, tip) => { ;(tip ? tips : errors).push(msg) } if (options) { // merge custom modules if (options.modules) { finalOptions.modules = (baseOptions.modules || []).concat( options.modules, ) } // merge custom directives if (options.directives) { finalOptions.directives = extend( Object.create(baseOptions.directives), options.directives, ) } // copy other options for (const key in options) { if (key !== 'modules' && key !== 'directives') { finalOptions[key] = options[key] } } } const compiled = baseCompile(template, finalOptions) if (process.env.NODE_ENV !== 'production') { errors.push.apply(errors, detectErrors(compiled.ast)) } compiled.errors = errors compiled.tips = tips return compiled } function compileToFunctions( template: string, options?: CompilerOptions, vm?: Component, ): CompiledFunctionResult { options = options || {} /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production') { // detect possible CSP restriction try { new Function('return 1') } catch (e) { if (e.toString().match(/unsafe-eval|CSP/)) { warn( 'It seems you are using the standalone build of Vue.js in an ' + 'environment with Content Security Policy that prohibits unsafe-eval. ' + 'The template compiler cannot work in this environment. Consider ' + 'relaxing the policy to allow unsafe-eval or pre-compiling your ' + 'templates into render functions.', ) } } } // check cache const key = options.delimiters ? String(options.delimiters) + template : template if (functionCompileCache[key]) { return functionCompileCache[key] } // compile const compiled = compile(template, options) // check compilation errors/tips if (process.env.NODE_ENV !== 'production') { if (compiled.errors && compiled.errors.length) { warn( `Error compiling template:\n\n${template}\n\n` + compiled.errors.map(e => `- ${e}`).join('\n') + '\n', vm, ) } if (compiled.tips && compiled.tips.length) { compiled.tips.forEach(msg => tip(msg, vm)) } } // turn code into functions const res = {} const fnGenErrors = [] res.render = makeFunction(compiled.render, fnGenErrors) const l = compiled.staticRenderFns.length res.staticRenderFns = new Array(l) for (let i = 0; i < l; i++) { res.staticRenderFns[i] = makeFunction( compiled.staticRenderFns[i], fnGenErrors, ) } // check function generation errors. // this should only happen if there is a bug in the compiler itself. // mostly for codegen development use /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production') { if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { warn( `Failed to generate render function:\n\n` + fnGenErrors .map(({ err, code }) => `${err.toString()} in\n\n${code}\n`) .join('\n'), vm, ) } } return (functionCompileCache[key] = res) } return { compile, compileToFunctions, } } ================================================ FILE: src/compiler/optimizer.js ================================================ /* @flow */ import { makeMap, isBuiltInTag, cached, no } from 'shared/util' let isStaticKey let isPlatformReservedTag const genStaticKeysCached = cached(genStaticKeys) /** * Goal of the optimizer: walk the generated template AST tree * and detect sub-trees that are purely static, i.e. parts of * the DOM that never needs to change. * * Once we detect these sub-trees, we can: * * 1. Hoist them into constants, so that we no longer need to * create fresh nodes for them on each re-render; * 2. Completely skip them in the patching process. */ export function optimize(root: ?ASTElement, options: CompilerOptions) { if (!root) return isStaticKey = genStaticKeysCached(options.staticKeys || '') isPlatformReservedTag = options.isReservedTag || no // first pass: mark all non-static nodes. markStatic(root) // second pass: mark static roots. markStaticRoots(root, false) } function genStaticKeys(keys: string): Function { return makeMap( 'type,tag,attrsList,attrsMap,plain,parent,children,attrs' + (keys ? ',' + keys : ''), ) } function markStatic(node: ASTNode) { node.static = isStatic(node) if (node.type === 1) { // do not make component slot content static. this avoids // 1. components not able to mutate slot nodes // 2. static slot content fails for hot-reloading if ( !isPlatformReservedTag(node.tag) && node.tag !== 'slot' && node.attrsMap['inline-template'] == null ) { return } for (let i = 0, l = node.children.length; i < l; i++) { const child = node.children[i] markStatic(child) if (!child.static) { node.static = false } } } } function markStaticRoots(node: ASTNode, isInFor: boolean) { if (node.type === 1) { if (node.static || node.once) { node.staticInFor = isInFor } // For a node to qualify as a static root, it should have children that // are not just static text. Otherwise the cost of hoisting out will // outweigh the benefits and it's better off to just always render it fresh. if ( node.static && node.children.length && !(node.children.length === 1 && node.children[0].type === 3) ) { node.staticRoot = true return } else { node.staticRoot = false } if (node.children) { for (let i = 0, l = node.children.length; i < l; i++) { markStaticRoots(node.children[i], isInFor || !!node.for) } } if (node.ifConditions) { walkThroughConditionsBlocks(node.ifConditions, isInFor) } } } function walkThroughConditionsBlocks( conditionBlocks: ASTIfConditions, isInFor: boolean, ): void { for (let i = 1, len = conditionBlocks.length; i < len; i++) { markStaticRoots(conditionBlocks[i].block, isInFor) } } function isStatic(node: ASTNode): boolean { if (node.type === 2) { // expression return false } if (node.type === 3) { // text return true } return !!( node.pre || (!node.hasBindings && // no dynamic bindings !node.if && !node.for && // not v-if or v-for or v-else !isBuiltInTag(node.tag) && // not a built-in isPlatformReservedTag(node.tag) && // not a component !isDirectChildOfTemplateFor(node) && Object.keys(node).every(isStaticKey)) ) } function isDirectChildOfTemplateFor(node: ASTElement): boolean { while (node.parent) { node = node.parent if (node.tag !== 'template') { return false } if (node.for) { return true } } return false } ================================================ FILE: src/compiler/parser/entity-decoder.js ================================================ /* @flow */ let decoder export function decode(html: string): string { decoder = decoder || document.createElement('div') decoder.innerHTML = html return decoder.textContent } ================================================ FILE: src/compiler/parser/filter-parser.js ================================================ /* @flow */ const validDivisionCharRE = /[\w).+\-_$\]]/ export function parseFilters(exp: string): string { let inSingle = false let inDouble = false let inTemplateString = false let inRegex = false let curly = 0 let square = 0 let paren = 0 let lastFilterIndex = 0 let c, prev, i, expression, filters for (i = 0; i < exp.length; i++) { prev = c c = exp.charCodeAt(i) if (inSingle) { if (c === 0x27 && prev !== 0x5c) inSingle = false } else if (inDouble) { if (c === 0x22 && prev !== 0x5c) inDouble = false } else if (inTemplateString) { if (c === 0x60 && prev !== 0x5c) inTemplateString = false } else if (inRegex) { if (c === 0x2f && prev !== 0x5c) inRegex = false } else if ( c === 0x7c && // pipe exp.charCodeAt(i + 1) !== 0x7c && exp.charCodeAt(i - 1) !== 0x7c && !curly && !square && !paren ) { if (expression === undefined) { // first filter, end of expression lastFilterIndex = i + 1 expression = exp.slice(0, i).trim() } else { pushFilter() } } else { switch (c) { case 0x22: inDouble = true break // " case 0x27: inSingle = true break // ' case 0x60: inTemplateString = true break // ` case 0x28: paren++ break // ( case 0x29: paren-- break // ) case 0x5b: square++ break // [ case 0x5d: square-- break // ] case 0x7b: curly++ break // { case 0x7d: curly-- break // } } if (c === 0x2f) { // / let j = i - 1 let p // find first non-whitespace prev char for (; j >= 0; j--) { p = exp.charAt(j) if (p !== ' ') break } if (!p || !validDivisionCharRE.test(p)) { inRegex = true } } } } if (expression === undefined) { expression = exp.slice(0, i).trim() } else if (lastFilterIndex !== 0) { pushFilter() } function pushFilter() { ;(filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim()) lastFilterIndex = i + 1 } if (filters) { for (i = 0; i < filters.length; i++) { expression = wrapFilter(expression, filters[i]) } } return expression } function wrapFilter(exp: string, filter: string): string { const i = filter.indexOf('(') if (i < 0) { // _f: resolveFilter return `_f("${filter}")(${exp})` } else { const name = filter.slice(0, i) const args = filter.slice(i + 1) return `_f("${name}")(${exp},${args}` } } ================================================ FILE: src/compiler/parser/html-parser.js ================================================ /** * Not type-checking this file because it's mostly vendor code. */ /*! * HTML Parser By John Resig (ejohn.org) * Modified by Juriy "kangax" Zaytsev * Original code by Erik Arvidsson, Mozilla Public License * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js */ import { makeMap, no } from 'shared/util' import { isNonPhrasingTag } from 'web/compiler/util' // Regular Expressions for parsing tags and attributes const singleAttrIdentifier = /([^\s"'<>/=]+)/ const singleAttrAssign = /(?:=)/ const singleAttrValues = [ // attr value double quotes /"([^"]*)"+/.source, // attr value, single quotes /'([^']*)'+/.source, // attr value, no quotes /([^\s"'=<>`]+)/.source, ] const attribute = new RegExp( '^\\s*' + singleAttrIdentifier.source + '(?:\\s*(' + singleAttrAssign.source + ')' + '\\s*(?:' + singleAttrValues.join('|') + '))?', ) // could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName // but for Vue templates we can enforce a simple charset const ncname = '[a-zA-Z_][\\w\\-\\.]*' const qnameCapture = '((?:' + ncname + '\\:)?' + ncname + ')' const startTagOpen = new RegExp('^<' + qnameCapture) const startTagClose = /^\s*(\/?)>/ const endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>') const doctype = /^]+>/i const comment = /^') if (commentEnd >= 0) { advance(commentEnd + 3) continue } } // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment if (conditionalComment.test(html)) { const conditionalEnd = html.indexOf(']>') if (conditionalEnd >= 0) { advance(conditionalEnd + 2) continue } } // Doctype: const doctypeMatch = html.match(doctype) if (doctypeMatch) { advance(doctypeMatch[0].length) continue } // End tag: const endTagMatch = html.match(endTag) if (endTagMatch) { const curIndex = index advance(endTagMatch[0].length) parseEndTag(endTagMatch[1], curIndex, index) continue } // Start tag: const startTagMatch = parseStartTag() if (startTagMatch) { handleStartTag(startTagMatch) continue } } let text, rest, next if (textEnd >= 0) { rest = html.slice(textEnd) while ( !endTag.test(rest) && !startTagOpen.test(rest) && !comment.test(rest) && !conditionalComment.test(rest) ) { // < in plain text, be forgiving and treat it as text next = rest.indexOf('<', 1) if (next < 0) break textEnd += next rest = html.slice(textEnd) } text = html.substring(0, textEnd) advance(textEnd) } if (textEnd < 0) { text = html html = '' } if (options.chars && text) { options.chars(text) } } else { var stackedTag = lastTag.toLowerCase() var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp( '([\\s\\S]*?)(]*>)', 'i', )) var endTagLength = 0 var rest = html.replace(reStackedTag, function(all, text, endTag) { endTagLength = endTag.length if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') { text = text .replace(//g, '$1') .replace(//g, '$1') } if (options.chars) { options.chars(text) } return '' }) index += html.length - rest.length html = rest parseEndTag(stackedTag, index - endTagLength, index) } if (html === last) { options.chars && options.chars(html) if ( process.env.NODE_ENV !== 'production' && !stack.length && options.warn ) { options.warn(`Mal-formatted tag at end of template: "${html}"`) } break } } // Clean up any remaining tags parseEndTag() function advance(n) { index += n html = html.substring(n) } function parseStartTag() { const start = html.match(startTagOpen) if (start) { const match = { tagName: start[1], attrs: [], start: index, } advance(start[0].length) let end, attr while ( !(end = html.match(startTagClose)) && (attr = html.match(attribute)) ) { advance(attr[0].length) match.attrs.push(attr) } if (end) { match.unarySlash = end[1] advance(end[0].length) match.end = index return match } } } function handleStartTag(match) { const tagName = match.tagName const unarySlash = match.unarySlash if (expectHTML) { if (lastTag === 'p' && isNonPhrasingTag(tagName)) { parseEndTag(lastTag) } if (canBeLeftOpenTag(tagName) && lastTag === tagName) { parseEndTag(tagName) } } const unary = isUnaryTag(tagName) || (tagName === 'html' && lastTag === 'head') || !!unarySlash const l = match.attrs.length const attrs = new Array(l) for (let i = 0; i < l; i++) { const args = match.attrs[i] // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { if (args[3] === '') { delete args[3] } if (args[4] === '') { delete args[4] } if (args[5] === '') { delete args[5] } } let value = args[3] || args[4] || args[5] || '' let name = args[1] /** * react-vue change *
* {name: "autorun", value: """"} => {name: "autorun", value: "true"} */ if (args[1].indexOf('v-') === -1 && args[2] === undefined) { value = 'true' name = ':' + name } attrs[i] = { name: name, value: decodeAttr(value, options.shouldDecodeNewlines), } } if (!unary) { stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs, }) lastTag = tagName } if (options.start) { options.start(tagName, attrs, unary, match.start, match.end) } } function parseEndTag(tagName, start, end) { let pos, lowerCasedTagName if (start == null) start = index if (end == null) end = index if (tagName) { lowerCasedTagName = tagName.toLowerCase() } // Find the closest opened tag of the same type if (tagName) { for (pos = stack.length - 1; pos >= 0; pos--) { if (stack[pos].lowerCasedTag === lowerCasedTagName) { break } } } else { // If no tag name is provided, clean shop pos = 0 } if (pos >= 0) { // Close all the open elements, up the stack for (let i = stack.length - 1; i >= pos; i--) { if ( process.env.NODE_ENV !== 'production' && (i > pos || !tagName) && options.warn ) { options.warn(`tag <${stack[i].tag}> has no matching end tag.`) } if (options.end) { options.end(stack[i].tag, start, end) } } // Remove the open elements from the stack stack.length = pos lastTag = pos && stack[pos - 1].tag } else if (lowerCasedTagName === 'br') { if (options.start) { options.start(tagName, [], true, start, end) } } else if (lowerCasedTagName === 'p') { if (options.start) { options.start(tagName, [], false, start, end) } if (options.end) { options.end(tagName, start, end) } } } } ================================================ FILE: src/compiler/parser/index.js ================================================ /* @flow */ import { decode } from 'he' import { parseHTML } from './html-parser' import { parseText } from './text-parser' import { parseFilters } from './filter-parser' import { cached, no, camelize } from 'shared/util' import { genAssignmentCode } from '../directives/model' import { isIE, isEdge, isServerRendering, isNative } from 'core/util/env' import ReactNativeRenderGenerator from 'vue-native/compiler/codegen/NativeRenderGenerator.js' import { addProp, addAttr, baseWarn, addHandler, addDirective, getBindingAttr, getAndRemoveAttr, pluckModuleFunction, } from '../helpers' export const onRE = /^@|^v-on:/ export const dirRE = /^v-|^@|^:/ export const forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/ export const forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/ const argRE = /:(.*)$/ const bindRE = /^:|^v-bind:/ const modifierRE = /\.[^.]+/g const splitRE = /\r?\n/g const decodeHTMLCached = cached(decode) // configurable state export let warn let delimiters let transforms let preTransforms let postTransforms let platformIsPreTag let platformMustUseProp let platformGetTagNamespace /** * Convert HTML string to AST. */ export function parse( template: string, options: CompilerOptions, ): ASTElement | void { warn = options.warn || baseWarn platformGetTagNamespace = options.getTagNamespace || no platformMustUseProp = options.mustUseProp || no platformIsPreTag = options.isPreTag || no preTransforms = pluckModuleFunction(options.modules, 'preTransformNode') transforms = pluckModuleFunction(options.modules, 'transformNode') postTransforms = pluckModuleFunction(options.modules, 'postTransformNode') delimiters = options.delimiters const stack = [] const preserveWhitespace = options.preserveWhitespace !== false let root let currentParent let inVPre = false let inPre = false let warned = false function warnOnce(msg) { if (!warned) { warned = true warn(msg) } } function endPre(element) { // check pre state if (element.pre) { inVPre = false } if (platformIsPreTag(element.tag)) { inPre = false } } parseHTML(template, { warn, expectHTML: options.expectHTML, isUnaryTag: options.isUnaryTag, canBeLeftOpenTag: options.canBeLeftOpenTag, shouldDecodeNewlines: options.shouldDecodeNewlines, start(tag, attrs, unary) { // check namespace. // inherit parent ns if there is one const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag) // handle IE svg bug /* istanbul ignore if */ if (isIE && ns === 'svg') { attrs = guardIESVGBug(attrs) } const element: ASTElement = { type: 1, tag, attrsList: attrs, attrsMap: makeAttrsMap(attrs), parent: currentParent, children: [], } if (ns) { element.ns = ns } if (isForbiddenTag(element) && !isServerRendering()) { element.forbidden = true process.env.NODE_ENV !== 'production' && warn( 'Templates should only be responsible for mapping the state to the ' + 'UI. Avoid placing tags with side-effects in your templates, such as ' + `<${tag}>` + ', as they will not be parsed.', ) } // apply pre-transforms for (let i = 0; i < preTransforms.length; i++) { preTransforms[i](element, options) } if (!inVPre) { processPre(element) if (element.pre) { inVPre = true } } if (platformIsPreTag(element.tag)) { inPre = true } if (inVPre) { processRawAttrs(element) } else { processFor(element) processIf(element) processOnce(element) processKey(element) // determine whether this is a plain element after // removing structural attributes element.plain = !element.key && !attrs.length processRef(element) processSlot(element) processComponent(element) for (let i = 0; i < transforms.length; i++) { transforms[i](element, options) } processAttrs(element) } function checkRootConstraints(el) { if (process.env.NODE_ENV !== 'production') { if (el.tag === 'slot' || el.tag === 'template') { warnOnce( `Cannot use <${el.tag}> as component root element because it may ` + 'contain multiple nodes.', ) } // eslint-disable-next-line no-prototype-builtins if (el.attrsMap.hasOwnProperty('v-for')) { warnOnce( 'Cannot use v-for on stateful component root element because ' + 'it renders multiple elements.', ) } } } // tree management if (!root) { root = element checkRootConstraints(root) } else if (!stack.length) { // allow root elements with v-if, v-else-if and v-else if (root.if && (element.elseif || element.else)) { checkRootConstraints(element) addIfCondition(root, { exp: element.elseif, block: element, }) } else if (process.env.NODE_ENV !== 'production') { warnOnce( `Component template should contain exactly one root element. ` + `If you are using v-if on multiple elements, ` + `use v-else-if to chain them instead.`, ) } } if (currentParent && !element.forbidden) { if (element.elseif || element.else) { processIfConditions(element, currentParent) } else if (element.slotScope) { // scoped slot currentParent.plain = false const name = element.slotTarget || '"default"' ;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[ name ] = element } else { currentParent.children.push(element) element.parent = currentParent } } if (!unary) { currentParent = element stack.push(element) } else { endPre(element) } // apply post-transforms for (let i = 0; i < postTransforms.length; i++) { postTransforms[i](element, options) } }, end() { // remove trailing whitespace const element = stack[stack.length - 1] const lastNode = element.children[element.children.length - 1] if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) { element.children.pop() } // pop stack stack.length -= 1 currentParent = stack[stack.length - 1] endPre(element) }, chars(text: string) { if (!currentParent) { if (process.env.NODE_ENV !== 'production') { if (text === template) { warnOnce( 'Component template requires a root element, rather than just text.', ) } else if ((text = text.trim())) { warnOnce(`text "${text}" outside root element will be ignored.`) } } return } // IE textarea placeholder bug /* istanbul ignore if */ if ( isIE && currentParent.tag === 'textarea' && currentParent.attrsMap.placeholder === text ) { return } const children = currentParent.children text = inPre || text.trim() ? decodeHTMLCached(text) : // only preserve whitespace if its not right after a starting tag preserveWhitespace && children.length ? ' ' : '' if (text) { let expression if ( !inVPre && text !== ' ' && (expression = parseText(text, delimiters)) ) { children.push({ type: 2, expression, text, }) } else if ( text !== ' ' || !children.length || children[children.length - 1].text !== ' ' ) { children.push({ type: 3, text, }) } } }, }) return root } function processPre(el) { if (getAndRemoveAttr(el, 'v-pre') != null) { el.pre = true } } function processRawAttrs(el) { const l = el.attrsList.length if (l) { const attrs = (el.attrs = new Array(l)) for (let i = 0; i < l; i++) { attrs[i] = { name: el.attrsList[i].name, value: JSON.stringify(el.attrsList[i].value), } } } else if (!el.pre) { // non root node in pre blocks with no attributes el.plain = true } } function processKey(el) { const exp = getBindingAttr(el, 'key') if (exp) { if (process.env.NODE_ENV !== 'production' && el.tag === 'template') { warn( `