├── .babelrc.js ├── .circleci └── config.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .flowconfig ├── .github ├── CODE_OF_CONDUCT.md ├── COMMIT_CONVENTION.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── BACKERS.md ├── LICENSE ├── README.md ├── benchmarks ├── big-table │ ├── demo.css │ ├── index.html │ └── style.css ├── dbmon │ ├── ENV.js │ ├── app.js │ ├── index.html │ └── lib │ │ ├── memory-stats.js │ │ ├── monitor.js │ │ └── styles.css ├── reorder-list │ └── index.html ├── ssr │ ├── README.md │ ├── common.js │ ├── renderToStream.js │ └── renderToString.js ├── svg │ └── index.html └── uptime │ └── index.html ├── dist ├── README.md ├── vue.common.dev.js ├── vue.common.js ├── vue.common.prod.js ├── vue.esm.browser.js ├── vue.esm.browser.min.js ├── vue.esm.js ├── vue.js ├── vue.min.js ├── vue.runtime.common.dev.js ├── vue.runtime.common.js ├── vue.runtime.common.prod.js ├── vue.runtime.esm.js ├── vue.runtime.js └── vue.runtime.min.js ├── examples ├── commits │ ├── app.js │ ├── index.html │ └── mock.js ├── elastic-header │ ├── index.html │ └── style.css ├── firebase │ ├── app.js │ ├── index.html │ └── style.css ├── grid │ ├── grid.js │ ├── index.html │ └── style.css ├── markdown │ ├── index.html │ └── style.css ├── modal │ ├── index.html │ └── style.css ├── move-animations │ └── index.html ├── select2 │ └── index.html ├── svg │ ├── index.html │ ├── style.css │ └── svg.js ├── todomvc │ ├── app.js │ ├── index.html │ └── readme.md └── tree │ ├── index.html │ └── tree.js ├── flow ├── compiler.js ├── component.js ├── global-api.js ├── modules.js ├── options.js ├── ssr.js ├── vnode.js └── weex.js ├── package.json ├── packages ├── vue-server-renderer │ ├── README.md │ ├── basic.js │ ├── build.dev.js │ ├── build.prod.js │ ├── client-plugin.d.ts │ ├── client-plugin.js │ ├── index.js │ ├── package.json │ ├── server-plugin.d.ts │ ├── server-plugin.js │ └── types │ │ ├── index.d.ts │ │ ├── plugin.d.ts │ │ └── tsconfig.json ├── vue-template-compiler │ ├── README.md │ ├── browser.js │ ├── build.js │ ├── index.js │ ├── package.json │ └── types │ │ ├── index.d.ts │ │ ├── test.ts │ │ └── tsconfig.json ├── weex-template-compiler │ ├── README.md │ ├── build.js │ ├── index.js │ └── package.json └── weex-vue-framework │ ├── README.md │ ├── factory.js │ ├── index.js │ └── package.json ├── scripts ├── alias.js ├── build.js ├── config.js ├── feature-flags.js ├── gen-release-note.js ├── get-weex-version.js ├── git-hooks │ ├── commit-msg │ └── pre-commit ├── release-weex.sh ├── release.sh └── verify-commit-msg.js ├── src ├── compiler │ ├── codeframe.js │ ├── codegen │ │ ├── events.js │ │ └── index.js │ ├── create-compiler.js │ ├── directives │ │ ├── bind.js │ │ ├── index.js │ │ ├── model.js │ │ └── on.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 │ └── to-function.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-dynamic-keys.js │ │ │ ├── bind-object-listeners.js │ │ │ ├── bind-object-props.js │ │ │ ├── check-keycodes.js │ │ │ ├── index.js │ │ │ ├── render-list.js │ │ │ ├── render-slot.js │ │ │ ├── render-static.js │ │ │ ├── resolve-filter.js │ │ │ ├── resolve-scoped-slots.js │ │ │ └── resolve-slots.js │ │ ├── render.js │ │ └── state.js │ ├── observer │ │ ├── array.js │ │ ├── dep.js │ │ ├── index.js │ │ ├── scheduler.js │ │ ├── traverse.js │ │ └── watcher.js │ ├── util │ │ ├── debug.js │ │ ├── env.js │ │ ├── error.js │ │ ├── index.js │ │ ├── lang.js │ │ ├── next-tick.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 │ │ ├── is-async-placeholder.js │ │ ├── merge-hook.js │ │ ├── normalize-children.js │ │ ├── normalize-scoped-slots.js │ │ ├── resolve-async-component.js │ │ └── update-listeners.js │ │ ├── modules │ │ ├── directives.js │ │ ├── index.js │ │ └── ref.js │ │ ├── patch.js │ │ └── vnode.js ├── platforms │ ├── web │ │ ├── compiler │ │ │ ├── directives │ │ │ │ ├── html.js │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ └── text.js │ │ │ ├── index.js │ │ │ ├── modules │ │ │ │ ├── class.js │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ └── style.js │ │ │ ├── options.js │ │ │ └── util.js │ │ ├── entry-compiler.js │ │ ├── entry-runtime-with-compiler.js │ │ ├── entry-runtime.js │ │ ├── entry-server-basic-renderer.js │ │ ├── entry-server-renderer.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 │ │ ├── server │ │ │ ├── compiler.js │ │ │ ├── directives │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ └── show.js │ │ │ ├── modules │ │ │ │ ├── attrs.js │ │ │ │ ├── class.js │ │ │ │ ├── dom-props.js │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ └── util.js │ │ └── util │ │ │ ├── attrs.js │ │ │ ├── class.js │ │ │ ├── compat.js │ │ │ ├── element.js │ │ │ ├── index.js │ │ │ └── style.js │ └── weex │ │ ├── compiler │ │ ├── directives │ │ │ ├── index.js │ │ │ └── model.js │ │ ├── index.js │ │ └── modules │ │ │ ├── append.js │ │ │ ├── class.js │ │ │ ├── index.js │ │ │ ├── props.js │ │ │ ├── recycle-list │ │ │ ├── component-root.js │ │ │ ├── component.js │ │ │ ├── index.js │ │ │ ├── recycle-list.js │ │ │ ├── text.js │ │ │ ├── v-bind.js │ │ │ ├── v-for.js │ │ │ ├── v-if.js │ │ │ ├── v-on.js │ │ │ └── v-once.js │ │ │ └── style.js │ │ ├── entry-compiler.js │ │ ├── entry-framework.js │ │ ├── entry-runtime-factory.js │ │ ├── runtime │ │ ├── components │ │ │ ├── index.js │ │ │ ├── richtext.js │ │ │ ├── transition-group.js │ │ │ └── transition.js │ │ ├── directives │ │ │ └── index.js │ │ ├── index.js │ │ ├── modules │ │ │ ├── attrs.js │ │ │ ├── class.js │ │ │ ├── events.js │ │ │ ├── index.js │ │ │ ├── style.js │ │ │ └── transition.js │ │ ├── node-ops.js │ │ ├── patch.js │ │ ├── recycle-list │ │ │ ├── render-component-template.js │ │ │ └── virtual-component.js │ │ └── text-node.js │ │ └── util │ │ ├── element.js │ │ ├── index.js │ │ └── parser.js ├── server │ ├── bundle-renderer │ │ ├── create-bundle-renderer.js │ │ ├── create-bundle-runner.js │ │ └── source-map-support.js │ ├── create-basic-renderer.js │ ├── create-renderer.js │ ├── optimizing-compiler │ │ ├── codegen.js │ │ ├── index.js │ │ ├── modules.js │ │ ├── optimizer.js │ │ └── runtime-helpers.js │ ├── render-context.js │ ├── render-stream.js │ ├── render.js │ ├── template-renderer │ │ ├── create-async-file-mapper.js │ │ ├── index.js │ │ ├── parse-template.js │ │ └── template-stream.js │ ├── util.js │ ├── webpack-plugin │ │ ├── client.js │ │ ├── server.js │ │ └── util.js │ └── write.js ├── sfc │ └── parser.js └── shared │ ├── constants.js │ └── util.js ├── test ├── e2e │ ├── .eslintrc.json │ ├── nightwatch.config.js │ ├── runner.js │ └── specs │ │ ├── async-edge-cases.html │ │ ├── async-edge-cases.js │ │ ├── basic-ssr.html │ │ ├── basic-ssr.js │ │ ├── commits.js │ │ ├── grid.js │ │ ├── markdown.js │ │ ├── modal.js │ │ ├── select2.js │ │ ├── svg.js │ │ ├── todomvc.js │ │ └── tree.js ├── helpers │ ├── .eslintrc.json │ ├── classlist.js │ ├── test-object-option.js │ ├── to-equal.js │ ├── to-have-been-warned.js │ ├── trigger-event.js │ ├── vdom.js │ └── wait-for-update.js ├── ssr │ ├── .eslintrc │ ├── async-loader.js │ ├── compile-with-webpack.js │ ├── fixtures │ │ ├── app.js │ │ ├── async-bar.js │ │ ├── async-foo.js │ │ ├── cache-opt-out.js │ │ ├── cache.js │ │ ├── error.js │ │ ├── nested-cache.js │ │ ├── promise-rejection.js │ │ ├── split.js │ │ ├── test.css │ │ ├── test.png │ │ └── test.woff2 │ ├── jasmine.js │ ├── ssr-basic-renderer.spec.js │ ├── ssr-bundle-render.spec.js │ ├── ssr-stream.spec.js │ ├── ssr-string.spec.js │ └── ssr-template.spec.js ├── unit │ ├── .eslintrc.json │ ├── features │ │ ├── component │ │ │ ├── component-async.spec.js │ │ │ ├── component-keep-alive.spec.js │ │ │ ├── component-scoped-slot.spec.js │ │ │ ├── component-slot.spec.js │ │ │ └── component.spec.js │ │ ├── debug.spec.js │ │ ├── directives │ │ │ ├── bind.spec.js │ │ │ ├── class.spec.js │ │ │ ├── cloak.spec.js │ │ │ ├── for.spec.js │ │ │ ├── html.spec.js │ │ │ ├── if.spec.js │ │ │ ├── model-checkbox.spec.js │ │ │ ├── model-component.spec.js │ │ │ ├── model-dynamic.spec.js │ │ │ ├── model-file.spec.js │ │ │ ├── model-parse.spec.js │ │ │ ├── model-radio.spec.js │ │ │ ├── model-select.spec.js │ │ │ ├── model-text.spec.js │ │ │ ├── on.spec.js │ │ │ ├── once.spec.js │ │ │ ├── pre.spec.js │ │ │ ├── show.spec.js │ │ │ ├── static-style-parser.spec.js │ │ │ ├── style.spec.js │ │ │ └── text.spec.js │ │ ├── error-handling.spec.js │ │ ├── filter │ │ │ └── filter.spec.js │ │ ├── global-api │ │ │ ├── assets.spec.js │ │ │ ├── compile.spec.js │ │ │ ├── config.spec.js │ │ │ ├── extend.spec.js │ │ │ ├── mixin.spec.js │ │ │ ├── observable.spec.js │ │ │ ├── set-delete.spec.js │ │ │ └── use.spec.js │ │ ├── instance │ │ │ ├── init.spec.js │ │ │ ├── methods-data.spec.js │ │ │ ├── methods-events.spec.js │ │ │ ├── methods-lifecycle.spec.js │ │ │ ├── properties.spec.js │ │ │ └── render-proxy.spec.js │ │ ├── options │ │ │ ├── _scopeId.spec.js │ │ │ ├── comments.spec.js │ │ │ ├── components.spec.js │ │ │ ├── computed.spec.js │ │ │ ├── data.spec.js │ │ │ ├── delimiters.spec.js │ │ │ ├── directives.spec.js │ │ │ ├── el.spec.js │ │ │ ├── errorCaptured.spec.js │ │ │ ├── extends.spec.js │ │ │ ├── functional.spec.js │ │ │ ├── inheritAttrs.spec.js │ │ │ ├── inject.spec.js │ │ │ ├── lifecycle.spec.js │ │ │ ├── methods.spec.js │ │ │ ├── mixins.spec.js │ │ │ ├── name.spec.js │ │ │ ├── parent.spec.js │ │ │ ├── props.spec.js │ │ │ ├── propsData.spec.js │ │ │ ├── render.spec.js │ │ │ ├── renderError.spec.js │ │ │ ├── template.spec.js │ │ │ └── watch.spec.js │ │ ├── ref.spec.js │ │ └── transition │ │ │ ├── inject-styles.js │ │ │ ├── transition-group.spec.js │ │ │ ├── transition-mode.spec.js │ │ │ └── transition.spec.js │ ├── index.js │ ├── karma.base.config.js │ ├── karma.cover.config.js │ ├── karma.dev.config.js │ ├── karma.sauce.config.js │ ├── karma.unit.config.js │ └── modules │ │ ├── compiler │ │ ├── codeframe.spec.js │ │ ├── codegen.spec.js │ │ ├── compiler-options.spec.js │ │ ├── optimizer.spec.js │ │ └── parser.spec.js │ │ ├── observer │ │ ├── dep.spec.js │ │ ├── observer.spec.js │ │ ├── scheduler.spec.js │ │ └── watcher.spec.js │ │ ├── server-compiler │ │ ├── compiler-options.spec.js │ │ └── optimizer.spec.js │ │ ├── sfc │ │ └── sfc-parser.spec.js │ │ ├── util │ │ ├── error.spec.js │ │ └── next-tick.spec.js │ │ └── vdom │ │ ├── create-component.spec.js │ │ ├── create-element.spec.js │ │ ├── modules │ │ ├── attrs.spec.js │ │ ├── class.spec.js │ │ ├── directive.spec.js │ │ ├── dom-props.spec.js │ │ ├── events.spec.js │ │ └── style.spec.js │ │ └── patch │ │ ├── children.spec.js │ │ ├── edge-cases.spec.js │ │ ├── element.spec.js │ │ ├── hooks.spec.js │ │ └── hydration.spec.js └── weex │ ├── .eslintrc │ ├── cases │ ├── cases.spec.js │ ├── event │ │ ├── click.after.vdom.js │ │ ├── click.before.vdom.js │ │ └── click.vue │ ├── recycle-list │ │ ├── attrs.vdom.js │ │ ├── attrs.vue │ │ ├── classname.vdom.js │ │ ├── classname.vue │ │ ├── components │ │ │ ├── banner.vue │ │ │ ├── counter.vue │ │ │ ├── editor.vue │ │ │ ├── footer.vue │ │ │ ├── lifecycle.vue │ │ │ ├── poster.vue │ │ │ ├── stateful-lifecycle.vdom.js │ │ │ ├── stateful-lifecycle.vue │ │ │ ├── stateful-v-model.vdom.js │ │ │ ├── stateful-v-model.vue │ │ │ ├── stateful.vdom.js │ │ │ ├── stateful.vue │ │ │ ├── stateless-multi-components.vdom.js │ │ │ ├── stateless-multi-components.vue │ │ │ ├── stateless-with-props.vdom.js │ │ │ ├── stateless-with-props.vue │ │ │ ├── stateless.vdom.js │ │ │ └── stateless.vue │ │ ├── inline-style.vdom.js │ │ ├── inline-style.vue │ │ ├── text-node.vdom.js │ │ ├── text-node.vue │ │ ├── v-else-if.vdom.js │ │ ├── v-else-if.vue │ │ ├── v-else.vdom.js │ │ ├── v-else.vue │ │ ├── v-for-iterator.vdom.js │ │ ├── v-for-iterator.vue │ │ ├── v-for.vdom.js │ │ ├── v-for.vue │ │ ├── v-if.vdom.js │ │ ├── v-if.vue │ │ ├── v-on-inline.vdom.js │ │ ├── v-on-inline.vue │ │ ├── v-on.vdom.js │ │ ├── v-on.vue │ │ ├── v-once.vdom.js │ │ └── v-once.vue │ └── render │ │ ├── class.vdom.js │ │ ├── class.vue │ │ ├── sample.vdom.js │ │ └── sample.vue │ ├── compiler │ ├── append.spec.js │ ├── class.spec.js │ ├── compile.spec.js │ ├── parser.spec.js │ ├── props.spec.js │ ├── style.spec.js │ └── v-model.spec.js │ ├── helpers │ └── index.js │ ├── jasmine.js │ └── runtime │ ├── attrs.spec.js │ ├── class.spec.js │ ├── components │ └── richtext.spec.js │ ├── events.spec.js │ ├── framework.spec.js │ ├── node.spec.js │ └── style.spec.js ├── types ├── index.d.ts ├── options.d.ts ├── plugin.d.ts ├── test │ ├── augmentation-test.ts │ ├── es-module.ts │ ├── options-test.ts │ ├── plugin-test.ts │ ├── ssr-test.ts │ ├── tsconfig.json │ ├── umd-test.ts │ └── vue-test.ts ├── tsconfig.json ├── typings.json ├── umd.d.ts ├── vnode.d.ts └── vue.d.ts └── yarn.lock /.babelrc.js: -------------------------------------------------------------------------------- 1 | const babelPresetFlowVue = { 2 | plugins: [ 3 | require('@babel/plugin-proposal-class-properties'), 4 | // require('@babel/plugin-syntax-flow'), // not needed, included in transform-flow-strip-types 5 | require('@babel/plugin-transform-flow-strip-types') 6 | ] 7 | } 8 | 9 | module.exports = { 10 | presets: [ 11 | require('@babel/preset-env'), 12 | // require('babel-preset-flow-vue') 13 | babelPresetFlowVue 14 | ], 15 | plugins: [ 16 | require('babel-plugin-transform-vue-jsx'), 17 | require('@babel/plugin-syntax-dynamic-import') 18 | ], 19 | ignore: [ 20 | 'dist/*.js', 21 | 'packages/**/*.js' 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | flow 2 | dist 3 | packages 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | parser: require.resolve('babel-eslint'), 5 | ecmaVersion: 2018, 6 | sourceType: 'module' 7 | }, 8 | env: { 9 | es6: true, 10 | node: true, 11 | browser: true 12 | }, 13 | plugins: [ 14 | "flowtype" 15 | ], 16 | extends: [ 17 | "eslint:recommended", 18 | "plugin:flowtype/recommended" 19 | ], 20 | globals: { 21 | "__WEEX__": true, 22 | "WXEnvironment": true 23 | }, 24 | rules: { 25 | 'no-console': process.env.NODE_ENV !== 'production' ? 0 : 2, 26 | 'no-useless-escape': 0, 27 | 'no-empty': 0 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.* 3 | .*/test/.* 4 | .*/scripts/.* 5 | .*/examples/.* 6 | .*/benchmarks/.* 7 | 8 | [include] 9 | 10 | [libs] 11 | flow 12 | 13 | [options] 14 | unsafe.enable_getters_and_setters=true 15 | module.name_mapper='^compiler/\(.*\)$' -> '/src/compiler/\1' 16 | module.name_mapper='^core/\(.*\)$' -> '/src/core/\1' 17 | module.name_mapper='^shared/\(.*\)$' -> '/src/shared/\1' 18 | module.name_mapper='^web/\(.*\)$' -> '/src/platforms/web/\1' 19 | module.name_mapper='^weex/\(.*\)$' -> '/src/platforms/weex/\1' 20 | module.name_mapper='^server/\(.*\)$' -> '/src/server/\1' 21 | module.name_mapper='^entries/\(.*\)$' -> '/src/entries/\1' 22 | module.name_mapper='^sfc/\(.*\)$' -> '/src/sfc/\1' 23 | suppress_comment= \\(.\\|\n\\)*\\$flow-disable-line 24 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of the level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [yyx990803, posva] 4 | patreon: evanyou 5 | open_collective: vuejs 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: npm/vue 8 | custom: # Replace with a single custom sponsorship URL 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | **What kind of change does this PR introduce?** (check at least one) 10 | 11 | - [ ] Bugfix 12 | - [ ] Feature 13 | - [ ] Code style update 14 | - [ ] Refactor 15 | - [ ] Build-related changes 16 | - [ ] Other, please describe: 17 | 18 | **Does this PR introduce a breaking change?** (check one) 19 | 20 | - [ ] Yes 21 | - [ ] No 22 | 23 | If yes, please describe the impact and migration path for existing applications: 24 | 25 | **The PR fulfills these requirements:** 26 | 27 | - [ ] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch 28 | - [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number) 29 | - [ ] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup 30 | - [ ] New/updated tests are included 31 | 32 | If adding a **new feature**, the PR's description includes: 33 | - [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it) 34 | 35 | **Other information:** 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.log 4 | explorations 5 | TODOs.md 6 | dist/*.gz 7 | dist/*.map 8 | dist/vue.common.min.js 9 | test/e2e/reports 10 | test/e2e/screenshots 11 | coverage 12 | RELEASE_NOTE*.md 13 | dist/*.js 14 | packages/vue-server-renderer/basic.js 15 | packages/vue-server-renderer/build.js 16 | packages/vue-server-renderer/server-plugin.js 17 | packages/vue-server-renderer/client-plugin.js 18 | packages/vue-template-compiler/build.js 19 | .vscode 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-present, Yuxi (Evan) You 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /benchmarks/big-table/demo.css: -------------------------------------------------------------------------------- 1 | form { 2 | margin-bottom: 15px; 3 | } 4 | 5 | td.hidden { 6 | color: #ccc; 7 | } 8 | 9 | table.filtered td.item { 10 | background-color: #FFFFBF; 11 | } 12 | 13 | table.filtered td.item.hidden { 14 | background-color: transparent; 15 | } 16 | -------------------------------------------------------------------------------- /benchmarks/dbmon/app.js: -------------------------------------------------------------------------------- 1 | var app = new Vue({ 2 | el: '#app', 3 | data: { 4 | databases: [] 5 | } 6 | }) 7 | 8 | function loadSamples() { 9 | app.databases = Object.freeze(ENV.generateData().toArray()); 10 | Monitoring.renderRate.ping(); 11 | setTimeout(loadSamples, ENV.timeout); 12 | } 13 | 14 | loadSamples() 15 | -------------------------------------------------------------------------------- /benchmarks/dbmon/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | dbmon (Vue) 7 | 8 | 9 |

10 | Reference: js-repaint-perfs 11 |

12 |
13 | 14 | 15 | 16 | 17 | 20 | 27 | 28 | 29 |
{{db.dbname}} 18 | {{db.lastSample.nbQueries}} 19 | 21 | {{q.formatElapsed}} 22 |
23 |
{{q.query}}
24 |
25 |
26 |
30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /benchmarks/ssr/README.md: -------------------------------------------------------------------------------- 1 | # Vue.js SSR benchmark 2 | 3 | This benchmark renders a table of 1000 rows with 10 columns (10k components), with around 30k normal elements on the page. Note this is not something likely to be seen in a typical app. This benchmark is mostly for stress/regression testing and comparing between `renderToString` and `renderToStream`. 4 | 5 | To view the results follow the run section. Note that the overall completion time for the results is variable, this is due to other system related variants at run time (available memory, processing power, etc). In ideal circumstances, both should finish within similar results. 6 | 7 | `renderToStream` pipes the content through a stream which provides considerable performance benefits (faster time-to-first-byte and non-event-loop-blocking) over `renderToString`. This can be observed through the benchmark. 8 | 9 | ### run 10 | 11 | ``` bash 12 | npm run bench:ssr 13 | ``` 14 | -------------------------------------------------------------------------------- /benchmarks/ssr/renderToStream.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | 'use strict' 4 | 5 | process.env.NODE_ENV = 'production' 6 | 7 | const Vue = require('../../dist/vue.runtime.common.js') 8 | const createRenderer = require('../../packages/vue-server-renderer').createRenderer 9 | const renderToStream = createRenderer().renderToStream 10 | const gridComponent = require('./common.js') 11 | 12 | console.log('--- renderToStream --- ') 13 | const self = (global || root) 14 | const s = self.performance.now() 15 | 16 | const stream = renderToStream(new Vue(gridComponent)) 17 | let str = '' 18 | let first 19 | let complete 20 | stream.once('data', () => { 21 | first = self.performance.now() - s 22 | }) 23 | stream.on('data', chunk => { 24 | str += chunk 25 | }) 26 | stream.on('end', () => { 27 | complete = self.performance.now() - s 28 | console.log(`first chunk: ${first.toFixed(2)}ms`) 29 | console.log(`complete: ${complete.toFixed(2)}ms`) 30 | console.log() 31 | }) 32 | -------------------------------------------------------------------------------- /benchmarks/ssr/renderToString.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.NODE_ENV = 'production' 4 | 5 | const Vue = require('../../dist/vue.runtime.common.js') 6 | const createRenderer = require('../../packages/vue-server-renderer').createRenderer 7 | const renderToString = createRenderer().renderToString 8 | const gridComponent = require('./common.js') 9 | 10 | console.log('--- renderToString --- ') 11 | const self = (global || root) 12 | self.s = self.performance.now() 13 | 14 | renderToString(new Vue(gridComponent), (err, res) => { 15 | if (err) throw err 16 | // console.log(res) 17 | console.log('Complete time: ' + (self.performance.now() - self.s).toFixed(2) + 'ms') 18 | console.log() 19 | }) 20 | -------------------------------------------------------------------------------- /dist/vue.common.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === 'production') { 2 | module.exports = require('./vue.common.prod.js') 3 | } else { 4 | module.exports = require('./vue.common.dev.js') 5 | } 6 | -------------------------------------------------------------------------------- /dist/vue.runtime.common.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === 'production') { 2 | module.exports = require('./vue.runtime.common.prod.js') 3 | } else { 4 | module.exports = require('./vue.runtime.common.dev.js') 5 | } 6 | -------------------------------------------------------------------------------- /examples/commits/app.js: -------------------------------------------------------------------------------- 1 | /* global Vue */ 2 | 3 | var apiURL = 'https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha=' 4 | 5 | /** 6 | * Actual demo 7 | */ 8 | 9 | new Vue({ 10 | 11 | el: '#demo', 12 | 13 | data: { 14 | branches: ['master', 'dev'], 15 | currentBranch: 'master', 16 | commits: null 17 | }, 18 | 19 | created: function () { 20 | this.fetchData() 21 | }, 22 | 23 | watch: { 24 | currentBranch: 'fetchData' 25 | }, 26 | 27 | filters: { 28 | truncate: function (v) { 29 | var newline = v.indexOf('\n') 30 | return newline > 0 ? v.slice(0, newline) : v 31 | }, 32 | formatDate: function (v) { 33 | return v.replace(/T|Z/g, ' ') 34 | } 35 | }, 36 | 37 | methods: { 38 | fetchData: function () { 39 | var self = this 40 | if (navigator.userAgent.indexOf('PhantomJS') > -1) { 41 | // use mocks in e2e to avoid dependency on network / authentication 42 | setTimeout(function () { 43 | self.commits = window.MOCKS[self.currentBranch] 44 | }, 0) 45 | } else { 46 | var xhr = new XMLHttpRequest() 47 | xhr.open('GET', apiURL + self.currentBranch) 48 | xhr.onload = function () { 49 | self.commits = JSON.parse(xhr.responseText) 50 | console.log(self.commits[0].html_url) 51 | } 52 | xhr.send() 53 | } 54 | } 55 | } 56 | }) 57 | -------------------------------------------------------------------------------- /examples/commits/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue.js github commits example 5 | 21 | 22 | 23 | 24 | 25 |
26 |

Latest Vue.js Commits

27 | 35 |

vuejs/vue@{{ currentBranch }}

36 | 44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/elastic-header/style.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-weight: 300; 3 | font-size: 1.8em; 4 | margin-top: 0; 5 | } 6 | a { 7 | color: #fff; 8 | } 9 | .draggable-header-view { 10 | background-color: #fff; 11 | box-shadow: 0 4px 16px rgba(0,0,0,.15); 12 | width: 320px; 13 | height: 560px; 14 | overflow: hidden; 15 | margin: 30px auto; 16 | position: relative; 17 | font-family: 'Roboto', Helvetica, Arial, sans-serif; 18 | color: #fff; 19 | font-size: 14px; 20 | font-weight: 300; 21 | -webkit-user-select: none; 22 | -moz-user-select: none; 23 | -ms-user-select: none; 24 | user-select: none; 25 | } 26 | .draggable-header-view .bg { 27 | position: absolute; 28 | top: 0; 29 | left: 0; 30 | z-index: 0; 31 | } 32 | .draggable-header-view .header, .draggable-header-view .content { 33 | position: relative; 34 | z-index: 1; 35 | padding: 30px; 36 | box-sizing: border-box; 37 | } 38 | .draggable-header-view .header { 39 | height: 160px; 40 | } 41 | .draggable-header-view .content { 42 | color: #333; 43 | line-height: 1.5em; 44 | } 45 | -------------------------------------------------------------------------------- /examples/firebase/app.js: -------------------------------------------------------------------------------- 1 | var emailRE = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 2 | 3 | // Setup Firebase 4 | var config = { 5 | apiKey: "AIzaSyAi_yuJciPXLFr_PYPeU3eTvtXf8jbJ8zw", 6 | authDomain: "vue-demo-537e6.firebaseapp.com", 7 | databaseURL: "https://vue-demo-537e6.firebaseio.com" 8 | } 9 | firebase.initializeApp(config) 10 | 11 | var usersRef = firebase.database().ref('users') 12 | 13 | // create Vue app 14 | var app = new Vue({ 15 | // element to mount to 16 | el: '#app', 17 | // initial data 18 | data: { 19 | newUser: { 20 | name: '', 21 | email: '' 22 | } 23 | }, 24 | // firebase binding 25 | // https://github.com/vuejs/vuefire 26 | firebase: { 27 | users: usersRef 28 | }, 29 | // computed property for form validation state 30 | computed: { 31 | validation: function () { 32 | return { 33 | name: !!this.newUser.name.trim(), 34 | email: emailRE.test(this.newUser.email) 35 | } 36 | }, 37 | isValid: function () { 38 | var validation = this.validation 39 | return Object.keys(validation).every(function (key) { 40 | return validation[key] 41 | }) 42 | } 43 | }, 44 | // methods 45 | methods: { 46 | addUser: function () { 47 | if (this.isValid) { 48 | usersRef.push(this.newUser) 49 | this.newUser.name = '' 50 | this.newUser.email = '' 51 | } 52 | }, 53 | removeUser: function (user) { 54 | usersRef.child(user['.key']).remove() 55 | } 56 | } 57 | }) 58 | -------------------------------------------------------------------------------- /examples/firebase/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue.js firebase + validation example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
    18 |
  • 19 | {{user.name}} - {{user.email}} 20 | 21 |
  • 22 |
23 |
24 | 25 | 26 | 27 |
28 |
    29 |
  • Name cannot be empty.
  • 30 |
  • Please provide a valid email address.
  • 31 |
32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /examples/firebase/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, Arial, sans-serif; 3 | } 4 | 5 | ul { 6 | padding: 0; 7 | } 8 | 9 | .user { 10 | height: 30px; 11 | line-height: 30px; 12 | padding: 10px; 13 | border-top: 1px solid #eee; 14 | overflow: hidden; 15 | transition: all .25s ease; 16 | } 17 | 18 | .user:last-child { 19 | border-bottom: 1px solid #eee; 20 | } 21 | 22 | .v-enter, .v-leave-to { 23 | height: 0; 24 | padding-top: 0; 25 | padding-bottom: 0; 26 | border-top-width: 0; 27 | border-bottom-width: 0; 28 | } 29 | 30 | .errors { 31 | color: #f00; 32 | } 33 | -------------------------------------------------------------------------------- /examples/grid/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js grid component example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 36 | 37 | 38 |
39 | 42 | 46 | 47 |
48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /examples/grid/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica Neue, Arial, sans-serif; 3 | font-size: 14px; 4 | color: #444; 5 | } 6 | 7 | table { 8 | border: 2px solid #42b983; 9 | border-radius: 3px; 10 | background-color: #fff; 11 | } 12 | 13 | th { 14 | background-color: #42b983; 15 | color: rgba(255,255,255,0.66); 16 | cursor: pointer; 17 | -webkit-user-select: none; 18 | -moz-user-select: none; 19 | -ms-user-select: none; 20 | user-select: none; 21 | } 22 | 23 | td { 24 | background-color: #f9f9f9; 25 | } 26 | 27 | th, td { 28 | min-width: 120px; 29 | padding: 10px 20px; 30 | } 31 | 32 | th.active { 33 | color: #fff; 34 | } 35 | 36 | th.active .arrow { 37 | opacity: 1; 38 | } 39 | 40 | .arrow { 41 | display: inline-block; 42 | vertical-align: middle; 43 | width: 0; 44 | height: 0; 45 | margin-left: 5px; 46 | opacity: 0.66; 47 | } 48 | 49 | .arrow.asc { 50 | border-left: 4px solid transparent; 51 | border-right: 4px solid transparent; 52 | border-bottom: 4px solid #fff; 53 | } 54 | 55 | .arrow.dsc { 56 | border-left: 4px solid transparent; 57 | border-right: 4px solid transparent; 58 | border-top: 4px solid #fff; 59 | } 60 | -------------------------------------------------------------------------------- /examples/markdown/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js markdown editor example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 |
18 | 19 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/markdown/style.css: -------------------------------------------------------------------------------- 1 | html, body, #editor { 2 | margin: 0; 3 | height: 100%; 4 | font-family: 'Helvetica Neue', Arial, sans-serif; 5 | color: #333; 6 | } 7 | 8 | textarea, #editor div { 9 | display: inline-block; 10 | width: 49%; 11 | height: 100%; 12 | vertical-align: top; 13 | -webkit-box-sizing: border-box; 14 | -moz-box-sizing: border-box; 15 | box-sizing: border-box; 16 | padding: 0 20px; 17 | } 18 | 19 | textarea { 20 | border: none; 21 | border-right: 1px solid #ccc; 22 | resize: none; 23 | outline: none; 24 | background-color: #f6f6f6; 25 | font-size: 14px; 26 | font-family: 'Monaco', courier, monospace; 27 | padding: 20px; 28 | } 29 | 30 | code { 31 | color: #f66; 32 | } -------------------------------------------------------------------------------- /examples/modal/style.css: -------------------------------------------------------------------------------- 1 | .modal-mask { 2 | position: fixed; 3 | z-index: 9998; 4 | top: 0; 5 | left: 0; 6 | width: 100%; 7 | height: 100%; 8 | background-color: rgba(0, 0, 0, .5); 9 | display: table; 10 | transition: opacity .3s ease; 11 | } 12 | 13 | .modal-wrapper { 14 | display: table-cell; 15 | vertical-align: middle; 16 | } 17 | 18 | .modal-container { 19 | width: 300px; 20 | margin: 0px auto; 21 | padding: 20px 30px; 22 | background-color: #fff; 23 | border-radius: 2px; 24 | box-shadow: 0 2px 8px rgba(0, 0, 0, .33); 25 | transition: all .3s ease; 26 | font-family: Helvetica, Arial, sans-serif; 27 | } 28 | 29 | .modal-header h3 { 30 | margin-top: 0; 31 | color: #42b983; 32 | } 33 | 34 | .modal-body { 35 | margin: 20px 0; 36 | } 37 | 38 | .modal-default-button { 39 | float: right; 40 | } 41 | 42 | /* 43 | * The following styles are auto-applied to elements with 44 | * transition="modal" when their visibility is toggled 45 | * by Vue.js. 46 | * 47 | * You can easily play with the modal transition by editing 48 | * these styles. 49 | */ 50 | 51 | .modal-enter { 52 | opacity: 0; 53 | } 54 | 55 | .modal-leave-to { 56 | opacity: 0; 57 | } 58 | 59 | .modal-enter .modal-container, 60 | .modal-leave-to .modal-container { 61 | -webkit-transform: scale(1.1); 62 | transform: scale(1.1); 63 | } 64 | -------------------------------------------------------------------------------- /examples/svg/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica Neue, Arial, sans-serif; 3 | } 4 | 5 | polygon { 6 | fill: #42b983; 7 | opacity: .75; 8 | } 9 | 10 | circle { 11 | fill: transparent; 12 | stroke: #999; 13 | } 14 | 15 | text { 16 | font-family: Helvetica Neue, Arial, sans-serif; 17 | font-size: 10px; 18 | fill: #666; 19 | } 20 | 21 | label { 22 | display: inline-block; 23 | margin-left: 10px; 24 | width: 20px; 25 | } 26 | 27 | #raw { 28 | position: absolute; 29 | top: 0; 30 | left: 300px; 31 | } -------------------------------------------------------------------------------- /examples/todomvc/readme.md: -------------------------------------------------------------------------------- 1 | # Vue.js TodoMVC Example 2 | 3 | > Vue.js is a library for building interactive web interfaces. 4 | It provides data-driven, nestable view components with a simple and flexible API. 5 | 6 | > _[Vue.js - vuejs.org](https://vuejs.org)_ 7 | 8 | ## Learning Vue.js 9 | The [Vue.js website](https://vuejs.org/) is a great resource to get started. 10 | 11 | Here are some links you may find helpful: 12 | 13 | * [Official Guide](https://vuejs.org/guide/) 14 | * [API Reference](https://vuejs.org/api/) 15 | * [Examples](https://vuejs.org/examples/) 16 | 17 | Get help from other Vue.js users: 18 | 19 | * [Vue.js official forum](http://forum.vuejs.org) 20 | * [Vue.js on Twitter](https://twitter.com/vuejs) 21 | * [Vue.js on Gitter](https://gitter.im/vuejs/vue) 22 | 23 | _If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._ 24 | 25 | ## Credit 26 | 27 | This TodoMVC application was created by [Evan You](http://evanyou.me). 28 | -------------------------------------------------------------------------------- /examples/tree/tree.js: -------------------------------------------------------------------------------- 1 | // demo data 2 | var data = { 3 | name: 'My Tree', 4 | children: [ 5 | { name: 'hello' }, 6 | { name: 'wat' }, 7 | { 8 | name: 'child folder', 9 | children: [ 10 | { 11 | name: 'child folder', 12 | children: [ 13 | { name: 'hello' }, 14 | { name: 'wat' } 15 | ] 16 | }, 17 | { name: 'hello' }, 18 | { name: 'wat' }, 19 | { 20 | name: 'child folder', 21 | children: [ 22 | { name: 'hello' }, 23 | { name: 'wat' } 24 | ] 25 | } 26 | ] 27 | } 28 | ] 29 | } 30 | 31 | // define the item component 32 | Vue.component('item', { 33 | template: '#item-template', 34 | props: { 35 | model: Object 36 | }, 37 | data: function () { 38 | return { 39 | open: false 40 | } 41 | }, 42 | computed: { 43 | isFolder: function () { 44 | return this.model.children && 45 | this.model.children.length 46 | } 47 | }, 48 | methods: { 49 | toggle: function () { 50 | if (this.isFolder) { 51 | this.open = !this.open 52 | } 53 | }, 54 | changeType: function () { 55 | if (!this.isFolder) { 56 | Vue.set(this.model, 'children', []) 57 | this.addChild() 58 | this.open = true 59 | } 60 | }, 61 | addChild: function () { 62 | this.model.children.push({ 63 | name: 'new stuff' 64 | }) 65 | } 66 | } 67 | }) 68 | 69 | // boot up the demo 70 | var demo = new Vue({ 71 | el: '#demo', 72 | data: { 73 | treeData: data 74 | } 75 | }) 76 | -------------------------------------------------------------------------------- /flow/global-api.js: -------------------------------------------------------------------------------- 1 | declare interface GlobalAPI { 2 | cid: number; 3 | options: Object; 4 | config: Config; 5 | util: Object; 6 | 7 | extend: (options: Object) => Function; 8 | set: (target: Object | Array, key: string | number, value: T) => T; 9 | delete: (target: Object| Array, key: string | number) => void; 10 | nextTick: (fn: Function, context?: Object) => void | Promise<*>; 11 | use: (plugin: Function | Object) => GlobalAPI; 12 | mixin: (mixin: Object) => GlobalAPI; 13 | compile: (template: string) => { render: Function, staticRenderFns: Array }; 14 | 15 | directive: (id: string, def?: Function | Object) => Function | Object | void; 16 | component: (id: string, def?: Class | Object) => Class; 17 | filter: (id: string, def?: Function) => Function | void; 18 | 19 | observable: (value: T) => T; 20 | 21 | // allow dynamic method registration 22 | [key: string]: any 23 | }; 24 | -------------------------------------------------------------------------------- /flow/modules.js: -------------------------------------------------------------------------------- 1 | declare module 'he' { 2 | declare function escape(html: string): string; 3 | declare function decode(html: string): string; 4 | } 5 | 6 | declare module 'source-map' { 7 | declare class SourceMapGenerator { 8 | setSourceContent(filename: string, content: string): void; 9 | addMapping(mapping: Object): void; 10 | toString(): string; 11 | } 12 | declare class SourceMapConsumer { 13 | constructor (map: Object): void; 14 | originalPositionFor(position: { line: number; column: number; }): { 15 | source: ?string; 16 | line: ?number; 17 | column: ?number; 18 | }; 19 | } 20 | } 21 | 22 | declare module 'lru-cache' { 23 | declare var exports: { 24 | (): any 25 | } 26 | } 27 | 28 | declare module 'de-indent' { 29 | declare var exports: { 30 | (input: string): string 31 | } 32 | } 33 | 34 | declare module 'serialize-javascript' { 35 | declare var exports: { 36 | (input: string, options: { isJSON: boolean }): string 37 | } 38 | } 39 | 40 | declare module 'lodash.template' { 41 | declare var exports: { 42 | (input: string, options: { interpolate: RegExp, escape: RegExp }): Function 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /flow/ssr.js: -------------------------------------------------------------------------------- 1 | declare type ComponentWithCacheContext = { 2 | type: 'ComponentWithCache'; 3 | bufferIndex: number; 4 | buffer: Array; 5 | key: string; 6 | }; 7 | 8 | declare type ElementContext = { 9 | type: 'Element'; 10 | children: Array; 11 | rendered: number; 12 | endTag: string; 13 | total: number; 14 | }; 15 | 16 | declare type ComponentContext = { 17 | type: 'Component'; 18 | prevActive: Component; 19 | }; 20 | 21 | declare type RenderState = ComponentContext | ComponentWithCacheContext | ElementContext; 22 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/README.md: -------------------------------------------------------------------------------- 1 | # vue-server-renderer 2 | 3 | > This package is auto-generated. For pull requests please see [src/platforms/web/entry-server-renderer.js](https://github.com/vuejs/vue/blob/dev/src/platforms/web/entry-server-renderer.js). 4 | 5 | This package offers Node.js server-side rendering for Vue 2.0. 6 | 7 | - [API Reference](https://ssr.vuejs.org/en/api.html) 8 | - [Vue.js Server-Side Rendering Guide](https://ssr.vuejs.org) 9 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/client-plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { WebpackPlugin } from './types/plugin'; 2 | declare const Plugin: WebpackPlugin; 3 | export = Plugin; 4 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | var vueVersion = require('vue').version 3 | } catch (e) {} 4 | 5 | var packageName = require('./package.json').name 6 | var packageVersion = require('./package.json').version 7 | if (vueVersion && vueVersion !== packageVersion) { 8 | throw new Error( 9 | '\n\nVue packages version mismatch:\n\n' + 10 | '- vue@' + vueVersion + '\n' + 11 | '- ' + packageName + '@' + packageVersion + '\n\n' + 12 | 'This may cause things to work incorrectly. Make sure to use the same version for both.\n' 13 | ) 14 | } 15 | 16 | if (process.env.NODE_ENV === 'production') { 17 | module.exports = require('./build.prod.js') 18 | } else { 19 | module.exports = require('./build.dev.js') 20 | } 21 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-server-renderer", 3 | "version": "2.6.11", 4 | "description": "server renderer for Vue 2.0", 5 | "main": "index.js", 6 | "types": "types/index.d.ts", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/vuejs/vue.git" 10 | }, 11 | "keywords": [ 12 | "vue", 13 | "server", 14 | "ssr" 15 | ], 16 | "author": "Evan You", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/vuejs/vue/issues" 20 | }, 21 | "dependencies": { 22 | "chalk": "^1.1.3", 23 | "hash-sum": "^1.0.2", 24 | "he": "^1.1.0", 25 | "lodash.template": "^4.5.0", 26 | "lodash.uniq": "^4.5.0", 27 | "resolve": "^1.2.0", 28 | "serialize-javascript": "^2.1.2", 29 | "source-map": "0.5.6" 30 | }, 31 | "devDependencies": { 32 | "vue": "file:../.." 33 | }, 34 | "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer#readme" 35 | } 36 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/server-plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { WebpackPlugin } from './types/plugin'; 2 | declare const Plugin: WebpackPlugin; 3 | export = Plugin; 4 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/types/plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'webpack'; 2 | 3 | interface WebpackPluginOptions { 4 | filename?: string; 5 | } 6 | 7 | export interface WebpackPlugin { 8 | new (options?: WebpackPluginOptions): Plugin; 9 | } 10 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "noEmit": true 8 | }, 9 | "compileOnSave": false, 10 | "include": [ 11 | "**/*.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/vue-template-compiler/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | var vueVersion = require('vue').version 3 | } catch (e) {} 4 | 5 | var packageName = require('./package.json').name 6 | var packageVersion = require('./package.json').version 7 | if (vueVersion && vueVersion !== packageVersion) { 8 | var vuePath = require.resolve('vue') 9 | var packagePath = require.resolve('./package.json') 10 | throw new Error( 11 | '\n\nVue packages version mismatch:\n\n' + 12 | '- vue@' + vueVersion + ' (' + vuePath + ')\n' + 13 | '- ' + packageName + '@' + packageVersion + ' (' + packagePath + ')\n\n' + 14 | 'This may cause things to work incorrectly. Make sure to use the same version for both.\n' + 15 | 'If you are using vue-loader@>=10.0, simply update vue-template-compiler.\n' + 16 | 'If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump ' + packageName + ' to the latest.\n' 17 | ) 18 | } 19 | 20 | module.exports = require('./build') 21 | -------------------------------------------------------------------------------- /packages/vue-template-compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-template-compiler", 3 | "version": "2.6.11", 4 | "description": "template compiler for Vue 2.0", 5 | "main": "index.js", 6 | "unpkg": "browser.js", 7 | "jsdelivr": "browser.js", 8 | "browser": "browser.js", 9 | "types": "types/index.d.ts", 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/vuejs/vue.git" 13 | }, 14 | "keywords": [ 15 | "vue", 16 | "compiler" 17 | ], 18 | "author": "Evan You", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/vuejs/vue/issues" 22 | }, 23 | "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-template-compiler#readme", 24 | "dependencies": { 25 | "he": "^1.1.0", 26 | "de-indent": "^1.0.2" 27 | }, 28 | "devDependencies": { 29 | "vue": "file:../.." 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/vue-template-compiler/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "noEmit": true 8 | }, 9 | "compileOnSave": false, 10 | "include": [ 11 | "**/*.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/weex-template-compiler/README.md: -------------------------------------------------------------------------------- 1 | # weex-template-compiler 2 | 3 | > This package is auto-generated. For pull requests please see [src/platforms/weex/entry-compiler.js](https://github.com/vuejs/vue/tree/dev/src/platforms/weex/entry-compiler.js). 4 | -------------------------------------------------------------------------------- /packages/weex-template-compiler/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | var vueVersion = require('weex-vue-framework').version 3 | } catch (e) {} 4 | 5 | var packageName = require('./package.json').name 6 | var packageVersion = require('./package.json').version 7 | if (vueVersion && vueVersion !== packageVersion) { 8 | throw new Error( 9 | '\n\nVue packages version mismatch:\n\n' + 10 | '- vue@' + vueVersion + '\n' + 11 | '- ' + packageName + '@' + packageVersion + '\n\n' + 12 | 'This may cause things to work incorrectly. Make sure to use the same version for both.\n' + 13 | 'If you are using weex-vue-loader, re-installing them should bump ' + packageName + ' to the latest.\n' 14 | ) 15 | } 16 | 17 | module.exports = require('./build') 18 | -------------------------------------------------------------------------------- /packages/weex-template-compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weex-template-compiler", 3 | "version": "2.4.2-weex.1", 4 | "description": "Weex template compiler for Vue 2.0", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/vuejs/vue.git" 9 | }, 10 | "keywords": [ 11 | "vue", 12 | "compiler" 13 | ], 14 | "author": "Evan You", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/vuejs/vue/issues" 18 | }, 19 | "homepage": "https://github.com/vuejs/vue/tree/dev/packages/weex-template-compiler#readme", 20 | "dependencies": { 21 | "acorn": "^5.2.1", 22 | "escodegen": "^1.8.1", 23 | "he": "^1.1.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/weex-vue-framework/README.md: -------------------------------------------------------------------------------- 1 | # weex-vue-framework 2 | 3 | > This package is auto-generated. For pull requests please see [src/platforms/weex/entry-framework.js](https://github.com/vuejs/vue/blob/dev/src/platforms/weex/entry-framework.js). 4 | -------------------------------------------------------------------------------- /packages/weex-vue-framework/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weex-vue-framework", 3 | "version": "2.4.2-weex.1", 4 | "description": "Vue 2.0 Framework for Weex", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/vuejs/vue.git" 9 | }, 10 | "keywords": [ 11 | "vue", 12 | "compiler" 13 | ], 14 | "author": "Evan You", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/vuejs/vue/issues" 18 | }, 19 | "homepage": "https://github.com/vuejs/vue/tree/dev/packages/weex-vue-framework#readme" 20 | } 21 | -------------------------------------------------------------------------------- /scripts/alias.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | const resolve = p => path.resolve(__dirname, '../', p) 4 | 5 | module.exports = { 6 | vue: resolve('src/platforms/web/entry-runtime-with-compiler'), 7 | compiler: resolve('src/compiler'), 8 | core: resolve('src/core'), 9 | shared: resolve('src/shared'), 10 | web: resolve('src/platforms/web'), 11 | weex: resolve('src/platforms/weex'), 12 | server: resolve('src/server'), 13 | sfc: resolve('src/sfc') 14 | } 15 | -------------------------------------------------------------------------------- /scripts/feature-flags.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NEW_SLOT_SYNTAX: true, 3 | VBIND_PROP_SHORTHAND: false 4 | } 5 | -------------------------------------------------------------------------------- /scripts/gen-release-note.js: -------------------------------------------------------------------------------- 1 | const version = process.argv[2] || process.env.VERSION 2 | const cc = require('conventional-changelog') 3 | const file = `./RELEASE_NOTE${version ? `_${version}` : ``}.md` 4 | const fileStream = require('fs').createWriteStream(file) 5 | 6 | cc({ 7 | preset: 'angular', 8 | pkg: { 9 | transform (pkg) { 10 | pkg.version = `v${version}` 11 | return pkg 12 | } 13 | } 14 | }).pipe(fileStream).on('close', () => { 15 | console.log(`Generated release note at ${file}`) 16 | }) 17 | -------------------------------------------------------------------------------- /scripts/get-weex-version.js: -------------------------------------------------------------------------------- 1 | const coreVersion = require('../package.json').version 2 | const weexVersion = require('../packages/weex-vue-framework/package.json').version 3 | let weexBaseVersion = weexVersion.match(/^[\d.]+/)[0] 4 | let weexSubVersion = Number(weexVersion.match(/-weex\.(\d+)$/)[1]) 5 | 6 | if (weexBaseVersion === coreVersion) { 7 | // same core version, increment sub version 8 | weexSubVersion++ 9 | } else { 10 | // new core version, reset sub version 11 | weexBaseVersion = coreVersion 12 | weexSubVersion = 1 13 | } 14 | 15 | if (process.argv[2] === '-c') { 16 | console.log(weexVersion) 17 | } else { 18 | console.log(weexBaseVersion + '-weex.' + weexSubVersion) 19 | } 20 | 21 | module.exports = { 22 | base: weexBaseVersion, 23 | sub: weexSubVersion 24 | } 25 | -------------------------------------------------------------------------------- /scripts/git-hooks/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Validate commit log 4 | commit_regex='^Merge.+|(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert|types)(\(.+\))?: .{1,50}' 5 | 6 | if ! grep -iqE "$commit_regex" "$1"; then 7 | echo 8 | echo " Error: proper commit message format is required for automated changelog generation." 9 | echo 10 | echo " - Use \`npm run commit\` to interactively generate a commit message." 11 | echo " - See .github/COMMIT_CONVENTION.md for more details." 12 | echo 13 | exit 1 14 | fi 15 | -------------------------------------------------------------------------------- /scripts/git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | files_to_lint=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js$') 4 | 5 | if [ -n "$files_to_lint" ]; then 6 | NODE_ENV=production eslint --quiet $files_to_lint 7 | fi 8 | -------------------------------------------------------------------------------- /scripts/release-weex.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | CUR_VERSION=$(node build/get-weex-version.js -c) 4 | NEXT_VERSION=$(node build/get-weex-version.js) 5 | 6 | echo "Current: $CUR_VERSION" 7 | read -p "Enter new version ($NEXT_VERSION): " -n 1 -r 8 | if ! [[ -z $REPLY ]]; then 9 | NEXT_VERSION=$REPLY 10 | fi 11 | 12 | read -p "Releasing weex-vue-framework@$NEXT_VERSION - are you sure? (y/n) " -n 1 -r 13 | echo 14 | if [[ $REPLY =~ ^[Yy]$ ]]; then 15 | echo "Releasing weex-vue-framework@$NEXT_VERSION ..." 16 | npm run lint 17 | npm run flow 18 | npm run test:weex 19 | 20 | # build 21 | WEEX_VERSION=$NEXT_VERSION npm run build:weex 22 | 23 | # update package 24 | # using subshells to avoid having to cd back 25 | ( cd packages/weex-vue-framework 26 | npm version "$NEXT_VERSION" 27 | npm publish 28 | ) 29 | 30 | ( cd packages/weex-template-compiler 31 | npm version "$NEXT_VERSION" 32 | npm publish 33 | ) 34 | 35 | # commit 36 | git add packages/weex* 37 | git commit -m "[release] weex-vue-framework@$NEXT_VERSION" 38 | fi 39 | -------------------------------------------------------------------------------- /scripts/verify-commit-msg.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk') 2 | const msgPath = process.env.GIT_PARAMS 3 | const msg = require('fs').readFileSync(msgPath, 'utf-8').trim() 4 | 5 | const commitRE = /^(revert: )?(feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types|build)(\(.+\))?: .{1,50}/ 6 | 7 | if (!commitRE.test(msg)) { 8 | console.log() 9 | console.error( 10 | ` ${chalk.bgRed.white(' ERROR ')} ${chalk.red(`invalid commit message format.`)}\n\n` + 11 | chalk.red(` Proper commit message format is required for automated changelog generation. Examples:\n\n`) + 12 | ` ${chalk.green(`feat(compiler): add 'comments' option`)}\n` + 13 | ` ${chalk.green(`fix(v-model): handle events on blur (close #28)`)}\n\n` + 14 | chalk.red(` See .github/COMMIT_CONVENTION.md for more details.\n`) + 15 | chalk.red(` You can also use ${chalk.cyan(`npm run commit`)} to interactively generate a commit message.\n`) 16 | ) 17 | process.exit(1) 18 | } 19 | -------------------------------------------------------------------------------- /src/compiler/codeframe.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | const range = 2 4 | 5 | export function generateCodeFrame ( 6 | source: string, 7 | start: number = 0, 8 | end: number = source.length 9 | ): string { 10 | const lines = source.split(/\r?\n/) 11 | let count = 0 12 | const res = [] 13 | for (let i = 0; i < lines.length; i++) { 14 | count += lines[i].length + 1 15 | if (count >= start) { 16 | for (let j = i - range; j <= i + range || end > count; j++) { 17 | if (j < 0 || j >= lines.length) continue 18 | res.push(`${j + 1}${repeat(` `, 3 - String(j + 1).length)}| ${lines[j]}`) 19 | const lineLength = lines[j].length 20 | if (j === i) { 21 | // push underline 22 | const pad = start - (count - lineLength) + 1 23 | const length = end > count ? lineLength - pad : end - start 24 | res.push(` | ` + repeat(` `, pad) + repeat(`^`, length)) 25 | } else if (j > i) { 26 | if (end > count) { 27 | const length = Math.min(end - count, lineLength) 28 | res.push(` | ` + repeat(`^`, length)) 29 | } 30 | count += lineLength + 1 31 | } 32 | } 33 | break 34 | } 35 | } 36 | return res.join('\n') 37 | } 38 | 39 | function repeat (str, n) { 40 | let result = '' 41 | if (n > 0) { 42 | while (true) { // eslint-disable-line 43 | if (n & 1) result += str 44 | n >>>= 1 45 | if (n <= 0) break 46 | str += str 47 | } 48 | } 49 | return result 50 | } 51 | -------------------------------------------------------------------------------- /src/compiler/directives/bind.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export default function bind (el: ASTElement, dir: ASTDirective) { 4 | el.wrapData = (code: string) => { 5 | return `_b(${code},'${el.tag}',${dir.value},${ 6 | dir.modifiers && dir.modifiers.prop ? 'true' : 'false' 7 | }${ 8 | dir.modifiers && dir.modifiers.sync ? ',true' : '' 9 | })` 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/compiler/directives/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import on from './on' 4 | import bind from './bind' 5 | import { noop } from 'shared/util' 6 | 7 | export default { 8 | on, 9 | bind, 10 | cloak: noop 11 | } 12 | -------------------------------------------------------------------------------- /src/compiler/directives/on.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn } from 'core/util/index' 4 | 5 | export default function on (el: ASTElement, dir: ASTDirective) { 6 | if (process.env.NODE_ENV !== 'production' && dir.modifiers) { 7 | warn(`v-on without argument does not support modifiers.`) 8 | } 9 | el.wrapListeners = (code: string) => `_g(${code},${dir.value})` 10 | } 11 | -------------------------------------------------------------------------------- /src/compiler/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parse } from './parser/index' 4 | import { optimize } from './optimizer' 5 | import { generate } from './codegen/index' 6 | import { createCompilerCreator } from './create-compiler' 7 | 8 | // `createCompilerCreator` allows creating compilers that use alternative 9 | // parser/optimizer/codegen, e.g the SSR optimizing compiler. 10 | // Here we just export a default compiler using the default parts. 11 | export const createCompiler = createCompilerCreator(function baseCompile ( 12 | template: string, 13 | options: CompilerOptions 14 | ): CompiledResult { 15 | const ast = parse(template.trim(), options) 16 | if (options.optimize !== false) { 17 | optimize(ast, options) 18 | } 19 | const code = generate(ast, options) 20 | return { 21 | ast, 22 | render: code.render, 23 | staticRenderFns: code.staticRenderFns 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /src/compiler/parser/entity-decoder.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | let decoder 4 | 5 | export default { 6 | decode (html: string): string { 7 | decoder = decoder || document.createElement('div') 8 | decoder.innerHTML = html 9 | return decoder.textContent 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/core/components/index.js: -------------------------------------------------------------------------------- 1 | import KeepAlive from './keep-alive' 2 | 3 | export default { 4 | KeepAlive 5 | } 6 | -------------------------------------------------------------------------------- /src/core/global-api/assets.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { ASSET_TYPES } from 'shared/constants' 4 | import { isPlainObject, validateComponentName } from '../util/index' 5 | 6 | export function initAssetRegisters (Vue: GlobalAPI) { 7 | /** 8 | * Create asset registration methods. 9 | */ 10 | ASSET_TYPES.forEach(type => { 11 | Vue[type] = function ( 12 | id: string, 13 | definition: Function | Object 14 | ): Function | Object | void { 15 | if (!definition) { 16 | return this.options[type + 's'][id] 17 | } else { 18 | /* istanbul ignore if */ 19 | if (process.env.NODE_ENV !== 'production' && type === 'component') { 20 | validateComponentName(id) 21 | } 22 | if (type === 'component' && isPlainObject(definition)) { 23 | definition.name = definition.name || id 24 | definition = this.options._base.extend(definition) 25 | } 26 | if (type === 'directive' && typeof definition === 'function') { 27 | definition = { bind: definition, update: definition } 28 | } 29 | this.options[type + 's'][id] = definition 30 | return definition 31 | } 32 | } 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /src/core/global-api/mixin.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { mergeOptions } from '../util/index' 4 | 5 | export function initMixin (Vue: GlobalAPI) { 6 | Vue.mixin = function (mixin: Object) { 7 | this.options = mergeOptions(this.options, mixin) 8 | return this 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/core/global-api/use.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { toArray } from '../util/index' 4 | 5 | export function initUse (Vue: GlobalAPI) { 6 | Vue.use = function (plugin: Function | Object) { 7 | const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])) 8 | if (installedPlugins.indexOf(plugin) > -1) { 9 | return this 10 | } 11 | 12 | // additional parameters 13 | const args = toArray(arguments, 1) 14 | args.unshift(this) 15 | if (typeof plugin.install === 'function') { 16 | plugin.install.apply(plugin, args) 17 | } else if (typeof plugin === 'function') { 18 | plugin.apply(null, args) 19 | } 20 | installedPlugins.push(plugin) 21 | return this 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/core/index.js: -------------------------------------------------------------------------------- 1 | import Vue from './instance/index' 2 | import { initGlobalAPI } from './global-api/index' 3 | import { isServerRendering } from 'core/util/env' 4 | import { FunctionalRenderContext } from 'core/vdom/create-functional-component' 5 | 6 | initGlobalAPI(Vue) 7 | 8 | Object.defineProperty(Vue.prototype, '$isServer', { 9 | get: isServerRendering 10 | }) 11 | 12 | Object.defineProperty(Vue.prototype, '$ssrContext', { 13 | get () { 14 | /* istanbul ignore next */ 15 | return this.$vnode && this.$vnode.ssrContext 16 | } 17 | }) 18 | 19 | // expose FunctionalRenderContext for ssr runtime helper installation 20 | Object.defineProperty(Vue, 'FunctionalRenderContext', { 21 | value: FunctionalRenderContext 22 | }) 23 | 24 | Vue.version = '__VERSION__' 25 | 26 | export default Vue 27 | -------------------------------------------------------------------------------- /src/core/instance/index.js: -------------------------------------------------------------------------------- 1 | import { initMixin } from './init' 2 | import { stateMixin } from './state' 3 | import { renderMixin } from './render' 4 | import { eventsMixin } from './events' 5 | import { lifecycleMixin } from './lifecycle' 6 | import { warn } from '../util/index' 7 | 8 | function Vue (options) { 9 | if (process.env.NODE_ENV !== 'production' && 10 | !(this instanceof Vue) 11 | ) { 12 | warn('Vue is a constructor and should be called with the `new` keyword') 13 | } 14 | this._init(options) 15 | } 16 | 17 | initMixin(Vue) 18 | stateMixin(Vue) 19 | eventsMixin(Vue) 20 | lifecycleMixin(Vue) 21 | renderMixin(Vue) 22 | 23 | export default Vue 24 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/bind-dynamic-keys.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | // helper to process dynamic keys for dynamic arguments in v-bind and v-on. 4 | // For example, the following template: 5 | // 6 | //
7 | // 8 | // compiles to the following: 9 | // 10 | // _c('div', { attrs: bindDynamicKeys({ "id": "app" }, [key, value]) }) 11 | 12 | import { warn } from 'core/util/debug' 13 | 14 | export function bindDynamicKeys (baseObj: Object, values: Array): Object { 15 | for (let i = 0; i < values.length; i += 2) { 16 | const key = values[i] 17 | if (typeof key === 'string' && key) { 18 | baseObj[values[i]] = values[i + 1] 19 | } else if (process.env.NODE_ENV !== 'production' && key !== '' && key !== null) { 20 | // null is a special value for explicitly removing a binding 21 | warn( 22 | `Invalid value for dynamic directive argument (expected string or null): ${key}`, 23 | this 24 | ) 25 | } 26 | } 27 | return baseObj 28 | } 29 | 30 | // helper to dynamically append modifier runtime markers to event names. 31 | // ensure only append when value is already string, otherwise it will be cast 32 | // to string and cause the type check to miss. 33 | export function prependModifier (value: any, symbol: string): any { 34 | return typeof value === 'string' ? symbol + value : value 35 | } 36 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/bind-object-listeners.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn, extend, isPlainObject } from 'core/util/index' 4 | 5 | export function bindObjectListeners (data: any, value: any): VNodeData { 6 | if (value) { 7 | if (!isPlainObject(value)) { 8 | process.env.NODE_ENV !== 'production' && warn( 9 | 'v-on without argument expects an Object value', 10 | this 11 | ) 12 | } else { 13 | const on = data.on = data.on ? extend({}, data.on) : {} 14 | for (const key in value) { 15 | const existing = on[key] 16 | const ours = value[key] 17 | on[key] = existing ? [].concat(existing, ours) : ours 18 | } 19 | } 20 | } 21 | return data 22 | } 23 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/check-keycodes.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from 'core/config' 4 | import { hyphenate } from 'shared/util' 5 | 6 | function isKeyNotMatch (expect: T | Array, actual: T): boolean { 7 | if (Array.isArray(expect)) { 8 | return expect.indexOf(actual) === -1 9 | } else { 10 | return expect !== actual 11 | } 12 | } 13 | 14 | /** 15 | * Runtime helper for checking keyCodes from config. 16 | * exposed as Vue.prototype._k 17 | * passing in eventKeyName as last argument separately for backwards compat 18 | */ 19 | export function checkKeyCodes ( 20 | eventKeyCode: number, 21 | key: string, 22 | builtInKeyCode?: number | Array, 23 | eventKeyName?: string, 24 | builtInKeyName?: string | Array 25 | ): ?boolean { 26 | const mappedKeyCode = config.keyCodes[key] || builtInKeyCode 27 | if (builtInKeyName && eventKeyName && !config.keyCodes[key]) { 28 | return isKeyNotMatch(builtInKeyName, eventKeyName) 29 | } else if (mappedKeyCode) { 30 | return isKeyNotMatch(mappedKeyCode, eventKeyCode) 31 | } else if (eventKeyName) { 32 | return hyphenate(eventKeyName) !== key 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { toNumber, toString, looseEqual, looseIndexOf } from 'shared/util' 4 | import { createTextVNode, createEmptyVNode } from 'core/vdom/vnode' 5 | import { renderList } from './render-list' 6 | import { renderSlot } from './render-slot' 7 | import { resolveFilter } from './resolve-filter' 8 | import { checkKeyCodes } from './check-keycodes' 9 | import { bindObjectProps } from './bind-object-props' 10 | import { renderStatic, markOnce } from './render-static' 11 | import { bindObjectListeners } from './bind-object-listeners' 12 | import { resolveScopedSlots } from './resolve-scoped-slots' 13 | import { bindDynamicKeys, prependModifier } from './bind-dynamic-keys' 14 | 15 | export function installRenderHelpers (target: any) { 16 | target._o = markOnce 17 | target._n = toNumber 18 | target._s = toString 19 | target._l = renderList 20 | target._t = renderSlot 21 | target._q = looseEqual 22 | target._i = looseIndexOf 23 | target._m = renderStatic 24 | target._f = resolveFilter 25 | target._k = checkKeyCodes 26 | target._b = bindObjectProps 27 | target._v = createTextVNode 28 | target._e = createEmptyVNode 29 | target._u = resolveScopedSlots 30 | target._g = bindObjectListeners 31 | target._d = bindDynamicKeys 32 | target._p = prependModifier 33 | } 34 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/render-list.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isObject, isDef, hasSymbol } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering v-for lists. 7 | */ 8 | export function renderList ( 9 | val: any, 10 | render: ( 11 | val: any, 12 | keyOrIndex: string | number, 13 | index?: number 14 | ) => VNode 15 | ): ?Array { 16 | let ret: ?Array, i, l, keys, key 17 | if (Array.isArray(val) || typeof val === 'string') { 18 | ret = new Array(val.length) 19 | for (i = 0, l = val.length; i < l; i++) { 20 | ret[i] = render(val[i], i) 21 | } 22 | } else if (typeof val === 'number') { 23 | ret = new Array(val) 24 | for (i = 0; i < val; i++) { 25 | ret[i] = render(i + 1, i) 26 | } 27 | } else if (isObject(val)) { 28 | if (hasSymbol && val[Symbol.iterator]) { 29 | ret = [] 30 | const iterator: Iterator = val[Symbol.iterator]() 31 | let result = iterator.next() 32 | while (!result.done) { 33 | ret.push(render(result.value, ret.length)) 34 | result = iterator.next() 35 | } 36 | } else { 37 | keys = Object.keys(val) 38 | ret = new Array(keys.length) 39 | for (i = 0, l = keys.length; i < l; i++) { 40 | key = keys[i] 41 | ret[i] = render(val[key], key, i) 42 | } 43 | } 44 | } 45 | if (!isDef(ret)) { 46 | ret = [] 47 | } 48 | (ret: any)._isVList = true 49 | return ret 50 | } 51 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/render-slot.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { extend, warn, isObject } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering 7 | */ 8 | export function renderSlot ( 9 | name: string, 10 | fallback: ?Array, 11 | props: ?Object, 12 | bindObject: ?Object 13 | ): ?Array { 14 | const scopedSlotFn = this.$scopedSlots[name] 15 | let nodes 16 | if (scopedSlotFn) { // scoped slot 17 | props = props || {} 18 | if (bindObject) { 19 | if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { 20 | warn( 21 | 'slot v-bind without argument expects an Object', 22 | this 23 | ) 24 | } 25 | props = extend(extend({}, bindObject), props) 26 | } 27 | nodes = scopedSlotFn(props) || fallback 28 | } else { 29 | nodes = this.$slots[name] || fallback 30 | } 31 | 32 | const target = props && props.slot 33 | if (target) { 34 | return this.$createElement('template', { slot: target }, nodes) 35 | } else { 36 | return nodes 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/resolve-filter.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { identity, resolveAsset } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for resolving filters 7 | */ 8 | export function resolveFilter (id: string): Function { 9 | return resolveAsset(this.$options, 'filters', id, true) || identity 10 | } 11 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/resolve-scoped-slots.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function resolveScopedSlots ( 4 | fns: ScopedSlotsData, // see flow/vnode 5 | res?: Object, 6 | // the following are added in 2.6 7 | hasDynamicKeys?: boolean, 8 | contentHashKey?: number 9 | ): { [key: string]: Function, $stable: boolean } { 10 | res = res || { $stable: !hasDynamicKeys } 11 | for (let i = 0; i < fns.length; i++) { 12 | const slot = fns[i] 13 | if (Array.isArray(slot)) { 14 | resolveScopedSlots(slot, res, hasDynamicKeys) 15 | } else if (slot) { 16 | // marker for reverse proxying v-slot without scope on this.$slots 17 | if (slot.proxy) { 18 | slot.fn.proxy = true 19 | } 20 | res[slot.key] = slot.fn 21 | } 22 | } 23 | if (contentHashKey) { 24 | (res: any).$key = contentHashKey 25 | } 26 | return res 27 | } 28 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/resolve-slots.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import type VNode from 'core/vdom/vnode' 4 | 5 | /** 6 | * Runtime helper for resolving raw children VNodes into a slot object. 7 | */ 8 | export function resolveSlots ( 9 | children: ?Array, 10 | context: ?Component 11 | ): { [key: string]: Array } { 12 | if (!children || !children.length) { 13 | return {} 14 | } 15 | const slots = {} 16 | for (let i = 0, l = children.length; i < l; i++) { 17 | const child = children[i] 18 | const data = child.data 19 | // remove slot attribute if the node is resolved as a Vue slot node 20 | if (data && data.attrs && data.attrs.slot) { 21 | delete data.attrs.slot 22 | } 23 | // named slots should only be respected if the vnode was rendered in the 24 | // same context. 25 | if ((child.context === context || child.fnContext === context) && 26 | data && data.slot != null 27 | ) { 28 | const name = data.slot 29 | const slot = (slots[name] || (slots[name] = [])) 30 | if (child.tag === 'template') { 31 | slot.push.apply(slot, child.children || []) 32 | } else { 33 | slot.push(child) 34 | } 35 | } else { 36 | (slots.default || (slots.default = [])).push(child) 37 | } 38 | } 39 | // ignore slots that contains only whitespace 40 | for (const name in slots) { 41 | if (slots[name].every(isWhitespace)) { 42 | delete slots[name] 43 | } 44 | } 45 | return slots 46 | } 47 | 48 | function isWhitespace (node: VNode): boolean { 49 | return (node.isComment && !node.asyncFactory) || node.text === ' ' 50 | } 51 | -------------------------------------------------------------------------------- /src/core/observer/array.js: -------------------------------------------------------------------------------- 1 | /* 2 | * not type checking this file because flow doesn't play well with 3 | * dynamically accessing methods on Array prototype 4 | */ 5 | 6 | import { def } from '../util/index' 7 | 8 | const arrayProto = Array.prototype 9 | export const arrayMethods = Object.create(arrayProto) 10 | 11 | const methodsToPatch = [ 12 | 'push', 13 | 'pop', 14 | 'shift', 15 | 'unshift', 16 | 'splice', 17 | 'sort', 18 | 'reverse' 19 | ] 20 | 21 | /** 22 | * Intercept mutating methods and emit events 23 | */ 24 | methodsToPatch.forEach(function (method) { 25 | // cache original method 26 | const original = arrayProto[method] 27 | def(arrayMethods, method, function mutator (...args) { 28 | const result = original.apply(this, args) 29 | const ob = this.__ob__ 30 | let inserted 31 | switch (method) { 32 | case 'push': 33 | case 'unshift': 34 | inserted = args 35 | break 36 | case 'splice': 37 | inserted = args.slice(2) 38 | break 39 | } 40 | if (inserted) ob.observeArray(inserted) 41 | // notify change 42 | ob.dep.notify() 43 | return result 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /src/core/observer/traverse.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { _Set as Set, isObject } from '../util/index' 4 | import type { SimpleSet } from '../util/index' 5 | import VNode from '../vdom/vnode' 6 | 7 | const seenObjects = new Set() 8 | 9 | /** 10 | * Recursively traverse an object to evoke all converted 11 | * getters, so that every nested property inside the object 12 | * is collected as a "deep" dependency. 13 | */ 14 | export function traverse (val: any) { 15 | _traverse(val, seenObjects) 16 | seenObjects.clear() 17 | } 18 | 19 | function _traverse (val: any, seen: SimpleSet) { 20 | let i, keys 21 | const isA = Array.isArray(val) 22 | if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) { 23 | return 24 | } 25 | if (val.__ob__) { 26 | const depId = val.__ob__.dep.id 27 | if (seen.has(depId)) { 28 | return 29 | } 30 | seen.add(depId) 31 | } 32 | if (isA) { 33 | i = val.length 34 | while (i--) _traverse(val[i], seen) 35 | } else { 36 | keys = Object.keys(val) 37 | i = keys.length 38 | while (i--) _traverse(val[keys[i]], seen) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/core/util/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export * from 'shared/util' 4 | export * from './lang' 5 | export * from './env' 6 | export * from './options' 7 | export * from './debug' 8 | export * from './props' 9 | export * from './error' 10 | export * from './next-tick' 11 | export { defineReactive } from '../observer/index' 12 | -------------------------------------------------------------------------------- /src/core/util/lang.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | /** 4 | * unicode letters used for parsing html tags, component names and property paths. 5 | * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname 6 | * skipping \u10000-\uEFFFF due to it freezing up PhantomJS 7 | */ 8 | export const unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/ 9 | 10 | /** 11 | * Check if a string starts with $ or _ 12 | */ 13 | export function isReserved (str: string): boolean { 14 | const c = (str + '').charCodeAt(0) 15 | return c === 0x24 || c === 0x5F 16 | } 17 | 18 | /** 19 | * Define a property. 20 | */ 21 | export function def (obj: Object, key: string, val: any, enumerable?: boolean) { 22 | Object.defineProperty(obj, key, { 23 | value: val, 24 | enumerable: !!enumerable, 25 | writable: true, 26 | configurable: true 27 | }) 28 | } 29 | 30 | /** 31 | * Parse simple path. 32 | */ 33 | const bailRE = new RegExp(`[^${unicodeRegExp.source}.$_\\d]`) 34 | export function parsePath (path: string): any { 35 | if (bailRE.test(path)) { 36 | return 37 | } 38 | const segments = path.split('.') 39 | return function (obj) { 40 | for (let i = 0; i < segments.length; i++) { 41 | if (!obj) return 42 | obj = obj[segments[i]] 43 | } 44 | return obj 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/core/util/perf.js: -------------------------------------------------------------------------------- 1 | import { inBrowser } from './env' 2 | 3 | export let mark 4 | export let measure 5 | 6 | if (process.env.NODE_ENV !== 'production') { 7 | const perf = inBrowser && window.performance 8 | /* istanbul ignore if */ 9 | if ( 10 | perf && 11 | perf.mark && 12 | perf.measure && 13 | perf.clearMarks && 14 | perf.clearMeasures 15 | ) { 16 | mark = tag => perf.mark(tag) 17 | measure = (name, startTag, endTag) => { 18 | perf.measure(name, startTag, endTag) 19 | perf.clearMarks(startTag) 20 | perf.clearMarks(endTag) 21 | // perf.clearMeasures(name) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/get-first-component-child.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isDef } from 'shared/util' 4 | import { isAsyncPlaceholder } from './is-async-placeholder' 5 | 6 | export function getFirstComponentChild (children: ?Array): ?VNode { 7 | if (Array.isArray(children)) { 8 | for (let i = 0; i < children.length; i++) { 9 | const c = children[i] 10 | if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { 11 | return c 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export * from './merge-hook' 4 | export * from './extract-props' 5 | export * from './update-listeners' 6 | export * from './normalize-children' 7 | export * from './resolve-async-component' 8 | export * from './get-first-component-child' 9 | export * from './is-async-placeholder' 10 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/is-async-placeholder.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function isAsyncPlaceholder (node: VNode): boolean { 4 | return node.isComment && node.asyncFactory 5 | } 6 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/merge-hook.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import VNode from '../vnode' 4 | import { createFnInvoker } from './update-listeners' 5 | import { remove, isDef, isUndef, isTrue } from 'shared/util' 6 | 7 | export function mergeVNodeHook (def: Object, hookKey: string, hook: Function) { 8 | if (def instanceof VNode) { 9 | def = def.data.hook || (def.data.hook = {}) 10 | } 11 | let invoker 12 | const oldHook = def[hookKey] 13 | 14 | function wrappedHook () { 15 | hook.apply(this, arguments) 16 | // important: remove merged hook to ensure it's called only once 17 | // and prevent memory leak 18 | remove(invoker.fns, wrappedHook) 19 | } 20 | 21 | if (isUndef(oldHook)) { 22 | // no existing hook 23 | invoker = createFnInvoker([wrappedHook]) 24 | } else { 25 | /* istanbul ignore if */ 26 | if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { 27 | // already a merged invoker 28 | invoker = oldHook 29 | invoker.fns.push(wrappedHook) 30 | } else { 31 | // existing plain hook 32 | invoker = createFnInvoker([oldHook, wrappedHook]) 33 | } 34 | } 35 | 36 | invoker.merged = true 37 | def[hookKey] = invoker 38 | } 39 | -------------------------------------------------------------------------------- /src/core/vdom/modules/index.js: -------------------------------------------------------------------------------- 1 | import directives from './directives' 2 | import ref from './ref' 3 | 4 | export default [ 5 | ref, 6 | directives 7 | ] 8 | -------------------------------------------------------------------------------- /src/core/vdom/modules/ref.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { remove, isDef } from 'shared/util' 4 | 5 | export default { 6 | create (_: any, vnode: VNodeWithData) { 7 | registerRef(vnode) 8 | }, 9 | update (oldVnode: VNodeWithData, vnode: VNodeWithData) { 10 | if (oldVnode.data.ref !== vnode.data.ref) { 11 | registerRef(oldVnode, true) 12 | registerRef(vnode) 13 | } 14 | }, 15 | destroy (vnode: VNodeWithData) { 16 | registerRef(vnode, true) 17 | } 18 | } 19 | 20 | export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) { 21 | const key = vnode.data.ref 22 | if (!isDef(key)) return 23 | 24 | const vm = vnode.context 25 | const ref = vnode.componentInstance || vnode.elm 26 | const refs = vm.$refs 27 | if (isRemoval) { 28 | if (Array.isArray(refs[key])) { 29 | remove(refs[key], ref) 30 | } else if (refs[key] === ref) { 31 | refs[key] = undefined 32 | } 33 | } else { 34 | if (vnode.data.refInFor) { 35 | if (!Array.isArray(refs[key])) { 36 | refs[key] = [ref] 37 | } else if (refs[key].indexOf(ref) < 0) { 38 | // $flow-disable-line 39 | refs[key].push(ref) 40 | } 41 | } else { 42 | refs[key] = ref 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/directives/html.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addProp } from 'compiler/helpers' 4 | 5 | export default function html (el: ASTElement, dir: ASTDirective) { 6 | if (dir.value) { 7 | addProp(el, 'innerHTML', `_s(${dir.value})`, dir) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | import text from './text' 3 | import html from './html' 4 | 5 | export default { 6 | model, 7 | text, 8 | html 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/directives/text.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addProp } from 'compiler/helpers' 4 | 5 | export default function text (el: ASTElement, dir: ASTDirective) { 6 | if (dir.value) { 7 | addProp(el, 'textContent', `_s(${dir.value})`, dir) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { baseOptions } from './options' 4 | import { createCompiler } from 'compiler/index' 5 | 6 | const { compile, compileToFunctions } = createCompiler(baseOptions) 7 | 8 | export { compile, compileToFunctions } 9 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/modules/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parseText } from 'compiler/parser/text-parser' 4 | import { 5 | getAndRemoveAttr, 6 | getBindingAttr, 7 | baseWarn 8 | } from 'compiler/helpers' 9 | 10 | function transformNode (el: ASTElement, options: CompilerOptions) { 11 | const warn = options.warn || baseWarn 12 | const staticClass = getAndRemoveAttr(el, 'class') 13 | if (process.env.NODE_ENV !== 'production' && staticClass) { 14 | const res = parseText(staticClass, options.delimiters) 15 | if (res) { 16 | warn( 17 | `class="${staticClass}": ` + 18 | 'Interpolation inside attributes has been removed. ' + 19 | 'Use v-bind or the colon shorthand instead. For example, ' + 20 | 'instead of
, use
.', 21 | el.rawAttrsMap['class'] 22 | ) 23 | } 24 | } 25 | if (staticClass) { 26 | el.staticClass = JSON.stringify(staticClass) 27 | } 28 | const classBinding = getBindingAttr(el, 'class', false /* getStatic */) 29 | if (classBinding) { 30 | el.classBinding = classBinding 31 | } 32 | } 33 | 34 | function genData (el: ASTElement): string { 35 | let data = '' 36 | if (el.staticClass) { 37 | data += `staticClass:${el.staticClass},` 38 | } 39 | if (el.classBinding) { 40 | data += `class:${el.classBinding},` 41 | } 42 | return data 43 | } 44 | 45 | export default { 46 | staticKeys: ['staticClass'], 47 | transformNode, 48 | genData 49 | } 50 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/modules/index.js: -------------------------------------------------------------------------------- 1 | import klass from './class' 2 | import style from './style' 3 | import model from './model' 4 | 5 | export default [ 6 | klass, 7 | style, 8 | model 9 | ] 10 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/modules/style.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parseText } from 'compiler/parser/text-parser' 4 | import { parseStyleText } from 'web/util/style' 5 | import { 6 | getAndRemoveAttr, 7 | getBindingAttr, 8 | baseWarn 9 | } from 'compiler/helpers' 10 | 11 | function transformNode (el: ASTElement, options: CompilerOptions) { 12 | const warn = options.warn || baseWarn 13 | const staticStyle = getAndRemoveAttr(el, 'style') 14 | if (staticStyle) { 15 | /* istanbul ignore if */ 16 | if (process.env.NODE_ENV !== 'production') { 17 | const res = parseText(staticStyle, options.delimiters) 18 | if (res) { 19 | warn( 20 | `style="${staticStyle}": ` + 21 | 'Interpolation inside attributes has been removed. ' + 22 | 'Use v-bind or the colon shorthand instead. For example, ' + 23 | 'instead of
, use
.', 24 | el.rawAttrsMap['style'] 25 | ) 26 | } 27 | } 28 | el.staticStyle = JSON.stringify(parseStyleText(staticStyle)) 29 | } 30 | 31 | const styleBinding = getBindingAttr(el, 'style', false /* getStatic */) 32 | if (styleBinding) { 33 | el.styleBinding = styleBinding 34 | } 35 | } 36 | 37 | function genData (el: ASTElement): string { 38 | let data = '' 39 | if (el.staticStyle) { 40 | data += `staticStyle:${el.staticStyle},` 41 | } 42 | if (el.styleBinding) { 43 | data += `style:(${el.styleBinding}),` 44 | } 45 | return data 46 | } 47 | 48 | export default { 49 | staticKeys: ['staticStyle'], 50 | transformNode, 51 | genData 52 | } 53 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/options.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { 4 | isPreTag, 5 | mustUseProp, 6 | isReservedTag, 7 | getTagNamespace 8 | } from '../util/index' 9 | 10 | import modules from './modules/index' 11 | import directives from './directives/index' 12 | import { genStaticKeys } from 'shared/util' 13 | import { isUnaryTag, canBeLeftOpenTag } from './util' 14 | 15 | export const baseOptions: CompilerOptions = { 16 | expectHTML: true, 17 | modules, 18 | directives, 19 | isPreTag, 20 | isUnaryTag, 21 | mustUseProp, 22 | canBeLeftOpenTag, 23 | isReservedTag, 24 | getTagNamespace, 25 | staticKeys: genStaticKeys(modules) 26 | } 27 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { makeMap } from 'shared/util' 4 | 5 | export const isUnaryTag = makeMap( 6 | 'area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' + 7 | 'link,meta,param,source,track,wbr' 8 | ) 9 | 10 | // Elements that you can, intentionally, leave open 11 | // (and which close themselves) 12 | export const canBeLeftOpenTag = makeMap( 13 | 'colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source' 14 | ) 15 | 16 | // HTML5 tags https://html.spec.whatwg.org/multipage/indices.html#elements-3 17 | // Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content 18 | export const isNonPhrasingTag = makeMap( 19 | 'address,article,aside,base,blockquote,body,caption,col,colgroup,dd,' + 20 | 'details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,' + 21 | 'h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,' + 22 | 'optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,' + 23 | 'title,tr,track' 24 | ) 25 | -------------------------------------------------------------------------------- /src/platforms/web/entry-compiler.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export { parseComponent } from 'sfc/parser' 4 | export { compile, compileToFunctions } from './compiler/index' 5 | export { ssrCompile, ssrCompileToFunctions } from './server/compiler' 6 | export { generateCodeFrame } from 'compiler/codeframe' 7 | -------------------------------------------------------------------------------- /src/platforms/web/entry-runtime.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import Vue from './runtime/index' 4 | 5 | export default Vue 6 | -------------------------------------------------------------------------------- /src/platforms/web/entry-server-basic-renderer.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import modules from './server/modules/index' 4 | import directives from './server/directives/index' 5 | import { isUnaryTag, canBeLeftOpenTag } from './compiler/util' 6 | import { createBasicRenderer } from 'server/create-basic-renderer' 7 | 8 | export default createBasicRenderer({ 9 | modules, 10 | directives, 11 | isUnaryTag, 12 | canBeLeftOpenTag 13 | }) 14 | -------------------------------------------------------------------------------- /src/platforms/web/entry-server-renderer.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | process.env.VUE_ENV = 'server' 4 | 5 | import { extend } from 'shared/util' 6 | import modules from './server/modules/index' 7 | import baseDirectives from './server/directives/index' 8 | import { isUnaryTag, canBeLeftOpenTag } from './compiler/util' 9 | 10 | import { createRenderer as _createRenderer } from 'server/create-renderer' 11 | import { createBundleRendererCreator } from 'server/bundle-renderer/create-bundle-renderer' 12 | 13 | export function createRenderer (options?: Object = {}): { 14 | renderToString: Function, 15 | renderToStream: Function 16 | } { 17 | return _createRenderer(extend(extend({}, options), { 18 | isUnaryTag, 19 | canBeLeftOpenTag, 20 | modules, 21 | // user can provide server-side implementations for custom directives 22 | // when creating the renderer. 23 | directives: extend(baseDirectives, options.directives) 24 | })) 25 | } 26 | 27 | export const createBundleRenderer = createBundleRendererCreator(createRenderer) 28 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/components/index.js: -------------------------------------------------------------------------------- 1 | import Transition from './transition' 2 | import TransitionGroup from './transition-group' 3 | 4 | export default { 5 | Transition, 6 | TransitionGroup 7 | } 8 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | import show from './show' 3 | 4 | export default { 5 | model, 6 | show 7 | } 8 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/modules/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { 4 | isDef, 5 | isUndef 6 | } from 'shared/util' 7 | 8 | import { 9 | concat, 10 | stringifyClass, 11 | genClassForVnode 12 | } from 'web/util/index' 13 | 14 | function updateClass (oldVnode: any, vnode: any) { 15 | const el = vnode.elm 16 | const data: VNodeData = vnode.data 17 | const oldData: VNodeData = oldVnode.data 18 | if ( 19 | isUndef(data.staticClass) && 20 | isUndef(data.class) && ( 21 | isUndef(oldData) || ( 22 | isUndef(oldData.staticClass) && 23 | isUndef(oldData.class) 24 | ) 25 | ) 26 | ) { 27 | return 28 | } 29 | 30 | let cls = genClassForVnode(vnode) 31 | 32 | // handle transition classes 33 | const transitionClass = el._transitionClasses 34 | if (isDef(transitionClass)) { 35 | cls = concat(cls, stringifyClass(transitionClass)) 36 | } 37 | 38 | // set the class 39 | if (cls !== el._prevClass) { 40 | el.setAttribute('class', cls) 41 | el._prevClass = cls 42 | } 43 | } 44 | 45 | export default { 46 | create: updateClass, 47 | update: updateClass 48 | } 49 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/modules/index.js: -------------------------------------------------------------------------------- 1 | import attrs from './attrs' 2 | import klass from './class' 3 | import events from './events' 4 | import domProps from './dom-props' 5 | import style from './style' 6 | import transition from './transition' 7 | 8 | export default [ 9 | attrs, 10 | klass, 11 | events, 12 | domProps, 13 | style, 14 | transition 15 | ] 16 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/patch.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import * as nodeOps from 'web/runtime/node-ops' 4 | import { createPatchFunction } from 'core/vdom/patch' 5 | import baseModules from 'core/vdom/modules/index' 6 | import platformModules from 'web/runtime/modules/index' 7 | 8 | // the directive module should be applied last, after all 9 | // built-in modules have been applied. 10 | const modules = platformModules.concat(baseModules) 11 | 12 | export const patch: Function = createPatchFunction({ nodeOps, modules }) 13 | -------------------------------------------------------------------------------- /src/platforms/web/server/compiler.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { baseOptions } from '../compiler/options' 4 | import { createCompiler } from 'server/optimizing-compiler/index' 5 | 6 | const { compile, compileToFunctions } = createCompiler(baseOptions) 7 | 8 | export { 9 | compile as ssrCompile, 10 | compileToFunctions as ssrCompileToFunctions 11 | } 12 | -------------------------------------------------------------------------------- /src/platforms/web/server/directives/index.js: -------------------------------------------------------------------------------- 1 | import show from './show' 2 | import model from './model' 3 | 4 | export default { 5 | show, 6 | model 7 | } 8 | -------------------------------------------------------------------------------- /src/platforms/web/server/directives/model.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { looseEqual, looseIndexOf } from 'shared/util' 4 | 5 | // this is only applied for 14 |
15 |
16 | 25 | 26 | 27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 | countA: {{countA}} 36 |
37 |
38 | countB: {{countB}} 39 |
40 |
41 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /test/e2e/specs/async-edge-cases.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'async edge cases': function (browser) { 3 | browser 4 | .url('http://localhost:8080/test/e2e/specs/async-edge-cases.html') 5 | // #4510 6 | .assert.containsText('#case-1', '1') 7 | .assert.checked('#case-1 input', false) 8 | 9 | .click('#case-1 input') 10 | .assert.containsText('#case-1', '2') 11 | .assert.checked('#case-1 input', true) 12 | 13 | .click('#case-1 input') 14 | .assert.containsText('#case-1', '3') 15 | .assert.checked('#case-1 input', false) 16 | 17 | // // #6566 18 | .assert.containsText('#case-2 button', 'Expand is True') 19 | .assert.containsText('.count-a', 'countA: 0') 20 | .assert.containsText('.count-b', 'countB: 0') 21 | 22 | .click('#case-2 button') 23 | .assert.containsText('#case-2 button', 'Expand is False') 24 | .assert.containsText('.count-a', 'countA: 1') 25 | .assert.containsText('.count-b', 'countB: 0') 26 | 27 | .click('#case-2 button') 28 | .assert.containsText('#case-2 button', 'Expand is True') 29 | .assert.containsText('.count-a', 'countA: 1') 30 | .assert.containsText('.count-b', 'countB: 1') 31 | 32 | .end() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/e2e/specs/basic-ssr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
wtf
12 | 13 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/e2e/specs/basic-ssr.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'basic SSR': function (browser) { 3 | browser 4 | .url('http://localhost:8080/test/e2e/specs/basic-ssr.html') 5 | .assert.containsText('#result', '
foo
') 6 | .end() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/e2e/specs/commits.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'commits': function (browser) { 3 | browser 4 | .url('http://localhost:8080/examples/commits/') 5 | .waitForElementVisible('li', 5000) 6 | .assert.count('input', 2) 7 | .assert.count('label', 2) 8 | .assert.containsText('label[for="master"]', 'master') 9 | .assert.containsText('label[for="dev"]', 'dev') 10 | .assert.checked('#master') 11 | .assert.checked('#dev', false) 12 | .assert.containsText('p', 'vuejs/vue@master') 13 | .assert.count('li', 3) 14 | .assert.count('li .commit', 3) 15 | .assert.count('li .message', 3) 16 | .click('#dev') 17 | .assert.containsText('p', 'vuejs/vue@dev') 18 | .assert.count('li', 3) 19 | .assert.count('li .commit', 3) 20 | .assert.count('li .message', 3) 21 | .end() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/e2e/specs/markdown.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'markdown': function (browser) { 3 | browser 4 | .url('http://localhost:8080/examples/markdown/') 5 | .waitForElementVisible('#editor', 1000) 6 | .assert.value('textarea', '# hello') 7 | .assert.hasHTML('#editor div', '

hello

') 8 | .setValue('textarea', '\n## foo\n\n- bar\n- baz') 9 | // assert the output is not updated yet because of debounce 10 | .assert.hasHTML('#editor div', '

hello

') 11 | .waitFor(500) 12 | .assert.hasHTML('#editor div', 13 | '

hello

\n' + 14 | '

foo

\n' + 15 | '
    \n
  • bar
  • \n
  • baz
  • \n
' 16 | ) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/e2e/specs/modal.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'modal': function (browser) { 3 | browser 4 | .url('http://localhost:8080/examples/modal/') 5 | .waitForElementVisible('#app', 1000) 6 | .assert.elementNotPresent('.modal-mask') 7 | .click('#show-modal') 8 | .assert.elementPresent('.modal-mask') 9 | .assert.elementPresent('.modal-wrapper') 10 | .assert.elementPresent('.modal-container') 11 | .waitFor(50) 12 | .assert.cssClassPresent('.modal-mask', 'modal-enter-active') 13 | .waitFor(300) 14 | .assert.cssClassNotPresent('.modal-mask', 'modal-enter-active') 15 | .assert.containsText('.modal-header h3', 'custom header') 16 | .assert.containsText('.modal-body', 'default body') 17 | .assert.containsText('.modal-footer', 'default footer') 18 | .click('.modal-default-button') 19 | // should have transition 20 | .assert.elementPresent('.modal-mask') 21 | .waitFor(50) 22 | .assert.cssClassPresent('.modal-mask', 'modal-leave-active') 23 | .waitFor(300) 24 | .assert.elementNotPresent('.modal-mask') 25 | .end() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/helpers/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "globals": { 6 | "waitForUpdate": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/helpers/classlist.js: -------------------------------------------------------------------------------- 1 | beforeEach(() => { 2 | jasmine.addMatchers({ 3 | // since classList may not be supported in all browsers 4 | toHaveClass: () => { 5 | return { 6 | compare: (el, cls) => { 7 | const pass = el.classList 8 | ? el.classList.contains(cls) 9 | : el.getAttribute('class').split(/\s+/g).indexOf(cls) > -1 10 | return { 11 | pass, 12 | message: `Expected element${pass ? ' ' : ' not '}to have class ${cls}` 13 | } 14 | } 15 | } 16 | } 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /test/helpers/test-object-option.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export default function testObjectOption (name) { 4 | it(`Options ${name}: should warn non object value`, () => { 5 | const options = {} 6 | options[name] = () => {} 7 | new Vue(options) 8 | expect(`Invalid value for option "${name}"`).toHaveBeenWarned() 9 | }) 10 | 11 | it(`Options ${name}: should not warn valid object value`, () => { 12 | const options = {} 13 | options[name] = {} 14 | new Vue(options) 15 | expect(`Invalid value for option "${name}"`).not.toHaveBeenWarned() 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /test/helpers/to-equal.js: -------------------------------------------------------------------------------- 1 | import { isEqual } from 'lodash' 2 | 3 | beforeEach(() => { 4 | jasmine.addMatchers({ 5 | // override built-in toEqual because it behaves incorrectly 6 | // on Vue-observed arrays in Safari 7 | toEqual: () => { 8 | return { 9 | compare: (a, b) => { 10 | const pass = isEqual(a, b) 11 | return { 12 | pass, 13 | message: `Expected ${a} to equal ${b}` 14 | } 15 | } 16 | } 17 | } 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /test/helpers/trigger-event.js: -------------------------------------------------------------------------------- 1 | window.triggerEvent = function triggerEvent (target, event, process) { 2 | const e = document.createEvent('HTMLEvents') 3 | e.initEvent(event, true, true) 4 | if (event === 'click') { 5 | e.button = 0 6 | } 7 | if (process) process(e) 8 | target.dispatchEvent(e) 9 | } 10 | -------------------------------------------------------------------------------- /test/helpers/vdom.js: -------------------------------------------------------------------------------- 1 | import VNode from 'core/vdom/vnode' 2 | 3 | window.createTextVNode = function (text) { 4 | return new VNode(undefined, undefined, undefined, text) 5 | } 6 | -------------------------------------------------------------------------------- /test/ssr/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "plugins": ["jasmine"], 6 | "rules": { 7 | "jasmine/no-focused-tests": 2 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/ssr/async-loader.js: -------------------------------------------------------------------------------- 1 | const hash = require('hash-sum') 2 | 3 | module.exports = function (code) { 4 | const id = hash(this.request) // simulating vue-loader module id injection 5 | return code.replace('__MODULE_ID__', id) 6 | } 7 | -------------------------------------------------------------------------------- /test/ssr/compile-with-webpack.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import webpack from 'webpack' 3 | import MemoryFS from 'memory-fs' 4 | 5 | export function compileWithWebpack (file, extraConfig, cb) { 6 | const config = Object.assign({ 7 | mode: 'development', 8 | entry: path.resolve(__dirname, 'fixtures', file), 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.js$/, 13 | loader: 'babel-loader' 14 | }, 15 | { 16 | test: /async-.*\.js$/, 17 | loader: require.resolve('./async-loader') 18 | }, 19 | { 20 | test: /\.(png|woff2|css)$/, 21 | loader: 'file-loader', 22 | options: { 23 | name: '[name].[ext]' 24 | } 25 | } 26 | ] 27 | } 28 | }, extraConfig) 29 | 30 | const compiler = webpack(config) 31 | const fs = new MemoryFS() 32 | compiler.outputFileSystem = fs 33 | 34 | compiler.run((err, stats) => { 35 | expect(err).toBeFalsy() 36 | expect(stats.errors).toBeFalsy() 37 | cb(fs) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /test/ssr/fixtures/app.js: -------------------------------------------------------------------------------- 1 | import Vue from '../../../dist/vue.runtime.common.js' 2 | 3 | export default context => { 4 | return new Promise(resolve => { 5 | context.msg = 'hello' 6 | resolve(new Vue({ 7 | render (h) { 8 | return h('div', context.url) 9 | } 10 | })) 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /test/ssr/fixtures/async-bar.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | beforeCreate () { 3 | this.$vnode.ssrContext._registeredComponents.add('__MODULE_ID__') 4 | }, 5 | render (h) { 6 | return h('div', 'async bar') 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/ssr/fixtures/async-foo.js: -------------------------------------------------------------------------------- 1 | // import image and font 2 | import './test.css' 3 | import font from './test.woff2' 4 | import image from './test.png' 5 | 6 | export default { 7 | beforeCreate () { 8 | this.$vnode.ssrContext._registeredComponents.add('__MODULE_ID__') 9 | }, 10 | render (h) { 11 | return h('div', `async ${font} ${image}`) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/ssr/fixtures/cache-opt-out.js: -------------------------------------------------------------------------------- 1 | import Vue from '../../../dist/vue.runtime.common.js' 2 | 3 | const app = { 4 | name: 'app', 5 | props: ['id'], 6 | serverCacheKey: props => props.id === 1 ? false : props.id, 7 | render (h) { 8 | return h('div', '/test') 9 | } 10 | } 11 | 12 | export default () => { 13 | return Promise.resolve(new Vue({ 14 | render: h => h(app, { props: { id: 1 }}) 15 | })) 16 | } 17 | -------------------------------------------------------------------------------- /test/ssr/fixtures/cache.js: -------------------------------------------------------------------------------- 1 | import Vue from '../../../dist/vue.runtime.common.js' 2 | 3 | const app = { 4 | name: 'app', 5 | props: ['id'], 6 | serverCacheKey: props => props.id, 7 | render (h) { 8 | return h('div', '/test') 9 | } 10 | } 11 | 12 | export default () => { 13 | return Promise.resolve(new Vue({ 14 | render: h => h(app, { props: { id: 1 }}) 15 | })) 16 | } 17 | -------------------------------------------------------------------------------- /test/ssr/fixtures/error.js: -------------------------------------------------------------------------------- 1 | throw new Error('foo') 2 | -------------------------------------------------------------------------------- /test/ssr/fixtures/nested-cache.js: -------------------------------------------------------------------------------- 1 | import Vue from '../../../dist/vue.runtime.common.js' 2 | 3 | function createRegisterFn (id) { 4 | return function (context) { 5 | context = context || this.$vnode.ssrContext 6 | context.registered.push(id) 7 | } 8 | } 9 | 10 | function addHooks (comp) { 11 | const hook = createRegisterFn(comp.name) 12 | return Object.assign(comp, { 13 | _ssrRegister: hook, 14 | beforeCreate: hook 15 | }) 16 | } 17 | 18 | const grandchild = addHooks({ 19 | name: 'grandchild', 20 | props: ['id'], 21 | serverCacheKey: props => props.id, 22 | render (h) { 23 | return h('div', '/test') 24 | } 25 | }) 26 | 27 | const child = addHooks({ 28 | name: 'child', 29 | props: ['id'], 30 | serverCacheKey: props => props.id, 31 | render (h) { 32 | return h(grandchild, { props: { id: this.id }}) 33 | } 34 | }) 35 | 36 | const app = addHooks({ 37 | name: 'app', 38 | props: ['id'], 39 | serverCacheKey: props => props.id, 40 | render (h) { 41 | return h(child, { props: { id: this.id }}) 42 | } 43 | }) 44 | 45 | export default () => { 46 | return Promise.resolve(new Vue({ 47 | render: h => h(app, { props: { id: 1 }}) 48 | })) 49 | } 50 | -------------------------------------------------------------------------------- /test/ssr/fixtures/promise-rejection.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return Promise.reject(new Error('foo')) 3 | } 4 | -------------------------------------------------------------------------------- /test/ssr/fixtures/split.js: -------------------------------------------------------------------------------- 1 | import Vue from '../../../dist/vue.runtime.common.js' 2 | 3 | // async component! 4 | const Foo = () => import('./async-foo') 5 | const Bar = () => import('./async-bar') // eslint-disable-line 6 | 7 | export default context => { 8 | return new Promise(resolve => { 9 | context.msg = 'hello' 10 | const vm = new Vue({ 11 | render (h) { 12 | return h('div', [ 13 | context.url, 14 | h(Foo) 15 | ]) 16 | } 17 | }) 18 | 19 | // simulate router.onReady 20 | Foo().then(comp => { 21 | // resolve now to make the render sync 22 | Foo.resolved = Vue.extend(comp.default) 23 | resolve(vm) 24 | }) 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /test/ssr/fixtures/test.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/vue/6390f70c2e4ec7e51cce1d2a4ba35e2d0d328205/test/ssr/fixtures/test.css -------------------------------------------------------------------------------- /test/ssr/fixtures/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/vue/6390f70c2e4ec7e51cce1d2a4ba35e2d0d328205/test/ssr/fixtures/test.png -------------------------------------------------------------------------------- /test/ssr/fixtures/test.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/vue/6390f70c2e4ec7e51cce1d2a4ba35e2d0d328205/test/ssr/fixtures/test.woff2 -------------------------------------------------------------------------------- /test/ssr/jasmine.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | spec_dir: 'test/ssr', 3 | spec_files: [ 4 | '*.spec.js' 5 | ], 6 | helpers: [ 7 | require.resolve('@babel/register'), 8 | '../helpers/to-have-been-warned.js' 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/unit/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "globals": { 6 | "waitForUpdate": true, 7 | "triggerEvent": true, 8 | "createTextVNode": true 9 | }, 10 | "plugins": ["jasmine"], 11 | "rules": { 12 | "jasmine/no-focused-tests": 2, 13 | "no-unused-vars": 0 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/unit/features/directives/cloak.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Directive v-cloak', () => { 4 | it('should be removed after compile', () => { 5 | const el = document.createElement('div') 6 | el.setAttribute('v-cloak', '') 7 | const vm = new Vue({ el }) 8 | expect(vm.$el.hasAttribute('v-cloak')).toBe(false) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /test/unit/features/directives/model-file.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Directive v-model file', () => { 4 | it('warn to use @change instead', () => { 5 | new Vue({ 6 | data: { 7 | file: '' 8 | }, 9 | template: '' 10 | }).$mount() 11 | expect('Use a v-on:change listener instead').toHaveBeenWarned() 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /test/unit/features/directives/model-parse.spec.js: -------------------------------------------------------------------------------- 1 | import { parseModel } from 'compiler/directives/model' 2 | 3 | describe('model expression parser', () => { 4 | it('parse single path', () => { 5 | const res = parseModel('foo') 6 | expect(res.exp).toBe('foo') 7 | expect(res.key).toBe(null) 8 | }) 9 | 10 | it('parse object dot notation', () => { 11 | const res = parseModel('a.b.c') 12 | expect(res.exp).toBe('a.b') 13 | expect(res.key).toBe('"c"') 14 | }) 15 | 16 | it('parse string in brackets', () => { 17 | const res = parseModel('a["b"][c]') 18 | expect(res.exp).toBe('a["b"]') 19 | expect(res.key).toBe('c') 20 | }) 21 | 22 | it('parse brackets with object dot notation', () => { 23 | const res = parseModel('a["b"][c].xxx') 24 | expect(res.exp).toBe('a["b"][c]') 25 | expect(res.key).toBe('"xxx"') 26 | }) 27 | 28 | it('parse nested brackets', () => { 29 | const res = parseModel('a[i[c]]') 30 | expect(res.exp).toBe('a') 31 | expect(res.key).toBe('i[c]') 32 | }) 33 | 34 | it('combined', () => { 35 | const res = parseModel('test.xxx.a["asa"][test1[key]]') 36 | expect(res.exp).toBe('test.xxx.a["asa"]') 37 | expect(res.key).toBe('test1[key]') 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /test/unit/features/directives/pre.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Directive v-pre', function () { 4 | it('should not compile inner content', function () { 5 | const vm = new Vue({ 6 | template: `
7 |
{{ a }}
8 |
{{ a }}
9 |
10 | 11 |
12 |
`, 13 | data: { 14 | a: 123 15 | } 16 | }) 17 | vm.$mount() 18 | expect(vm.$el.firstChild.textContent).toBe('{{ a }}') 19 | expect(vm.$el.children[1].textContent).toBe('123') 20 | expect(vm.$el.lastChild.innerHTML).toBe('') 21 | }) 22 | 23 | it('should not compile on root node', function () { 24 | const vm = new Vue({ 25 | template: '
{{ a }}
', 26 | replace: true, 27 | data: { 28 | a: 123 29 | } 30 | }) 31 | vm.$mount() 32 | expect(vm.$el.firstChild.textContent).toBe('{{ a }}') 33 | }) 34 | 35 | // #8286 36 | it('should not compile custom component tags', function () { 37 | Vue.component('vtest', { template: `
Hello World
` }) 38 | const vm = new Vue({ 39 | template: '
', 40 | replace: true 41 | }) 42 | vm.$mount() 43 | expect(vm.$el.firstChild.tagName).toBe('VTEST') 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /test/unit/features/global-api/compile.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Global API: compile', () => { 4 | it('should compile render functions', () => { 5 | const res = Vue.compile('
{{ msg }}
') 6 | const vm = new Vue({ 7 | data: { 8 | msg: 'hello' 9 | }, 10 | render: res.render, 11 | staticRenderFns: res.staticRenderFns 12 | }).$mount() 13 | expect(vm.$el.innerHTML).toContain('hello') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /test/unit/features/global-api/observable.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Global API: observable', () => { 4 | it('should work', done => { 5 | const state = Vue.observable({ 6 | count: 0 7 | }) 8 | 9 | const app = new Vue({ 10 | render(h) { 11 | return h('div', [ 12 | h('span', state.count), 13 | h('button', { 14 | on: { 15 | click: () => { 16 | state.count++ 17 | } 18 | } 19 | }, '+') 20 | ]) 21 | } 22 | }).$mount() 23 | 24 | expect(app.$el.querySelector('span').textContent).toBe('0') 25 | app.$el.querySelector('button').click() 26 | waitForUpdate(() => { 27 | expect(app.$el.querySelector('span').textContent).toBe('1') 28 | }).then(done) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /test/unit/features/instance/init.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Initialization', () => { 4 | it('without new', () => { 5 | try { Vue() } catch (e) {} 6 | expect('Vue is a constructor and should be called with the `new` keyword').toHaveBeenWarned() 7 | }) 8 | 9 | it('with new', () => { 10 | expect(new Vue() instanceof Vue).toBe(true) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /test/unit/features/options/comments.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Comments', () => { 4 | it('comments should be kept', () => { 5 | const vm = new Vue({ 6 | comments: true, 7 | data () { 8 | return { 9 | foo: 1 10 | } 11 | }, 12 | template: '
node1{{foo}}
' 13 | }).$mount() 14 | expect(vm.$el.innerHTML).toEqual('node11') 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /test/unit/features/options/inheritAttrs.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options inheritAttrs', () => { 4 | it('should work', done => { 5 | const vm = new Vue({ 6 | template: ``, 7 | data: { foo: 'foo' }, 8 | components: { 9 | foo: { 10 | inheritAttrs: false, 11 | template: `
foo
` 12 | } 13 | } 14 | }).$mount() 15 | expect(vm.$el.id).toBe('') 16 | vm.foo = 'bar' 17 | waitForUpdate(() => { 18 | expect(vm.$el.id).toBe('') 19 | }).then(done) 20 | }) 21 | 22 | it('with inner v-bind', done => { 23 | const vm = new Vue({ 24 | template: ``, 25 | data: { foo: 'foo' }, 26 | components: { 27 | foo: { 28 | inheritAttrs: false, 29 | template: `
` 30 | } 31 | } 32 | }).$mount() 33 | expect(vm.$el.children[0].id).toBe('foo') 34 | vm.foo = 'bar' 35 | waitForUpdate(() => { 36 | expect(vm.$el.children[0].id).toBe('bar') 37 | }).then(done) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /test/unit/features/options/methods.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import testObjectOption from '../../../helpers/test-object-option' 3 | 4 | describe('Options methods', () => { 5 | testObjectOption('methods') 6 | 7 | it('should have correct context', () => { 8 | const vm = new Vue({ 9 | data: { 10 | a: 1 11 | }, 12 | methods: { 13 | plus () { 14 | this.a++ 15 | } 16 | } 17 | }) 18 | vm.plus() 19 | expect(vm.a).toBe(2) 20 | }) 21 | 22 | it('should warn methods of not function type', () => { 23 | new Vue({ 24 | methods: { 25 | hello: {} 26 | } 27 | }) 28 | expect('Method "hello" has type "object" in the component definition').toHaveBeenWarned() 29 | }) 30 | 31 | it('should warn methods conflicting with data', () => { 32 | new Vue({ 33 | data: { 34 | foo: 1 35 | }, 36 | methods: { 37 | foo () {} 38 | } 39 | }) 40 | expect(`Method "foo" has already been defined as a data property`).toHaveBeenWarned() 41 | }) 42 | 43 | it('should warn methods conflicting with internal methods', () => { 44 | new Vue({ 45 | methods: { 46 | _update () {} 47 | } 48 | }) 49 | expect(`Method "_update" conflicts with an existing Vue instance method`).toHaveBeenWarned() 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /test/unit/features/options/name.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options name', () => { 4 | it('should contain itself in self components', () => { 5 | const vm = Vue.extend({ 6 | name: 'SuperVue' 7 | }) 8 | 9 | expect(vm.options.components['SuperVue']).toEqual(vm) 10 | }) 11 | 12 | it('should warn when incorrect name given', () => { 13 | Vue.extend({ 14 | name: 'Hyper*Vue' 15 | }) 16 | 17 | /* eslint-disable */ 18 | expect(`Invalid component name: "Hyper*Vue".`) 19 | .toHaveBeenWarned() 20 | /* eslint-enable */ 21 | 22 | Vue.extend({ 23 | name: '2Cool2BValid' 24 | }) 25 | 26 | /* eslint-disable */ 27 | expect(`Invalid component name: "2Cool2BValid".`) 28 | .toHaveBeenWarned() 29 | /* eslint-enable */ 30 | }) 31 | 32 | it('id should not override given name when using Vue.component', () => { 33 | const SuperComponent = Vue.component('super-component', { 34 | name: 'SuperVue' 35 | }) 36 | 37 | expect(SuperComponent.options.components['SuperVue']).toEqual(SuperComponent) 38 | expect(SuperComponent.options.components['super-component']).toEqual(SuperComponent) 39 | }) 40 | 41 | it('should allow all potential custom element name for component name including non-alphanumeric characters', () => { 42 | Vue.extend({ 43 | name: 'my-컴포넌트' 44 | }) 45 | 46 | expect(`Invalid component name`).not.toHaveBeenWarned() 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /test/unit/features/options/parent.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options parent', () => { 4 | it('should work', () => { 5 | const parent = new Vue({ 6 | render () {} 7 | }).$mount() 8 | 9 | const child = new Vue({ 10 | parent: parent, 11 | render () {} 12 | }).$mount() 13 | 14 | // this option is straight-forward 15 | // it should register 'parent' as a $parent for 'child' 16 | // and push 'child' to $children array on 'parent' 17 | expect(child.$options.parent).toBeDefined() 18 | expect(child.$options.parent).toEqual(parent) 19 | expect(child.$parent).toBeDefined() 20 | expect(child.$parent).toEqual(parent) 21 | expect(parent.$children).toContain(child) 22 | 23 | // destroy 'child' and check if it was removed from 'parent' $children 24 | child.$destroy() 25 | expect(parent.$children.length).toEqual(0) 26 | parent.$destroy() 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /test/unit/features/options/propsData.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options propsData', () => { 4 | it('should work', done => { 5 | const A = Vue.extend({ 6 | props: ['a'], 7 | template: '
{{ a }}
' 8 | }) 9 | const vm = new A({ 10 | propsData: { 11 | a: 123 12 | } 13 | }).$mount() 14 | expect(vm.a).toBe(123) 15 | expect(vm.$el.textContent).toBe('123') 16 | vm.a = 234 17 | waitForUpdate(() => { 18 | expect(vm.$el.textContent).toBe('234') 19 | }).then(done) 20 | }) 21 | 22 | it('warn non instantiation usage', () => { 23 | Vue.extend({ 24 | propsData: { 25 | a: 123 26 | } 27 | }) 28 | expect('option "propsData" can only be used during instance creation').toHaveBeenWarned() 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /test/unit/features/options/render.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options render', () => { 4 | it('basic usage', () => { 5 | const vm = new Vue({ 6 | render (h) { 7 | const children = [] 8 | for (let i = 0; i < this.items.length; i++) { 9 | children.push(h('li', { staticClass: 'task' }, [this.items[i].name])) 10 | } 11 | return h('ul', { staticClass: 'tasks' }, children) 12 | }, 13 | data: { 14 | items: [{ id: 1, name: 'task1' }, { id: 2, name: 'task2' }] 15 | } 16 | }).$mount() 17 | expect(vm.$el.tagName).toBe('UL') 18 | for (let i = 0; i < vm.$el.children.length; i++) { 19 | const li = vm.$el.children[i] 20 | expect(li.tagName).toBe('LI') 21 | expect(li.textContent).toBe(vm.items[i].name) 22 | } 23 | }) 24 | 25 | it('allow null data', () => { 26 | const vm = new Vue({ 27 | render (h) { 28 | return h('div', null, 'hello' /* string as children*/) 29 | } 30 | }).$mount() 31 | expect(vm.$el.tagName).toBe('DIV') 32 | expect(vm.$el.textContent).toBe('hello') 33 | }) 34 | 35 | it('should warn non `render` option and non `template` option', () => { 36 | new Vue().$mount() 37 | expect('Failed to mount component: template or render function not defined.').toHaveBeenWarned() 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /test/unit/features/options/renderError.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options renderError', () => { 4 | it('should be used on render errors', done => { 5 | Vue.config.errorHandler = () => {} 6 | const vm = new Vue({ 7 | data: { 8 | ok: true 9 | }, 10 | render (h) { 11 | if (this.ok) { 12 | return h('div', 'ok') 13 | } else { 14 | throw new Error('no') 15 | } 16 | }, 17 | renderError (h, err) { 18 | return h('div', err.toString()) 19 | } 20 | }).$mount() 21 | expect(vm.$el.textContent).toBe('ok') 22 | vm.ok = false 23 | waitForUpdate(() => { 24 | expect(vm.$el.textContent).toBe('Error: no') 25 | Vue.config.errorHandler = null 26 | }).then(done) 27 | }) 28 | 29 | it('should pass on errors in renderError to global handler', () => { 30 | const spy = Vue.config.errorHandler = jasmine.createSpy() 31 | const err = new Error('renderError') 32 | const vm = new Vue({ 33 | render () { 34 | throw new Error('render') 35 | }, 36 | renderError () { 37 | throw err 38 | } 39 | }).$mount() 40 | expect(spy).toHaveBeenCalledWith(err, vm, 'renderError') 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | require('es6-promise/auto') 2 | 3 | // import all helpers 4 | const helpersContext = require.context('../helpers', true) 5 | helpersContext.keys().forEach(helpersContext) 6 | 7 | // require all test files 8 | const testsContext = require.context('./', true, /\.spec$/) 9 | testsContext.keys().forEach(testsContext) 10 | -------------------------------------------------------------------------------- /test/unit/karma.base.config.js: -------------------------------------------------------------------------------- 1 | const alias = require('../../scripts/alias') 2 | const featureFlags = require('../../scripts/feature-flags') 3 | const webpack = require('webpack') 4 | 5 | const webpackConfig = { 6 | mode: 'development', 7 | resolve: { 8 | alias: alias 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.js$/, 14 | loader: 'babel-loader', 15 | exclude: /node_modules/ 16 | } 17 | ] 18 | }, 19 | plugins: [ 20 | new webpack.DefinePlugin({ 21 | __WEEX__: false, 22 | 'process.env': { 23 | TRANSITION_DURATION: process.env.CI ? 100 : 50, 24 | TRANSITION_BUFFER: 10, 25 | ...featureFlags 26 | } 27 | }) 28 | ], 29 | devtool: '#inline-source-map' 30 | } 31 | 32 | // shared config for all unit tests 33 | module.exports = { 34 | frameworks: ['jasmine'], 35 | files: [ 36 | './index.js' 37 | ], 38 | preprocessors: { 39 | './index.js': ['webpack', 'sourcemap'] 40 | }, 41 | webpack: webpackConfig, 42 | webpackMiddleware: { 43 | noInfo: true 44 | }, 45 | plugins: [ 46 | 'karma-jasmine', 47 | 'karma-mocha-reporter', 48 | 'karma-sourcemap-loader', 49 | 'karma-webpack' 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /test/unit/karma.cover.config.js: -------------------------------------------------------------------------------- 1 | const base = require('./karma.base.config.js') 2 | 3 | process.env.CHROME_BIN = require('puppeteer').executablePath() 4 | 5 | module.exports = function (config) { 6 | const options = Object.assign(base, { 7 | browsers: ['ChromeHeadlessCI'], 8 | customLaunchers: { 9 | 'ChromeHeadlessCI': { 10 | base: 'ChromeHeadless', 11 | flags: ['--no-sandbox'] 12 | } 13 | }, 14 | reporters: ['mocha', 'coverage'], 15 | coverageReporter: { 16 | reporters: [ 17 | { type: 'lcov', dir: '../../coverage', subdir: '.' }, 18 | { type: 'text-summary', dir: '../../coverage', subdir: '.' } 19 | ] 20 | }, 21 | singleRun: true, 22 | plugins: base.plugins.concat([ 23 | 'karma-coverage', 24 | 'karma-chrome-launcher' 25 | ]) 26 | }) 27 | 28 | // add babel-plugin-istanbul for code instrumentation 29 | options.webpack.module.rules[0].options = { 30 | plugins: [['istanbul', { 31 | exclude: [ 32 | 'test/', 33 | 'src/compiler/parser/html-parser.js', 34 | 'src/core/instance/proxy.js', 35 | 'src/sfc/deindent.js', 36 | 'src/platforms/weex/' 37 | ] 38 | }]] 39 | } 40 | 41 | config.set(options) 42 | } 43 | -------------------------------------------------------------------------------- /test/unit/karma.dev.config.js: -------------------------------------------------------------------------------- 1 | const base = require('./karma.base.config.js') 2 | 3 | process.env.CHROME_BIN = require('puppeteer').executablePath() 4 | 5 | module.exports = function (config) { 6 | config.set(Object.assign(base, { 7 | browsers: ['ChromeHeadless'], 8 | reporters: ['progress'], 9 | plugins: base.plugins.concat([ 10 | 'karma-chrome-launcher' 11 | ]) 12 | })) 13 | } 14 | -------------------------------------------------------------------------------- /test/unit/karma.unit.config.js: -------------------------------------------------------------------------------- 1 | const base = require('./karma.base.config.js') 2 | 3 | module.exports = function (config) { 4 | config.set(Object.assign(base, { 5 | browsers: ['Chrome', 'Firefox', 'Safari'], 6 | reporters: ['progress'], 7 | singleRun: true, 8 | plugins: base.plugins.concat([ 9 | 'karma-chrome-launcher', 10 | 'karma-firefox-launcher', 11 | 'karma-safari-launcher' 12 | ]) 13 | })) 14 | } 15 | -------------------------------------------------------------------------------- /test/unit/modules/observer/dep.spec.js: -------------------------------------------------------------------------------- 1 | import Dep from 'core/observer/dep' 2 | 3 | describe('Dep', () => { 4 | let dep 5 | 6 | beforeEach(() => { 7 | dep = new Dep() 8 | }) 9 | 10 | describe('instance', () => { 11 | it('should be created with correct properties', () => { 12 | expect(dep.subs.length).toBe(0) 13 | expect(new Dep().id).toBe(dep.id + 1) 14 | }) 15 | }) 16 | 17 | describe('addSub()', () => { 18 | it('should add sub', () => { 19 | dep.addSub(null) 20 | expect(dep.subs.length).toBe(1) 21 | expect(dep.subs[0]).toBe(null) 22 | }) 23 | }) 24 | 25 | describe('removeSub()', () => { 26 | it('should remove sub', () => { 27 | dep.subs.push(null) 28 | dep.removeSub(null) 29 | expect(dep.subs.length).toBe(0) 30 | }) 31 | }) 32 | 33 | describe('depend()', () => { 34 | let _target 35 | 36 | beforeAll(() => { 37 | _target = Dep.target 38 | }) 39 | 40 | afterAll(() => { 41 | Dep.target = _target 42 | }) 43 | 44 | it('should do nothing if no target', () => { 45 | Dep.target = null 46 | dep.depend() 47 | }) 48 | 49 | it('should add itself to target', () => { 50 | Dep.target = jasmine.createSpyObj('TARGET', ['addDep']) 51 | dep.depend() 52 | expect(Dep.target.addDep).toHaveBeenCalledWith(dep) 53 | }) 54 | }) 55 | 56 | describe('notify()', () => { 57 | it('should notify subs', () => { 58 | dep.subs.push(jasmine.createSpyObj('SUB', ['update'])) 59 | dep.notify() 60 | expect(dep.subs[0].update).toHaveBeenCalled() 61 | }) 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /test/unit/modules/server-compiler/compiler-options.spec.js: -------------------------------------------------------------------------------- 1 | import { ssrCompile } from 'web/server/compiler' 2 | 3 | describe('ssrCompile options', () => { 4 | it('comments', () => { 5 | const compiled = ssrCompile(` 6 |
7 | 8 |
9 | `, { comments: true }) 10 | 11 | expect(compiled.render).toContain('') 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /test/unit/modules/server-compiler/optimizer.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/vue/6390f70c2e4ec7e51cce1d2a4ba35e2d0d328205/test/unit/modules/server-compiler/optimizer.spec.js -------------------------------------------------------------------------------- /test/unit/modules/util/error.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { invokeWithErrorHandling } from 'core/util/error' 3 | 4 | describe('invokeWithErrorHandling', () => { 5 | if (typeof Promise !== 'undefined') { 6 | it('should errorHandler call once when nested calls return rejected promise', done => { 7 | const originalHandler = Vue.config.errorHandler 8 | const handler = Vue.config.errorHandler = jasmine.createSpy() 9 | const userCatch = jasmine.createSpy() 10 | const err = new Error('fake error') 11 | 12 | invokeWithErrorHandling(() => { 13 | return invokeWithErrorHandling(() => { 14 | return Promise.reject(err) 15 | }) 16 | }).catch(userCatch).then(() => { 17 | Vue.config.errorHandler = originalHandler 18 | expect(handler.calls.count()).toBe(1) 19 | expect(userCatch).toHaveBeenCalledWith(err) 20 | done() 21 | }) 22 | }) 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /test/unit/modules/util/next-tick.spec.js: -------------------------------------------------------------------------------- 1 | import { nextTick } from 'core/util/next-tick' 2 | 3 | describe('nextTick', () => { 4 | it('accepts a callback', done => { 5 | nextTick(done) 6 | }) 7 | 8 | it('returns undefined when passed a callback', () => { 9 | expect(nextTick(() => {})).toBeUndefined() 10 | }) 11 | 12 | if (typeof Promise !== 'undefined') { 13 | it('returns a Promise when provided no callback', done => { 14 | nextTick().then(done) 15 | }) 16 | 17 | it('returns a Promise with a context argument when provided a falsy callback and an object', done => { 18 | const obj = {} 19 | nextTick(undefined, obj).then(ctx => { 20 | expect(ctx).toBe(obj) 21 | done() 22 | }) 23 | }) 24 | 25 | it('returned Promise should resolve correctly vs callback', done => { 26 | const spy = jasmine.createSpy() 27 | nextTick(spy) 28 | nextTick().then(() => { 29 | expect(spy).toHaveBeenCalled() 30 | done() 31 | }) 32 | }) 33 | } 34 | }) 35 | -------------------------------------------------------------------------------- /test/unit/modules/vdom/modules/directive.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { patch } from 'web/runtime/patch' 3 | import VNode from 'core/vdom/vnode' 4 | 5 | describe('vdom directive module', () => { 6 | it('should work', () => { 7 | const directive1 = { 8 | bind: jasmine.createSpy('bind'), 9 | update: jasmine.createSpy('update'), 10 | unbind: jasmine.createSpy('unbind') 11 | } 12 | const vm = new Vue({ directives: { directive1 }}) 13 | // create 14 | const vnode1 = new VNode('div', {}, [ 15 | new VNode('p', { 16 | directives: [{ 17 | name: 'directive1', value: 'hello', arg: 'arg1', modifiers: { modifier1: true } 18 | }] 19 | }, undefined, 'hello world', undefined, vm) 20 | ]) 21 | patch(null, vnode1) 22 | expect(directive1.bind).toHaveBeenCalled() 23 | // update 24 | const vnode2 = new VNode('div', {}, [ 25 | new VNode('p', { 26 | directives: [{ 27 | name: 'directive1', value: 'world', arg: 'arg1', modifiers: { modifier1: true } 28 | }] 29 | }, undefined, 'hello world', undefined, vm) 30 | ]) 31 | patch(vnode1, vnode2) 32 | expect(directive1.update).toHaveBeenCalled() 33 | // destroy 34 | const vnode3 = new VNode('div') 35 | patch(vnode2, vnode3) 36 | expect(directive1.unbind).toHaveBeenCalled() 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /test/unit/modules/vdom/modules/style.spec.js: -------------------------------------------------------------------------------- 1 | import { patch } from 'web/runtime/patch' 2 | import VNode from 'core/vdom/vnode' 3 | 4 | describe('vdom style module', () => { 5 | it('should create an element with style', () => { 6 | const vnode = new VNode('p', { style: { fontSize: '12px' }}) 7 | const elm = patch(null, vnode) 8 | expect(elm.style.fontSize).toBe('12px') 9 | }) 10 | 11 | it('should create an element with array style', () => { 12 | const vnode = new VNode('p', { style: [{ fontSize: '12px' }, { color: 'red' }] }) 13 | const elm = patch(null, vnode) 14 | expect(elm.style.fontSize).toBe('12px') 15 | expect(elm.style.color).toBe('red') 16 | }) 17 | 18 | it('should change elements style', () => { 19 | const vnode1 = new VNode('p', { style: { fontSize: '12px' }}) 20 | const vnode2 = new VNode('p', { style: { fontSize: '10px', display: 'block' }}) 21 | patch(null, vnode1) 22 | const elm = patch(vnode1, vnode2) 23 | expect(elm.style.fontSize).toBe('10px') 24 | expect(elm.style.display).toBe('block') 25 | }) 26 | 27 | it('should remove elements attrs', () => { 28 | const vnode1 = new VNode('p', { style: { fontSize: '12px' }}) 29 | const vnode2 = new VNode('p', { style: { display: 'block' }}) 30 | patch(null, vnode1) 31 | const elm = patch(vnode1, vnode2) 32 | expect(elm.style.fontSize).toBe('') 33 | expect(elm.style.display).toBe('block') 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /test/weex/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "plugins": ["jasmine"], 6 | "rules": { 7 | "jasmine/no-focused-tests": 2 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/weex/cases/event/click.after.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'div', 3 | event: ['click'], 4 | children: [{ 5 | type: 'text', 6 | attr: { 7 | value: '43' 8 | } 9 | }] 10 | }) 11 | -------------------------------------------------------------------------------- /test/weex/cases/event/click.before.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'div', 3 | event: ['click'], 4 | children: [{ 5 | type: 'text', 6 | attr: { 7 | value: '42' 8 | } 9 | }] 10 | }) 11 | -------------------------------------------------------------------------------- /test/weex/cases/event/click.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/attrs.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', count: 1, source: 'http://whatever.com/x.png' }, 7 | { type: 'A', count: 2, source: 'http://whatever.com/y.png' }, 8 | { type: 'A', count: 3, source: 'http://whatever.com/z.png' } 9 | ], 10 | switch: 'type', 11 | alias: 'item' 12 | }, 13 | children: [{ 14 | type: 'cell-slot', 15 | attr: { append: 'tree', case: 'A' }, 16 | children: [{ 17 | type: 'image', 18 | attr: { 19 | resize: 'cover', 20 | src: { 21 | '@binding': 'item.source' 22 | } 23 | } 24 | }, { 25 | type: 'text', 26 | attr: { 27 | lines: '3', 28 | count: { 29 | '@binding': 'item.count' 30 | } 31 | } 32 | }] 33 | }] 34 | }) 35 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/attrs.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | 24 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/classname.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', color: 'red' }, 7 | { type: 'A', color: 'blue' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | style: { 16 | backgroundColor: '#FF6600' 17 | }, 18 | children: [{ 19 | type: 'text', 20 | attr: { 21 | // not supported yet 22 | // classList: ['text', { '@binding': 'item.color' }], 23 | value: 'content' 24 | } 25 | }] 26 | }] 27 | }) 28 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/classname.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | 25 | 37 | 38 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/banner.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/counter.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 37 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/editor.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 32 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/footer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/lifecycle.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 40 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/poster.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 22 | 23 | 34 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful-lifecycle.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'X' }, 7 | { type: 'X' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'X' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '@isComponentRoot': true, 19 | '@componentProps': {} 20 | }, 21 | children: [{ 22 | type: 'text', 23 | attr: { 24 | value: { '@binding': 'number' } 25 | } 26 | }] 27 | }] 28 | }] 29 | }) 30 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful-lifecycle.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful-v-model.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '@isComponentRoot': true, 19 | '@componentProps': { 20 | message: 'No binding' 21 | } 22 | }, 23 | children: [{ 24 | type: 'text', 25 | classList: ['output'], 26 | attr: { 27 | value: { '@binding': 'output' } 28 | } 29 | }, { 30 | type: 'input', 31 | event: ['input'], 32 | classList: ['input'], 33 | attr: { 34 | type: 'text', 35 | value: 'No binding' 36 | } 37 | }] 38 | }] 39 | }] 40 | }) 41 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful-v-model.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', number: 24 }, 7 | { type: 'A', number: 42 } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '@isComponentRoot': true, 19 | '@componentProps': { 20 | start: { '@binding': 'item.number' } 21 | } 22 | }, 23 | children: [{ 24 | type: 'text', 25 | classList: ['output'], 26 | attr: { 27 | value: { '@binding': 'count' } // need confirm 28 | } 29 | }, { 30 | type: 'text', 31 | event: ['click'], 32 | classList: ['button'], 33 | attr: { value: '+' } 34 | }] 35 | }, { 36 | type: 'text', 37 | attr: { value: 'other' } 38 | }] 39 | }] 40 | }) 41 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateless-multi-components.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 31 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateless-with-props.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', poster: 'xx', title: 'x' }, 7 | { type: 'A', poster: 'yy', title: 'y' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '@isComponentRoot': true, 19 | '@componentProps': { 20 | imageUrl: { '@binding': 'item.poster' }, 21 | title: { '@binding': 'item.title' } 22 | } 23 | }, 24 | children: [{ 25 | type: 'image', 26 | classList: ['image'], 27 | attr: { 28 | src: { '@binding': 'imageUrl' } 29 | } 30 | }, { 31 | type: 'text', 32 | classList: ['title'], 33 | attr: { 34 | value: { '@binding': 'title' } 35 | } 36 | }] 37 | }, { 38 | type: 'text', 39 | attr: { 40 | value: 'content' 41 | } 42 | }] 43 | }] 44 | }) 45 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateless-with-props.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateless.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '@isComponentRoot': true, 19 | '@componentProps': {} 20 | }, 21 | classList: ['banner'], 22 | children: [{ 23 | type: 'text', 24 | classList: ['title'], 25 | attr: { 26 | value: 'BANNER' 27 | } 28 | }] 29 | }, { 30 | type: 'text', 31 | attr: { 32 | value: 'content' 33 | } 34 | }] 35 | }] 36 | }) 37 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateless.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/inline-style.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', color: '#606060' }, 7 | { type: 'A', color: '#E5E5E5' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | style: { 16 | backgroundColor: '#FF6600' 17 | }, 18 | children: [{ 19 | type: 'text', 20 | style: { 21 | fontSize: '100px', 22 | color: { '@binding': 'item.color' } 23 | }, 24 | attr: { 25 | value: 'content' 26 | } 27 | }] 28 | }] 29 | }) 30 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/inline-style.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/text-node.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', dynamic: 'decimal', two: '2', four: '4' }, 7 | { type: 'A', dynamic: 'binary', two: '10', four: '100' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'text', 17 | attr: { 18 | value: 'static' 19 | } 20 | }, { 21 | type: 'text', 22 | attr: { 23 | value: { '@binding': 'item.dynamic' } 24 | } 25 | }, { 26 | type: 'text', 27 | attr: { 28 | value: [ 29 | 'one ', 30 | { '@binding': 'item.two' }, 31 | ' three ', 32 | { '@binding': 'item.four' }, 33 | ' five' 34 | ] 35 | } 36 | }] 37 | }] 38 | }) 39 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/text-node.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-else-if.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'image', 17 | attr: { 18 | '[[match]]': 'item.sourceA', 19 | src: { '@binding': 'item.sourceA' } 20 | } 21 | }, { 22 | type: 'image', 23 | attr: { 24 | '[[match]]': '!(item.sourceA) && (item.sourceB)', 25 | src: { '@binding': 'item.sourceB' } 26 | } 27 | }, { 28 | type: 'image', 29 | attr: { 30 | '[[match]]': '!(item.sourceA || item.sourceB)', 31 | src: { '@binding': 'item.placeholder' } 32 | } 33 | }] 34 | }] 35 | }) 36 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-else-if.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-else.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'image', 17 | attr: { 18 | '[[match]]': 'item.source', 19 | src: { '@binding': 'item.source' } 20 | } 21 | }, { 22 | type: 'image', 23 | attr: { 24 | '[[match]]': '!(item.source)', 25 | src: { '@binding': 'item.placeholder' } 26 | } 27 | }] 28 | }] 29 | }) 30 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-else.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-for-iterator.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '[[repeat]]': { 19 | '@expression': 'item.list', 20 | '@index': 'index', 21 | '@alias': 'object' 22 | } 23 | }, 24 | children: [{ 25 | type: 'text', 26 | attr: { 27 | value: { 28 | '@binding': 'object.name' 29 | } 30 | } 31 | }, { 32 | type: 'text', 33 | attr: { 34 | '[[repeat]]': { 35 | '@expression': 'object', 36 | '@alias': 'v', 37 | '@key': 'k', 38 | '@index': 'i' 39 | }, 40 | value: { 41 | '@binding': 'v' 42 | } 43 | } 44 | }] 45 | }] 46 | }] 47 | }) 48 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-for-iterator.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-for.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '[[repeat]]': { 19 | '@expression': 'item.list', 20 | '@alias': 'panel' 21 | } 22 | }, 23 | children: [{ 24 | type: 'text', 25 | attr: { 26 | value: { 27 | '@binding': 'panel.label' 28 | } 29 | } 30 | }] 31 | }] 32 | }] 33 | }) 34 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-for.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-if.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'image', 17 | attr: { 18 | '[[match]]': 'item.source', 19 | src: { '@binding': 'item.source' } 20 | } 21 | }, { 22 | type: 'text', 23 | attr: { 24 | '[[match]]': '!item.source', 25 | value: 'Title' 26 | } 27 | }] 28 | }] 29 | }) 30 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-if.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-on-inline.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'text', 17 | event: ['click', { 18 | type: 'longpress', 19 | params: [{ '@binding': 'item.key' }] 20 | }] 21 | }, { 22 | type: 'text', 23 | event: [{ 24 | type: 'appear', 25 | params: [ 26 | { '@binding': 'item.index' }, 27 | { '@binding': 'item.type' } 28 | ] 29 | }], 30 | attr: { value: 'Button' } 31 | }, { 32 | type: 'text', 33 | event: [{ type: 'disappear' }], 34 | attr: { value: 'Tips' } 35 | }] 36 | }] 37 | }) 38 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-on-inline.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 28 | 29 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-on.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'text', 17 | event: ['click', 'longpress'], 18 | attr: { value: 'A' } 19 | }, { 20 | type: 'text', 21 | event: ['touchend'], 22 | attr: { value: 'B' } 23 | }] 24 | }] 25 | }) 26 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-on.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 26 | 27 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-once.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | alias: 'item' 10 | }, 11 | children: [{ 12 | type: 'cell-slot', 13 | attr: { append: 'tree' }, 14 | children: [{ 15 | type: 'text', 16 | attr: { 17 | '[[once]]': true, 18 | value: { '@binding': 'item.type' } 19 | } 20 | }] 21 | }] 22 | }) 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-once.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | -------------------------------------------------------------------------------- /test/weex/cases/render/class.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'div', 3 | children: [{ 4 | type: 'div', 5 | classList: ['box', 'box1'] 6 | }, { 7 | type: 'div', 8 | classList: ['box', 'box2'] 9 | }, { 10 | type: 'div', 11 | classList: ['box', 'box3'] 12 | }, { 13 | type: 'div', 14 | classList: ['box', 'box4'] 15 | }, { 16 | type: 'div', 17 | classList: ['box', 'box5'] 18 | }] 19 | }) 20 | -------------------------------------------------------------------------------- /test/weex/cases/render/class.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 37 | 38 | -------------------------------------------------------------------------------- /test/weex/cases/render/sample.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'div', 3 | style: { 4 | justifyContent: 'center' 5 | }, 6 | children: [{ 7 | type: 'text', 8 | attr: { 9 | value: 'Yo' 10 | }, 11 | classList: ['freestyle'] 12 | }] 13 | }) 14 | -------------------------------------------------------------------------------- /test/weex/cases/render/sample.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 24 | -------------------------------------------------------------------------------- /test/weex/compiler/props.spec.js: -------------------------------------------------------------------------------- 1 | import { compile } from '../../../packages/weex-template-compiler' 2 | import { strToRegExp } from '../helpers/index' 3 | 4 | describe('compile props', () => { 5 | it('custom props', () => { 6 | const { render, staticRenderFns, errors } = compile(`
`) 7 | expect(render).not.toBeUndefined() 8 | expect(staticRenderFns).not.toBeUndefined() 9 | expect(staticRenderFns.length).toEqual(0) 10 | expect(render).toMatch(strToRegExp(`attrs:{"custom":"whatever"}`)) 11 | expect(errors).toEqual([]) 12 | }) 13 | 14 | it('camelize props', () => { 15 | const { render, staticRenderFns, errors } = compile(`
`) 16 | expect(render).not.toBeUndefined() 17 | expect(staticRenderFns).not.toBeUndefined() 18 | expect(staticRenderFns.length).toEqual(0) 19 | expect(render).toMatch(strToRegExp(`attrs:{"kebabCase":"whatever"}`)) 20 | expect(errors).toEqual([]) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /test/weex/jasmine.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | spec_dir: 'test/weex', 3 | spec_files: [ 4 | '**/*[sS]pec.js' 5 | ], 6 | helpers: [ 7 | require.resolve('@babel/register') 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from "./vue"; 2 | import "./umd"; 3 | 4 | export default Vue; 5 | 6 | export { 7 | CreateElement, 8 | VueConstructor 9 | } from "./vue"; 10 | 11 | export { 12 | Component, 13 | AsyncComponent, 14 | ComponentOptions, 15 | FunctionalComponentOptions, 16 | RenderContext, 17 | PropType, 18 | PropOptions, 19 | ComputedOptions, 20 | WatchHandler, 21 | WatchOptions, 22 | WatchOptionsWithHandler, 23 | DirectiveFunction, 24 | DirectiveOptions 25 | } from "./options"; 26 | 27 | export { 28 | PluginFunction, 29 | PluginObject 30 | } from "./plugin"; 31 | 32 | export { 33 | VNodeChildren, 34 | VNodeChildrenArrayContents, 35 | VNode, 36 | VNodeComponentOptions, 37 | VNodeData, 38 | VNodeDirective 39 | } from "./vnode"; 40 | -------------------------------------------------------------------------------- /types/plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue as _Vue } from "./vue"; 2 | 3 | export type PluginFunction = (Vue: typeof _Vue, options?: T) => void; 4 | 5 | export interface PluginObject { 6 | install: PluginFunction; 7 | [key: string]: any; 8 | } 9 | -------------------------------------------------------------------------------- /types/test/augmentation-test.ts: -------------------------------------------------------------------------------- 1 | import Vue from "../index"; 2 | 3 | declare module "../vue" { 4 | // add instance property and method 5 | interface Vue { 6 | $instanceProperty: string; 7 | $instanceMethod(): void; 8 | } 9 | 10 | // add static property and method 11 | interface VueConstructor { 12 | staticProperty: string; 13 | staticMethod(): void; 14 | } 15 | } 16 | 17 | // augment ComponentOptions 18 | declare module "../options" { 19 | interface ComponentOptions { 20 | foo?: string; 21 | } 22 | } 23 | 24 | const vm = new Vue({ 25 | props: ["bar"], 26 | data: { 27 | a: true 28 | }, 29 | foo: "foo", 30 | methods: { 31 | foo() { 32 | this.a = false; 33 | } 34 | }, 35 | computed: { 36 | BAR(): string { 37 | return this.bar.toUpperCase(); 38 | } 39 | } 40 | }); 41 | 42 | vm.$instanceProperty; 43 | vm.$instanceMethod(); 44 | 45 | Vue.staticProperty; 46 | Vue.staticMethod(); 47 | -------------------------------------------------------------------------------- /types/test/es-module.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /types/test/plugin-test.ts: -------------------------------------------------------------------------------- 1 | import Vue from "../index"; 2 | import { PluginFunction, PluginObject } from "../index"; 3 | 4 | class Option { 5 | prefix: string = ""; 6 | suffix: string = ""; 7 | } 8 | 9 | const plugin: PluginObject